Kollisions Reaktion o-O?
-
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 gefundenBitte helft mir (Pseudo oder C++ Code gern gesehn
cu
Squolly
-
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 SlideBye
-
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 .)
-
Cool, ab in die FAQ : Kollisions Reaktion aka Sliden