Kollisions Reaktion aka Sliden



  • Edit by Headhunter : Titel für die FAQ geändert

    Hi @ all.
    Letztes mal habe ich ja was zu Kollision in 2D spielen gefragt usw. Nun habe ich ne weitere Frage: Was soll man machen, wenn eine Kollision festgestellt wurde?
    Man kann ja nicht immer Einfallswinkel = Ausfallswinkel machen. Wenn man in 3D Egoshootern gegen eine Wand läuft slided man meistens an der Wand entlang, aber wie macht man das? Bei EW = AW würd der Spieler ja von der Wand abprallen. (In meinem 2D Game ist da auch son Prob, dass die Figur manchmal wegen falschem Collision Responding durch ein Objekt fällt.) Ich bin gespannt 🙂 Das interessiert mich wirklich, hab aber nie was vernümpftiges dazu gefunden 😕

    Bitte helft mir 🙂 (Pseudo oder C++ Code gern gesehn 😉

    cu
    Squolly

    [ Dieser Beitrag wurde am 28.01.2003 um 15:19 Uhr von Headhunter editiert. ]



  • Hi!
    Wenn du die Figure "sliden" lassen willst, dann berechnen den Punkt, der auf dem Rechntenwinkel zu der Kolllisionsebene liegt + den Abstand.
    Und schon hast du einen schönen Slide 🙂

    Bye



  • Ich glaub ich hab da genau das Richtige für dich! Hab nämlich vor kurzem selbst so eine Methode gebraucht und mit ein wenig Vektorrechnung genau das erreicht, was du jetzt willst. Wenn du n bisschen Code haben willst, kann ich dir was schicken.

    mfG D1B

    PS: Gegen das durch ein Hindernis hindurchfallen hab ich immer alle mathematisch positiv (gegen den Uhrzeigersinn) orientiert. Dadurch bleiben alle draußen, und wenn man doch ma drin is, kommt man wieder raus, wenn man gegen ne wand läuft.

    Edit: Ich würd hier auch gern bisschen Code posten, aber da ich viele eigene Strukturen habe würde das den Rahmen sprengen, auch die noch hier hinzuposten, ließt ja neben dir eh keiner... (Wenns jemanden interessiert, eMailAddresse)

    [ Dieser Beitrag wurde am 27.01.2003 um 17:46 Uhr von D1BAKEL editiert. ]



  • Poste den Code doch hier (wenn du möchtest), und ich verschieb in nachher ins FAQ !



  • Okay, hab den Code jetzt hoffentlich ausreichend kommentiert. Am Anfang zeige ich die Datenstrukturen, die ich verwendet hab, und dann die eigentliche Kollisionsmethode. Verzeiht mir bitte die schlechten Variablennamen in letzterer, aber ich hatte keine besseren. Und hier ist er:

    // Vertex.h
    ///////////////////////////////////////////////////////////
    
    #ifndef VERTEX_H
    #define VERTEX_H
    
    class CVertex
    {
    public:
        double x;
        double y;
    
        CVertex(double nx, double ny): x(nx), y(ny) {}
        CVertex() {}
    
        const CVertex& operator =(const CVertex *Vertex) {
            x = Vertex->x;
            y = Vertex->y;
            return *this;
        }
    
        const CVertex operator +(CVertex Vertex) const {
            CVertex Result;
            Result.x = x + Vertex.x;
            Result.y = y + Vertex.y;
            return Result;
        }
    
        const CVertex operator +(const CVertex *Vertex) const {
            CVertex Result;
            Result.x = x + Vertex->x;
            Result.y = y + Vertex->y;
            return Result;
        }
    
        const CVertex operator -(CVertex Vertex) const {
            CVertex Result;
            Result.x = x - Vertex.x;
            Result.y = y - Vertex.y;
            return Result;
        }
    
        const CVertex operator *(double d) const {
            CVertex Result;
            Result.x = x * d;
            Result.y = y * d;
            return Result;
        }
    
        const CVertex operator /(double d) const {
            CVertex Result;
            Result.x = x / d;
            Result.y = y / d;
            return Result;
        }
    };
    
    #endif
    
    // Rubber.h
    ///////////////////////////////////////////////////////////
    
    #ifndef RUBBER_H
    #define RUBBER_H
    
    #include <windows.h>
    
    #include "D3D.h"
    #include "Vertex.h"
    #include "Extra.h"
    #include "Obstacle.h"
    
    class CObstacle;
    class CGame;
    class CExtra;
    
    class CRubber           // Die Spielfiguren
    {
        friend CExtra;      // Ein Rubber kann Extras einsammeln und muss sie dann vernichten...
        friend CGame;       // Auch aufs Spiel braucht er Zugriff
    
    public:
        void Init(CVertex Pos, double angle, int id, BOOL ki);  // Initialisieren
        void Move(DWORD dwElapsed);                             // Bewegen
        void Render(CSurface Surface, DWORD Time);              // Rendern
        void EditBoard();                   // Spuren zeichnen (der eigentliche Sinn des Spiels)
        void Collide(CRubber  *Rubber);     // Collision mit Gegnern
        void Collide(CExtra   *Extra);      // Ein Extra eingesammelt?
        void Collide(CObstacle Obstacle);   // Hier wird die Kollision mit einem Hindernis behandelt
    
    private:
        CVertex Pos;        // Position
    
        double  Speed;      // Geschwindigkeit
        double  Radius;     // Größe
        double  Angle;      // Richtung in die er guckt und fährt
    
        float   Rotate;     // Wie stark er in welche Richtung lenkt
    
        DWORD   Counter;    // Wenn zwei Rubber zusammenstoßen wird Disabled auf true gestellt
        BOOL    Disabled;   // (fährt langsamer) und in Counter die aktuelle Zeit gespeichert,
                            // um Disabled nach 2 Sekunden wieder auf false zu setzen
    
        // KI-stuff
        int     ID;         // Kennziffer zur Unterscheidung
        BOOL    KI;         // Computergesteuert?
    
        CVertex Aim;        // das eigentliche Ziel
        CVertex Hint;       // ein Hilfspunkt für Umwege
        double  AimAngle;   // der Winkel zum Ziel- oder Hilfspunkt
    
        BOOL    Hinting;    // Wird ein Umweg gefahren?
        BOOL    TurnLeft;   // Lenkt er gerade nach links?
        BOOL    RoundLeft;  // Fährt er gerade linksrum um die Hindernisse?
    
        int     LastHintID; // Welche Ecke eines Hindernisses zuletzt angefahren
        BOOL    Agressive;  // Agressives Verhalten? (Wenn nicht werden Kollisionen mit Gegenspielern verieden)
    
        int     Aims;       // Ein paar Zahlen für die Statistik: Wieviele Ziele wurden erreicht
        int     Hints;      // bzw wieviele Hilfen wurden verwendet
        int     LostHints;  // Wieviele Hilfen wurden nicht innerhalb von 5 Sekunden erreicht?
        int     Collisions; // Wieviele Kollisionen hat der Rubber hinter sich?
    
        DWORD   KICounter;  // Mit diesem Zeitnehmer wird bereitgestellt, dass ein Hint alle 5 Sekunden
                            // erneuert wird, wenn er nicht erreicht wurde
    
        CGame  *Game;       // Ein Pointer auf den Vater (das Spiel selbst)
    };
    
    #endif
    
    // Obstacle.h
    ///////////////////////////////////////////////////////////
    
    #ifndef OBSTACLE_H
    #define OBSTACLE_H
    
    #include "Vertex.h"
    #include "Global.h"
    #include "D3D.h"
    #include "Rubber.h"
    
    class CGame;
    class CRubber;
    
    class CObstacle     // Hindernisse die einen Level charakterisieren
    {
        friend CGame;   // Freundschaft mit Game
        friend CRubber; // und den Spielern
    
    public:
        void Render(CSurface Surface);  // Rendern eines Hindernisses
        CObstacle() {}                  // und der Konstruktor
    
    private:
        int     Vertices;               // Wieviele Ecken hat er?
        CVertex Vertex[MAX_VERTICES];   // Array mit den Positionen der Ecken
    
        CGame *Game;        // und auch hier der Game-Pointer
    
    };
    
    #endif
    
    // Auszug aus der Game.cpp
    ///////////////////////////////////////////////////////////
    
    void CGame::InitLevel(int Level)
    {
        switch(Level)
        {
        case 0:
            {
                Obstacles = 0;
            } break;
    
        case 1:
            {
                    // es folgen Beispiele für Initialisierungen von Hindernissen
                    // Wichtig: mathematisch positiv (gegen Uhrzeigersinn), sonst
                    // wird man im Hindernis gefangen
    
                Obstacles = 2;
    
                Obstacle[0].Vertices = 4;
                Obstacle[0].Vertex[0] = new CVertex(350,  70);
                Obstacle[0].Vertex[1] = new CVertex(400,  70);
                Obstacle[0].Vertex[2] = new CVertex(250, 400);
                Obstacle[0].Vertex[3] = new CVertex(200, 400);
    
                Obstacle[1].Vertices = 4;
                Obstacle[1].Vertex[0] = new CVertex(550, 200);
                Obstacle[1].Vertex[1] = new CVertex(600, 200);
                Obstacle[1].Vertex[2] = new CVertex(450, 530);
                Obstacle[1].Vertex[3] = new CVertex(400, 530);
            } break;
    
        case 2:
            {
                Obstacles = 4;
    
                Obstacle[0].Vertices = 4;
                Obstacle[0].Vertex[0] = new CVertex(200, 180);
                Obstacle[0].Vertex[1] = new CVertex(600, 180);
                Obstacle[0].Vertex[2] = new CVertex(600, 220);
                Obstacle[0].Vertex[3] = new CVertex(200, 220);
    
                Obstacle[1].Vertices = 4;
                Obstacle[1].Vertex[0] = new CVertex(380, 400);
                Obstacle[1].Vertex[1] = new CVertex(420, 400);
                Obstacle[1].Vertex[2] = new CVertex(420, 600);
                Obstacle[1].Vertex[3] = new CVertex(380, 600);
    
                Obstacle[2].Vertices = 4;
                Obstacle[2].Vertex[0] = new CVertex(  0, 300);
                Obstacle[2].Vertex[1] = new CVertex(200, 380);
                Obstacle[2].Vertex[2] = new CVertex(200, 400);
                Obstacle[2].Vertex[3] = new CVertex(  0, 350);
    
                Obstacle[3].Vertices = 4;
                Obstacle[3].Vertex[0] = new CVertex(800, 300);
                Obstacle[3].Vertex[1] = new CVertex(800, 350);
                Obstacle[3].Vertex[2] = new CVertex(600, 400);
                Obstacle[3].Vertex[3] = new CVertex(600, 380);
            } break;
        case 3:
            {
                Obstacles = 4;
    
                Obstacle[0].Vertices = 4;
                Obstacle[0].Vertex[0] = new CVertex(  0, 280);
                Obstacle[0].Vertex[1] = new CVertex(300, 280);
                Obstacle[0].Vertex[2] = new CVertex(300, 320);
                Obstacle[0].Vertex[3] = new CVertex(  0, 320);
    
                Obstacle[1].Vertices = 4;
                Obstacle[1].Vertex[0] = new CVertex(500, 280);
                Obstacle[1].Vertex[1] = new CVertex(800, 280);
                Obstacle[1].Vertex[2] = new CVertex(800, 320);
                Obstacle[1].Vertex[3] = new CVertex(500, 320);
    
                Obstacle[2].Vertices = 4;
                Obstacle[2].Vertex[0] = new CVertex(380,   0);
                Obstacle[2].Vertex[1] = new CVertex(420,   0);
                Obstacle[2].Vertex[2] = new CVertex(420, 200);
                Obstacle[2].Vertex[3] = new CVertex(380, 200);
    
                Obstacle[3].Vertices = 4;
                Obstacle[3].Vertex[0] = new CVertex(380, 400);
                Obstacle[3].Vertex[1] = new CVertex(420, 400);
                Obstacle[3].Vertex[2] = new CVertex(420, 600);
                Obstacle[3].Vertex[3] = new CVertex(380, 600);
            } break;
        }
    }
    
    // Auszug aus der Rubber.cpp
    ///////////////////////////////////////////////////////////
    
    // und hier dann das eigentlich verlangte, die Kollision einer Spielfigur mit
    // einem Hindernis sodass sie "slidet":
    
    void CRubber::Collide(CObstacle Obstacle)
    {
        BOOL again = FALSE;
    
        CVertex V1(Obstacle.Vertex[Obstacle.Vertices - 1]);     // letzer Vertex
        CVertex V2;                                             // erster Vertex (siehe Schleife)
        CVertex VR(Pos.x, Pos.y);                               // Position des Rubber
    
        for(int i = 0; i <= Obstacle.Vertices; ++i)     // die Seiten des Hindernisses durchlaufen
        {
            // V1 zu V2 ist immer ein Vertex von einem Eckpunkt des Hindernisses zum nächsten
            // diese werden mittels der Schleife durchlaufen. V2 ist immer der Vertex i und 
            // V1 der Vertex i-1 (siehe Ende der Schleife)
            V2 = Obstacle.Vertex[i];
    
            // beim letzten Vertex wird geprüft, ob die erste Seite
            // nochmal getestet werden muss, sonst könnte man an der
            // letzten Ecke in das Hindernis hineinfahren
            if(i == Obstacle.Vertices)
            {
                if(again)                       // Nochmal die erste Seite?
                    V2 = Obstacle.Vertex[0];
                else return;                    // sonst: Ende
            }
    
            // Wenn man eine Seite des Hindernisses von außen betrachtet, ist V1 linkst und V2 rechts
            CVertex a(V2 - V1);     // Vector von ersten zu zweitem Vektor (links nach rechts)
            CVertex b(VR - V1);     // Vector vom Rubber zum ersten (linken) Eckpunkt
            CVertex c(VR - V2);     // Vector vom Rubber zum zweiten (rechten) Eckpunkt
    
            double apb   =      a.x * b.x + a.y * b.y;      // Punktprodunkt von a und b
            double la    = sqrt(a.x * a.x + a.y * a.y);     // Länge von a
            double lb    = sqrt(b.x * b.x + b.y * b.y);     // Länge von b
            double lc    = sqrt(c.x * c.x + c.y * c.y);     // Länge von c
            double alpha = acos(apb / (la * lb));           // Winkel zwischen a und b
    
            double ld = lb * sin(alpha);    // Abstand des Rubbermittelpunktes von der
                                            // Seite des Hindernisses
    
            if(ld < Radius)                 // Wenn kleiner als Radius (Größe), dann "sliden" lassen
            {
                double e = ld * cos(alpha) / sin(alpha);    // Abstand des Fußpunktes des Lotes (des
                                                            // Rubbermittelpunktes auf die Seite)
                                                            // von der ersten (linken) Ecke
    
                // Die Seiten sind Strecken, wurden bis jetzt aber wie Geraden behandelt. So gibt es jetzt
                // noch Kollisionen fernab vom Hindernis, jedesmal wenn der Spieler eine Gerade trifft, auf
                // der eine Seite des Hindernisses liegt. Deshalb muss man prüfen, ob die Kollision die 
                // wir bis jetzt ermittelt haben auch zwischen den Eckpunkten liegt. Der Abstand vom ersten
                // Punkt also größer Null und kleiner als der Abstand der beiden Eckpunkte sein.
                if(e > 0 && e < la)
                {
                    // Der Fußpunkt des Lotes des Rubbermittelpunktes auf die Seite des Hindernisses
                    CVertex VP(V1 + a / la * e); 
                    CVertex d((VR - VP) / ld * Radius); // Das Lot in der Länge des Radius (sliden)
    
                    // wie man folgendes nennt, weiß ich nicht, habs mir selber ausgedacht.
                    // Auf jeden Fall testet es, ob sich er Rubber links oder rechts von der Seite
                    // befindet und setzt ihn auf die rechte Seite indem er d entweder addiert
                    // oder subtraktiert. (gegen durch ein Objekt fallen)
                    if(a.y * d.x > 0 || a.x * d.y < 0)
                        Pos = VP + d; 
                    else
                        Pos = VP - d;
    
                    // in folgendem Block gehts um die KI
                    if(!Hinting) // Wenn noch nicht geholfen wird
                    {
                        // damit er nicht immer den selben Vertex ansteuert, wird der letzte
                        // immer gespeichert und beim nächsten mal getestet
                        int V1ID = i % Obstacle.Vertices;
                        int V2ID = (i - 1) % Obstacle.Vertices;
    
                        // hier wird dafür gesorgt, dass er ein Objekt möglichst in einer Richtung
                        // umfährt, da er sonst manchmal nie ankommt
                        if(RoundLeft && LastHintID == V2ID || !RoundLeft && LastHintID != V1ID)
                        {
                            Hint = V1 + d * 1.5 - a / la * Radius * 2;  // ein Hilfspunkt wird angesteuert
                            LastHintID = V1ID;
                            RoundLeft = FALSE;
                        }
                        else
                        {
                            Hint = V2 + d * 1.5 + a / la * Radius * 2;  // hier der genau anderer
                            LastHintID = V2ID;
                            RoundLeft = TRUE;
                        }
    
                        // Der Counter um bei nichterreichen der Hilfe nach 5 Sekunden wieder direkt das
                        // eigentliche Ziel anzusteuern. Sonst hängt er sich an schwierigen Stellen auf
                        KICounter = KI_LIMIT;
                        Hinting   = TRUE;       // Damit nicht bei jeder Kollision ne neue Hilfe kommt
    
                        // dann noch dafür sorgen, dass die Hilfe im Spielfeld bleibt, sonst kommt er
                        // ja nie an.
                        if( Hint.x < 0)
                            Hint.x = 0;
                        if( Hint.x > SCREEN_WIDTH)
                            Hint.x = SCREEN_WIDTH;
                        if( Hint.y < 0)
                            Hint.y = 0;
                        if( Hint.y > SCREEN_HEIGHT)
                            Hint.y = SCREEN_HEIGHT;
    
                    }
                }
    
                // und schließlich noch der Test, ob der Rubber ne Ecke gerammt hat
                else if(lc < Radius)
                {
                    CVertex d((VR - V2) / lc * Radius);     // dann den Rubber um den Radius von der Ecke
                    Pos = V2 + d;                           // von der Ecke wegschieben
    
                    // und wenns die letzte Ecke war, dann noch mal die erste Seite bearbeiten,
                    // da er bei der Verschiebung in die erste Seite geschoben worden sein kann
                    if(i == Obstacle.Vertices - 1) again = TRUE;
                }
            }
    
            V1 = V2;    // V2 wird an V1 weitergereicht um die Seiten durchzugehen (siehe oben)
        }
    }
    

    Wenn ihr Fehler entdeckt (die sollte es nur in den Kommentaren geben) oder Optimierungen ins Auge fasst, dann posten...

    mfG D1B



  • Wo sind die delete's?



  • http://www.peroxide.dk/download/tutorials/tut10/pxdtut10.html

    Hier steht (neben CollisionDetection) auch wie man den Slide-Vector berechnet...



  • THX



  • Hi @ all ^^.
    THX @ all. Wer auch immer als unregistrierter Squolly hier gepostet hat 😕 *g* hat die typischen Smilys vergessen .) DANKE ! Sehr geholfen 🙂 Werds auf jedenfall nochmal komplett durcharbeiten. Klasse ^^ *freu*

    cu @ all ^^ 🕶
    Squolly
    (wenn noch was ist. Ich meld mich .)


Anmelden zum Antworten