Ich habe eine einfache Kollisionserkennungsroutine unter Verwendung von AABBs zwischen meinem Hauptspiel-Sprite und verschiedenen Plattformen implementiert (siehe Code unten). Es funktioniert großartig, aber ich führe jetzt die Schwerkraft ein, um meinen Charakter fallen zu lassen, und es zeigt sich einige Probleme mit meiner CD-Routine.
Ich denke, der Kern der Sache ist, dass meine CD-Routine das Sprite entlang der Achse zurückbewegt, in der es am wenigsten in das andere Sprite eingedrungen ist . Wenn es sich also mehr in der Y-Achse als in der X-Achse befindet, wird es entlang der X-Achse zurückbewegt.
Jetzt fällt mein (Helden-) Sprite jedoch mit einer Geschwindigkeit von 30 Pixel pro Frame (es ist ein Bildschirm mit einer Höhe von 1504 - ich muss so schnell fallen, da ich versuchen möchte, die "normale" Schwerkraft zu simulieren, jedes langsamere sieht einfach komisch aus ) Ich bekomme diese Probleme. Ich werde versuchen, mit ein paar Bildern zu zeigen, was passiert (und was meiner Meinung nach es verursacht - obwohl ich nicht sicher bin): (Code unter den Bildern).
Ich würde mich über einige Vorschläge freuen, wie Sie dieses Problem umgehen können.
Zur Verdeutlichung ist es im obigen rechten Bild vielleicht etwas irreführend, wenn ich sage, dass die Position "falsch" korrigiert wurde. Der Code selbst funktioniert korrekt für die Art und Weise, wie er geschrieben wurde, oder anders ausgedrückt, der Algorithmus selbst funktioniert, wenn er sich so verhält, wie ich es erwarten würde, aber ich muss sein Verhalten ändern, um dieses Problem zu verhindern. Ich hoffe, das verdeutlicht meine Kommentare im Bild .
Mein Code
public boolean heroWithPlatforms(){
//Set Hero center for this frame
heroCenterX = hero.xScreen+(hero.quadWidth/2);
heroCenterY = hero.yScreen+(hero.quadHeight/2);
//Iterate through all platforms to check for collisions
for(x=0;x<platformCoords.length;x+=2){
//Set platform Center for this iteration
platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);
// the Dif variables are the difference (absolute value)
// of the center of the two sprites being compared (one for X axis difference
//and on for the Y axis difference)
difX = Math.abs(heroCenterX-platformCenterX);
difY = Math.abs(heroCenterY-platformCenterY);
//If 1
//Check the difference between the 2 centers and compare it to the vector (the sum of
//the two sprite's widths/2. If it is smaller, then the sprites are pverlapping along
//the X axis and we can now proceed to check the Y axis
if (difX<vecXPlatform){
//If 2
//Same as above but for the Y axis, if this is also true, then we have a collision
if(difY<vecYPlatform){
//we now know sprites are colliding, so we now need to know exactly by how much
//penX will hold the value equal to the amount one sprite (hero, in this case)
//has overlapped with the other sprite (the platform)
penX = vecXPlatform-difX;
penY = vecYPlatform-difY;
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite
//back on the X Axis
if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
//Sprite has penetrated into the other, mostly in the X asis, so move sprite
//back on the Y Axis
else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
return true;
}//Close 'If' 2
} //Close 'If' 1
}
//Otherwise, no collision
return false;
}
quelle
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Antworten:
Während meiner Experimente mit HTML5 Canvas und AABB habe ich genau das gefunden, was Sie erleben. Dies geschah, als ich versuchte, eine Plattform aus benachbarten Boxen mit 32 x 32 Pixel zu erstellen.
Lösungen, die ich in der Reihenfolge meiner persönlichen Präferenz versucht habe
1 - Teilen Sie die Bewegungen durch die Achse
Mein aktuelles, und ich denke, ich werde mein Spiel damit fortsetzen. Überprüfen Sie jedoch unbedingt , was ich Phantom AABB nenne, wenn Sie eine schnelle Lösung benötigen, ohne Ihr aktuelles Design stark ändern zu müssen.
Meine Spielwelt hat eine sehr einfache Physik. Es gibt (noch) kein Konzept von Kräften, nur Verschiebung. Die Schwerkraft ist eine konstante Verschiebung nach unten. (Noch) keine Beschleunigung.
Die Verschiebung erfolgt um die Achse. Und darin besteht diese erste Lösung.
Jeder Spielrahmen:
move_x wird gemäß der Eingabeverarbeitung eingestellt. Wenn die Pfeiltaste nicht gedrückt wird, ist move_x = 0. Werte unter Null bedeuten links, andernfalls rechts.
Verarbeiten Sie alle anderen sich bewegenden Sprites und bestimmen Sie die Werte ihrer "Moves" -Komponente. Sie haben auch die "Moves" -Komponente.
Hinweis: Nach der Kollisionserkennung und -reaktion muss die Bewegungskomponente auf Null gesetzt werden, sowohl die x- als auch die y-Eigenschaften, da beim nächsten Häkchen die Schwerkraft erneut hinzugefügt wird. Wenn Sie nicht zurücksetzen, enden Sie mit einer Bewegung, die das Zweifache der gewünschten Schwerkraft beträgt, und im nächsten Tick dreimal.
Geben Sie dann den Code für die Anwendung der Verschiebung und die Kollisionserkennung ein.
Die Bewegungskomponente ist nicht wirklich die aktuelle Position des Sprites. Das Sprite hat sich noch nicht bewegt, auch wenn sich movs.x oder y geändert haben. Die "Moves" -Komponente ist eine Möglichkeit, die Verschiebung zu speichern, die auf die Sprite-Position im richtigen Teil der Spielschleife angewendet werden soll.
Verarbeiten Sie zuerst die y-Achse (move.y). Wende mov.y auf sprite.position.y an. Wenden Sie dann die AABB-Methode an, um festzustellen, ob das sich bewegende bewegte Sprite eine Plattform-AABB überlappt (Sie haben bereits verstanden, wie das geht). Wenn es sich überlappt, bewegen Sie das Sprite durch Anwenden von penetration.y zurück in die y-Achse. Ja, im Gegensatz zu meiner anderen Antwort ignoriert diese von der Entwicklung entwickelte Methode jetzt die Penetration.x für diesen Schritt des Prozesses. Und wir verwenden die Penetration.y, selbst wenn sie größer als die Penetration.x ist, überprüfen wir sie nicht einmal.
Verarbeiten Sie dann die x-Achse (move.x). Wenden Sie move.x auf sprite.position.x an. Und mach das Gegenteil als zuvor. Sie bewegen das Sprite diesmal nur in der horizontalen Achse, indem Sie penetration.x anwenden. Nach wie vor ist es Ihnen egal, ob penetration.x kleiner oder größer als penetration.y ist.
Das Konzept hier ist, dass wenn sich das Sprite nicht bewegt und anfänglich nicht kollidierte, es im nächsten Frame so bleibt (sprite.moves.x und y sind Null). Der Sonderfall, in dem sich ein anderes Spielobjekt auf magische Weise an eine Position teleportiert, die den Sprite-Start überlappt, wird später behandelt.
Wenn sich ein Sprite zu bewegen beginnt. Wenn es sich nur in der x-Achse bewegt, sind wir nur interessiert, wenn es etwas von links oder rechts durchdringt. Da sich das Sprite nicht nach unten bewegt und wir dies wissen, weil die y-Eigenschaft der Bewegungskomponente Null ist, überprüfen wir nicht einmal den berechneten Wert für die Penetration.y, sondern nur bei penetration.x. Wenn eine Penetration in der Bewegungsachse vorhanden ist, wenden wir eine Korrektur an, andernfalls lassen wir das Sprite einfach bewegen.
Was ist mit der diagonalen Verschiebung, nicht unbedingt in einem Winkel von 45 Grad?
Das vorgeschlagene System kann damit umgehen. Sie müssen nur jede Achse einzeln bearbeiten. Während Ihrer Simulation. Auf das Sprite werden unterschiedliche Kräfte ausgeübt. Auch wenn Ihr Motor die Kräfte noch nicht versteht. Diese Kräfte führen zu einer beliebigen Verschiebung in der 2D-Ebene. Diese Verschiebung ist ein 2D-Vektor in jeder Richtung und beliebiger Länge. Aus diesem Vektor können Sie die y-Achse und die x-Achse extrahieren.
Was tun, wenn der Verschiebungsvektor zu groß ist?
Du willst das nicht:
Da unsere neuen Bewegungen, bei denen der Logikprozess angewendet wird, zuerst eine Achse und dann die andere verarbeiten, wird eine große Verschiebung möglicherweise vom Kollisionscode wie ein großes L erkannt. Dadurch werden Kollisionen mit Objekten, die Ihren Verschiebungsvektor schneiden, nicht korrekt erkannt.
Die Lösung besteht darin, große Verschiebungen in kleinen Schritten zu teilen, idealerweise Ihre Sprite-Breite oder -Höhe oder die Hälfte Ihrer Sprite-Breite oder -Höhe. So etwas erhalten:
Was ist mit dem Teleportieren von Sprites?
Wenn Sie Teleportation als große Verschiebung darstellen und dieselbe Bewegungskomponente wie eine normale Bewegung verwenden, ist dies kein Problem. Wenn Sie dies nicht tun, müssen Sie sich überlegen, wie Sie das Teleportations-Sprite markieren können, das vom Kollisionscode verarbeitet werden soll (Hinzufügen zu einer Liste, die diesem Zweck gewidmet ist?). Während des Antwortcodes können Sie das stehende Sprite, das sich darin befand, direkt verschieben die Art und Weise, als die Teleportation stattfand.
2 - Phantom AABB
Haben Sie einen sekundären AABB für jedes von der Schwerkraft betroffene Sprite, das am unteren Ende des Sprites beginnt, die gleiche Breite des AABB des Sprites hat und 1 Pixel hoch ist.
Die Idee hier ist, dass dieses Phantom-AABB immer mit der Plattform unter dem Sprite kollidiert. Nur wenn sich dieses Phantom-AABB nicht überlappt, darf die Schwerkraft auf das Sprite angewendet werden.
Sie müssen den Penetrationsvektor für das Phantom AABB und die Plattform nicht berechnen. Sie interessieren sich nur für einen einfachen Kollisionstest. Wenn er eine Plattform überlappt, kann die Schwerkraft nicht angewendet werden. Ihr Sprite kann eine boolesche Eigenschaft haben, die angibt, ob es sich über einer Plattform oder in der Luft befindet. Sie sind in der Luft, wenn Ihr Phantom-AABB nicht mit einer Plattform unter dem Player kollidiert.
Diese:
Wird so etwas:
Sehr einfach und zuverlässig.
Sie lassen Ihre AABB-Implementierung unverändert.
Der Phantom-AABB verfügt möglicherweise über eine dedizierte Schleife, die diese verarbeitet, nur nach einfachen Kollisionen sucht und keine Zeit mit der Berechnung des Penetrationsvektors verschwendet. Diese Schleife muss vor dem normalen Kollisions- und Antwortcode stehen. Sie können während dieser Schleife die Schwerkraft anwenden, da diese Sprites in der Luft die Schwerkraft anwenden. Wenn das Phantom AABB an sich eine Klasse oder Struktur ist, kann es einen Verweis auf das Sprite haben, zu dem es gehört.
Dies ist der anderen vorgeschlagenen Lösung sehr ähnlich, bei der Sie überprüfen, ob Ihr Spieler springt. Ich gebe eine mögliche Möglichkeit zu erkennen, ob der Spieler seine Füße über einer Plattform hat.
quelle
Ich würde die Plattformkacheln zu einer einzigen Plattformeinheit kombinieren.
Angenommen, Sie nehmen die 3 Kacheln aus den Bildern und kombinieren sie zu einer einzigen Entität. Ihre Entität hätte eine Kollisionsbox mit:
Wenn Sie dies für Kollisionen verwenden, erhalten Sie das y als die am wenigsten durchdringende Achse auf dem größten Teil der Plattform, während Sie dennoch bestimmte Kacheln innerhalb der Plattform haben können.
quelle
Probleme wie diese treten häufig bei neuen Kollisionserkennungsmethoden auf.
Ich weiß nicht viel über Ihr aktuelles Kollisionssetup, aber hier ist, wie es gehen sollte:
Machen Sie zunächst diese Variablen:
Erstellen Sie als Zweites einen Timer, um das Delta zu berechnen, das die Geschwindigkeit des Objekts beeinflusst.
Drittens machen Sie dies:
Wenn Sie dies tun, sollte es kein Problem geben. Hoffe das hilft!
quelle