Ich habe ein Problem mit der AABB-Kollisionsauflösung.
Ich löse AABB-Schnittpunkte auf, indem ich zuerst die X-Achse und dann die Y-Achse auflöse. Dies geschieht, um diesen Fehler zu vermeiden: http://i.stack.imgur.com/NLg4j.png
Die aktuelle Methode funktioniert einwandfrei, wenn sich ein Objekt in den Player bewegt und der Player horizontal verschoben werden muss. Wie Sie im GIF sehen können, drücken die horizontalen Stacheln den Player richtig.
Wenn sich die vertikalen Stacheln in den Player hineinbewegen, wird die X-Achse immer noch zuerst aufgelöst. Dies macht die "Verwendung der Spikes als Aufzug" unmöglich.
Wenn sich der Spieler in die vertikalen Stacheln bewegt (von der Schwerkraft beeinflusst, fällt er hinein), wird er auf die Y-Achse gedrückt, da es anfangs keine Überlappung auf der X-Achse gab.
Ich habe versucht, die in der ersten Antwort dieses Links beschriebene Methode zu verwenden: 2D-Rechteckobjekt-Kollisionserkennung
Die Spikes und sich bewegenden Objekte bewegen sich jedoch, indem ihre Position geändert wird, nicht die Geschwindigkeit, und ich berechne ihre nächste vorhergesagte Position erst, wenn die Update () -Methode aufgerufen wird. Natürlich hat diese Lösung auch nicht funktioniert. :(
Ich muss die AABB-Kollision so lösen, dass beide oben beschriebenen Fälle wie beabsichtigt funktionieren.
Dies ist mein aktueller Kollisionsquellcode: http://pastebin.com/MiCi3nA1
Ich wäre wirklich dankbar, wenn jemand dies untersuchen könnte, da dieser Fehler von Anfang an in der Engine vorhanden war und ich mich bemüht habe, eine gute Lösung zu finden, ohne Erfolg. Dies bringt mich dazu, Nächte damit zu verbringen, mir den Kollisionscode anzuschauen und zu verhindern, dass ich zum "lustigen Teil" komme und die Spiellogik codiere :(
Ich habe versucht, dasselbe Kollisionssystem wie in der XNA AppHub-Plattform-Demo zu implementieren (indem ich die meisten Elemente kopiere und einfüge). Der "springende" Fehler tritt jedoch in meinem Spiel auf, während er in der AppHub-Demo nicht auftritt. [Jumping Bug: http://i.stack.imgur.com/NLg4j.png ]
Zum Springen überprüfe ich, ob der Spieler "onGround" ist und addiere -5 zu Velocity.Y.
Da die Velocity.X des Players höher ist als die Velocity.Y (siehe viertes Feld im Diagramm), wird onGround auf true gesetzt, wenn dies nicht der Fall sein sollte, und der Player springt in die Luft.
Ich glaube, dass dies in der AppHub-Demo nicht der Fall ist, da Velocity.X des Players niemals höher sein wird als Velocity.Y, aber ich kann mich irren.
Ich habe das zuvor gelöst, indem ich zuerst auf der X-Achse, dann auf der Y-Achse aufgelöst habe. Aber das schadet der Kollision mit den Spikes, wie ich oben sagte.
quelle
Antworten:
OK, ich habe herausgefunden, warum die XNA AppHub-Plattform-Demo nicht den "springenden" Fehler aufweist: Die Demo testet die Kollisionskacheln von oben nach unten . Wenn sich der Spieler an einer "Wand" befindet, überlappt er möglicherweise mehrere Kacheln. Die Auflösungsreihenfolge ist wichtig, da das Auflösen einer Kollision auch andere Kollisionen auflösen kann (jedoch in eine andere Richtung). Die
onGround
Eigenschaft wird nur festgelegt, wenn die Kollision behoben wird, indem der Player auf der y-Achse nach oben gedrückt wird. Diese Auflösung tritt nicht auf, wenn der Player durch die vorherigen Auflösungen nach unten und / oder horizontal gedrückt wurde.Ich konnte den "springenden" Fehler in der XNA-Demo reproduzieren, indem ich diese Zeile änderte:
dazu:
(Ich habe auch einige physikalische Konstanten angepasst, aber das sollte keine Rolle spielen.)
Vielleicht
bodiesToCheck
behebt das Sortieren auf der y-Achse vor dem Beheben der Kollisionen in Ihrem Spiel den "springenden" Fehler. Ich schlage vor, die Kollision auf der "flachen" Durchdringungsachse zu beheben, wie es die XNA-Demo und Trevor vorschlagen . Beachten Sie auch, dass der XNA-Demo-Player doppelt so groß ist wie die kollidierbaren Kacheln, wodurch die Wahrscheinlichkeit für Mehrfachkollisionen steigt.quelle
Position = nextPosition
sofort innerhalb derfor
Schleife, sonstonGround
treten die unerwünschten Kollisionsauflösungen (Einstellung ) immer noch auf. Der Player sollte beim Aufprall auf die Decke senkrecht nach unten (und niemals nach oben) gedrücktonGround
werden. So macht es die XNA-Demo, und ich kann den "Decken" -Fehler dort nicht wiederholen.onGround
eingestellt ist, daher kann ich nicht untersuchen, warum das Springen falsch erlaubt ist. Die XNA-Demo aktualisiert ihre analogeisOnGround
Eigenschaft im InnerenHandleCollisions()
.Velocity.Y
Warum bewegt die Schwerkraft den Player nach dem Einstellen auf 0 nicht wieder nach unten? Ich vermute, dieonGround
Eigenschaft ist falsch eingestellt. Schauen Sie sich an, wie die XNA-Demo aktualisiert wirdpreviousBottom
undisOnGround
(IsOnGround
).Die einfachste Lösung für Sie besteht darin, beide Kollisionsrichtungen für jedes Objekt auf der Welt zu überprüfen, bevor Sie Kollisionen auflösen, und nur die kleinere der beiden resultierenden "zusammengesetzten Kollisionen" aufzulösen. Dies bedeutet, dass Sie so wenig wie möglich auflösen, anstatt immer zuerst x oder immer zuerst y aufzulösen.
Ihr Code würde ungefähr so aussehen:
große Überarbeitung : Beim Lesen von Kommentaren zu anderen Antworten habe ich wohl endlich eine unausgesprochene Vermutung bemerkt, die dazu führt, dass dieser Ansatz nicht funktioniert (und erklärt, warum ich die Probleme, die einige - aber nicht alle - haben, nicht verstehen konnte. Leute sahen mit diesem Ansatz). Um dies zu erläutern, hier ist ein weiterer Pseudocode, der genauer zeigt, was die Funktionen, auf die ich zuvor verwiesen habe, eigentlich tun sollen:
Dies ist kein "Pro-Objekt-Paar" -Test. Es funktioniert nicht, wenn das sich bewegende Objekt für jede Kachel einer Weltkarte einzeln getestet und aufgelöst wird (dieser Ansatz funktioniert nie zuverlässig und scheitert mit abnehmender Kachelgröße auf immer katastrophalere Weise). Stattdessen wird das sich bewegende Objekt gegen jedes Objekt auf der Weltkarte gleichzeitig getestet und dann basierend auf Kollisionen gegen die gesamte Weltkarte aufgelöst.
Auf diese Weise stellen Sie sicher, dass (zum Beispiel) einzelne Wandkacheln innerhalb einer Wand den Spieler niemals zwischen zwei benachbarten Kacheln auf und ab hüpfen lassen, was dazu führt, dass der Spieler in einem nicht vorhandenen Raum zwischen ihnen eingeschlossen wird. Die Abstände der Kollisionsauflösung werden immer bis zum leeren Weltraum berechnet, nicht nur bis zur Grenze einer einzelnen Kachel, auf der sich möglicherweise eine weitere feste Kachel befindet.
quelle
if(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
Bearbeiten
Ich habe es verbessert
Das Wichtigste, was ich geändert habe, scheint die Klassifizierung der Kreuzungen zu sein.
Schnittpunkte sind entweder:
und ich löse sie in dieser Reihenfolge
Definiere eine Bodenkollision als Spieler, der mindestens 1/4 Weg auf dem Plättchen ist
Das ist also eine Bodenkollision und der Spieler ( blau ) wird oben auf dem Plättchen sitzen ( schwarz )
Dies ist jedoch KEINE Bodenkollision und der Spieler "rutscht" auf der rechten Seite des Plättchens, wenn er darauf landet
Auf diese Weise bleibt der Spieler nicht mehr an den Wänden hängen
quelle
Forenote:
Meine Engine verwendet eine Kollisionserkennungsklasse, die Kollisionen mit allen Objekten in Echtzeit überprüft, und zwar nur dann, wenn sich ein Objekt bewegt, und nur auf den Gitterquadraten, die es derzeit einnimmt.
Meine Lösung:
Als ich in meinem 2d-Plattformer auf solche Probleme stieß, habe ich Folgendes getan:
- (Die Engine erkennt mögliche Kollisionen schnell)
- (Das Spiel weist die Engine an, die genauen Kollisionen für ein bestimmtes Objekt zu überprüfen.) [Gibt den Vektor des Objekts zurück. *]
- (Das Objekt betrachtet die Eindringtiefe sowie die vorherige Position im Verhältnis zur vorherigen Position des anderen Objekts. um zu bestimmen, von welcher Seite herauszugleiten ist)
- (Objekt bewegt sich und mittelt seine Geschwindigkeit mit der Geschwindigkeit des Objekts (wenn sich das Objekt bewegt))
quelle
In Videospielen, die ich programmiert habe, sollte der Ansatz eine Funktion haben, die angibt, ob Ihre Position gültig ist, d. H. bool ValidPos (int x, int y, int wi, int he);
Die Funktion prüft den Begrenzungsrahmen (x, y, wi, he) gegen alle Geometrien im Spiel und gibt false zurück, wenn es eine Schnittmenge gibt.
Wenn Sie sich bewegen möchten, sagen Sie nach rechts, nehmen Sie die Spielerposition, addieren Sie 4 zu x und prüfen Sie, ob die Position gültig ist. Wenn nicht, prüfen Sie mit + 3, + 2 usw., bis dies der Fall ist.
Wenn Sie auch die Schwerkraft benötigen, benötigen Sie eine Variable, die wächst, solange Sie nicht auf dem Boden aufschlagen (Auftreffen auf den Boden: ValidPos (x, y + 1, wi, he) == true, y ist hier nach unten positiv). Wenn Sie diese Strecke zurücklegen können (dh ValidPos (x, y + Schwerkraft, wi, er) gibt true zurück), fallen Sie. Dies ist manchmal nützlich, wenn Sie Ihren Charakter beim Fallen nicht kontrollieren können.
Ihr Problem ist nun, dass Sie Objekte in Ihrer Welt haben, die sich bewegen. Überprüfen Sie also zunächst, ob Ihre alte Position noch gültig ist!
Wenn dies nicht der Fall ist, müssen Sie eine Position suchen, bei der es sich um eine handelt. Wenn sich Objekte im Spiel nicht schneller als beispielsweise 2 Pixel pro Spielumdrehung bewegen können, müssten Sie prüfen, ob die Position (x, y-1) gültig ist, dann (x, y-2), dann (x + 1, y) usw usw. Der gesamte Abstand zwischen (x-2, y-2) und (x + 2, y + 2) sollte überprüft werden. Wenn es keine gültige Position gibt, bedeutet dies, dass Sie "gequetscht" wurden.
HTH
Valmond
quelle
Ich habe ein paar Fragen, bevor ich anfange, diese zu beantworten. Waren diese Kacheln in dem ursprünglichen Käfer, in dem Sie in den Wänden stecken geblieben sind, auf den linken einzelnen Kacheln im Gegensatz zu einer großen Kacheln? Und wenn ja, steckte der Spieler zwischen ihnen fest? Wenn Sie beide Fragen mit Ja beantworten, vergewissern Sie sich, dass Ihre neue Position gültig ist . Das bedeutet, dass Sie überprüfen müssen, ob eine Kollision vorliegt, an der Sie dem Spieler mitteilen, dass er sich bewegen soll. Löse also die minimale Verschiebung wie unten beschrieben und bewege deinen Spieler danach nur, wenn er kanndorthin ziehen. Fast zu tief unter der Nase: P Dies führt tatsächlich zu einem weiteren Fehler, den ich "Eckfälle" nenne. Im Wesentlichen in Bezug auf Ecken (wie links unten, wo die horizontalen Spitzen in Ihrem GIF herauskommen, aber wenn es keine Spitzen gäbe) würde eine Kollision nicht aufgelöst, da davon ausgegangen wird, dass keine der von Ihnen generierten Auflösungen zu einer gültigen Position führt . Behalten Sie zur Behebung dieses Problems einfach einen Überblick darüber, ob die Kollision behoben wurde, sowie eine Liste aller Mindestauflösungen für die Penetration. Wenn die Kollision nicht behoben wurde, durchlaufen Sie anschließend jede von Ihnen erzeugte Auflösung und verfolgen Sie die maximale X- und Y-Auflösung (die Maximalwerte müssen nicht aus derselben Auflösung stammen). Beheben Sie dann die Kollision mit diesen Maximalwerten. Dies scheint alle Ihre Probleme sowie die Probleme zu lösen, auf die ich gestoßen bin.
Eine andere Frage: Sind die Stacheln, die Sie zeigen, eine Kachel oder einzelne Kacheln? Wenn es sich um einzelne dünne Kacheln handelt, müssen Sie möglicherweise einen anderen Ansatz für die horizontalen und vertikalen Kacheln verwenden als im Folgenden beschrieben. Aber wenn es ganze Kacheln sind, sollte das funktionieren.
Okay, im Grunde ist es das, was @ Trevor Powell beschrieb. Da Sie nur AABBs verwenden, müssen Sie nur feststellen, wie weit ein Rechteck in das andere eindringt. Dies gibt Ihnen eine Größe auf der X-Achse und der Y-Achse. Wählen Sie das Minimum aus den beiden aus und bewegen Sie Ihr kollidierendes Objekt entlang dieser Größe. Das ist alles, was Sie brauchen, um eine AABB-Kollision zu beheben. Sie müssen sich bei einer solchen Kollision NIEMALS entlang mehr als einer Achse bewegen, daher sollten Sie sich niemals darüber im Klaren sein, welche zuerst bewegt werden soll, da Sie nur das Minimum bewegen.
Metanet Software verfügt über ein klassisches Tutorial auf einen Ansatz hier . Es geht auch in andere Formen.
Hier ist eine XNA-Funktion, die ich erstellt habe, um den Überlappungsvektor zweier Rechtecke zu finden:
(Ich hoffe, es ist einfach zu befolgen, da ich mir sicher bin, dass es bessere Möglichkeiten gibt, es umzusetzen ...)
isCollision (Rectangle) ist buchstäblich nur ein Aufruf von Rectangle.Intersects (Rectangle) von XNA.
Ich habe dies mit beweglichen Plattformen getestet und es scheint gut zu funktionieren. Ich werde weitere Tests durchführen, die Ihrer .gif-Datei ähneln, um sicherzustellen, dass sie nicht funktioniert, und einen entsprechenden Bericht erstatten.
quelle
Es ist einfach.
Schreiben Sie die Spikes neu. Es ist die Schuld der Spikes.
Ihre Kollisionen sollten in diskreten, greifbaren Einheiten stattfinden. Das Problem ist meines Erachtens:
Das Problem ist mit Schritt 2, nicht 3!
Wenn Sie versuchen, die Dinge solide zu machen, sollten Sie nicht zulassen, dass Gegenstände auf diese Weise ineinander gleiten. Sobald Sie sich in einer Kreuzungsposition befinden und Ihren Platz verlieren, wird es schwieriger, das Problem zu lösen.
Die Stacheln sollten idealerweise die Existenz des Spielers überprüfen und ihn bei Bedarf schieben , wenn sie sich bewegen .
Eine einfache Möglichkeit , dies zu erreichen , ist für die Spieler haben
moveX
undmoveY
Funktionen , die die Landschaft zu verstehen und werden den Spieler durch ein bestimmtes Delta schieben oder so weit wie möglich , ohne ein Hindernis zu treffen .Normalerweise werden sie von der Ereignisschleife aufgerufen. Sie können jedoch auch von Objekten aufgerufen werden, um den Player zu schieben.
Offensichtlich muss der Spieler immer noch auf die Stacheln reagieren, wenn er in sie eindringt .
Die übergeordnete Regel lautet, dass ein Objekt , nachdem es sich bewegt hat, an einer Position ohne Schnittpunkt enden soll. Auf diese Weise ist es unmöglich, die Pannen zu bekommen, die Sie sehen.
quelle
moveY
die Kreuzung nicht entfernt werden kann (weil eine andere Wand im Weg ist), können Sie erneut nach einer Kreuzung suchen und entweder moveX versuchen oder ihn einfach töten.