knittel - Di 09.07.13 16:35
Titel: Kollisionsabfrage - Spieler mit Wand 2D
Hallo allerseits,
In meinem Spiel soll sich der Charakter durch eine Welt bewegen können. Seine Bewegungsrichtung ist komplett beliebig, d.h. er kann sich nicht nach Nord/Nord-Ost usw. bewegen sondern auch in einem z.B. 13° Winkel. In dem Spiel gibt es Wände mit denen der Charakter kollidieren kann. Und wenn der Spieler den Charakter auf eine Wand zu steuert, dann soll die beiden kollidieren und der Charakter soll sich nur noch in die Richtung bewegen die Parallel zum Verlauf der Wand ist (sowie man es aus allen Spielen kennt).
Zuerst hatte ich das nur für Wände gemacht die parallel zur X oder Y-Achse verliefen, aber nun mit dem Dazukommen von schrägen Wänden, hab ich so meine Probleme.
Das ist mein Kollisionscode, der erstmal nur prüft ob der Spieler mit der Wand kollidiert:
Variablen Erklärung:
nPos: Position des Spielers (vom Typ TMeleeCoord, besteht aus x, y (float) Koordinate);
nMov: Bewegungsvektor des Spielers
nSize: Radius der Kollisionsgröße des Spielers.
FLine.A: Startpunkt der Wand.
FLine.B: Endpunkt der Wand.
fv: Vektor von Start zu Endpunkt der Wand.
nv: Bewegungsvektor des Spielers
fvn: normalisiert(fv)
nvn: normalisiert(nv)
rN: Zahlen Teil von r (r =
rN + rS * s)
rS: s Teil von r (r = rN +
rS * s)
sN: Zahlen von s
hitlength: Verlängerungsmaß des Richtungsvektor um zu verhindern, dass wenn man schräg auf eine Wand zu geht, man dichter an die Wand gehen kann, als eigentlich von Kreisradius möglich wäre.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39:
| function TMeColliderLine.IsColliding(const nPos, nMov: TMeleeCoord; nSize: single = 0): boolean; var fv, nv, fvn, nvn: TMeleeCoord; q: boolean; rN, rS, sN, hitlength: single; begin result := false; if (Abs(nPos.X - FLine.A.X) > ABS(FLine.B.X - FLine.A.X) + ABS(nMov.X)) or (Abs(nPos.Y - FLine.A.Y) > ABS(FLine.B.Y - FLine.A.Y) + ABS(nMov.Y)) then exit; fv := meCalculate(FLine.B, me_Substract, FLine.A); nv := nMov; q := meIsParallel(fv, nv); if q then exit; if fv.X <> 0 then begin rN := (nPos.X - FLine.A.X) / fv.X; rS := (nv.X) / fv.X; sN := (FLine.A.Y + fv.Y * rN - nPos.Y) / (nv.Y - fv.Y * rS); rN := rN + rS * sN; end else begin rN := (nPos.Y - FLine.A.Y) / fv.Y; rS := (nv.Y) / fv.Y; sN := (FLine.A.X + fv.X * rN - nPos.X) / (nv.X - fv.X * rS); rN := rN + rS * sN; end; hitlength := abs(nSize / meLength(nv) * tan(90 - arccos(meDot(meNormalize(fv), meNormalize(nv))))); if (rN > 0 - nSize / meLength(fv)) and (rN < 1 + nSize / meLength(fv)) and (sN > 0 - hitlength) and (sN < 1 + hitlength) then begin result := true; FHitPoint := meCalculate(FLine.A, me_Add, fv); end; end; |
Bei einer Kollision wird Folgendes noch ausgeführt:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| var qVec: TMeleeCoord; x: single; begin qVec := meNormalize(meCalculate(FLine.B, me_Substract, FLine.A)); x := qVec.X; qVec.X := qVec.Y; qVec.Y := -x; qVec := meCalculate(qVec, me_Multiply, meLength(nMov) * meDot(qVec, meNormalize(nMov))); nPos := meCalculate(npos, me_Add, meCalculate(meNormalize(qVec), me_Multiply, )); nMov := meCalculate(nMov, me_Substract, qVec); end; |
Der Berechnungsteil ob sich die Geraden schneiden funktioniert, nur irgendwie kommt man zu Nahe an die Wand heran und stuckt in ihr. Manchmal kann man sich auch irgendwie durch sie hindurch bewegen, wenn man lange genug wild rumprobiert.
Wenn ihr eine andere Idee habt wie man das implementieren könnte so, dass es performant und "korrekt genug" ist immer her damit :D (es soll sich halt flüssig anfühlen). Ihr könnt aber auch Veränderungen an meinem Code vorschlagen, die weiterhelfen würden. Danke auf jeden Fall schonmal!
Mr_Emre_D - Di 09.07.13 17:09
Also sofern es sich bei deinen Objekten nicht um konvexe Polygone handelt, lässt sich das ganze über das SAT-Theorem lösen - Seperating Axis Theorem:
Sofern man bei der Projektion aller Punkte auf alle Geradennormale beider Polygone einen Zustand vorfindet, wo die projezierten maximalen "Geraden" einander nicht schneiden, so kollidieren beide Objekte auch nicht.
Evt schwer zu verdauen, aber
hier [
http://wiki.delphigl.com/index.php/Tutorial_Separating_Axis_Theorem] wirds verständlicher erklärt.
Vorallem diese zwei Bilder
1 [
http://wiki.delphigl.com/index.php/Datei:SAT_Keine_Kollision.jpg] -
2 [
http://wiki.delphigl.com/index.php/Datei:SAT_Kollision.jpg]
Moderiert von
Narses: Beiträge zusammengefasst
Damit stellste zunächst einmal fest, obs zu einer Kollision kommt (davor würd ich aber das ganze über Quadtrees optimieren und nen gröberen Kollisionscheck über sog. Bounding Boxes machen - jene Objekte/Polygone, die den Test nicht bestehen, werden näher untersucht (eben mit SAT).
Nachdem du per SAT festgestellt hast, dass sie kollidieren, müsstest du über Geradenschnitt alle Geraden miteinander schneiden um den Schnittpunkt zu bestimmen (hier gibts 2 Fälle: 1 Schnitt oder mehrere Schnittpunkte)
Wenn du dann den Punkt hast, kannste bel. damit anstellen. Meistens reichen aber auch beide Geraden (Sprich 2 Eckpunkte der beiden kollidierenden Objekte) auch aus (normalen Bestimmung usw)
Edit: Verdammt, hab mich selbst zitiert, anstelle meine Beitrag zu editieren =/