Diese Frage ist eine "Folgefrage" aus meiner vorherigen Frage zur Kollisionserkennung und -behebung, die Sie hier finden .
Wenn Sie die vorherige Frage nicht lesen möchten, finden Sie hier eine kurze Beschreibung der Funktionsweise meiner Physik-Engine:
Jede physische Entität wird in einer Klasse namens SSSPBody gespeichert.
Es werden nur AABBs unterstützt.
Jeder SSSPBody wird in einer Klasse namens SSSPWorld gespeichert, die jeden Körper aktualisiert und mit der Schwerkraft umgeht.
Jeder Frame, SSSPWorld aktualisiert jeden Körper.
Jeder aktualisierte Körper sucht in einem räumlichen Hash nach in der Nähe befindlichen Körpern und prüft, ob sie Kollisionen mit ihnen erkennen müssen. Wenn ja, rufen sie ein "Kollisions" -Ereignis auf und prüfen, ob sie Kollisionen mit ihnen auflösen müssen. Wenn ja, berechnen sie den Penetrationsvektor und die gerichtete Überlappung und ändern dann ihre Position, um die Penetration aufzulösen.
Wenn ein Körper mit einem anderen kollidiert, überträgt er seine Geschwindigkeit auf den anderen, indem er einfach die Geschwindigkeit des Körpers auf seine eigene setzt.
Ein Körper ist Geschwindigkeit wird auf 0 gesetzt, wenn er seine Position seit dem letzten Frame nicht geändert hat. Wenn es auch mit einem sich bewegenden Körper kollidiert (z. B. einem Aufzug oder einer sich bewegenden Plattform), berechnet es die Bewegungsdifferenz des Aufzugs, um festzustellen, ob sich der Körper nicht von seiner letzten Position bewegt hat.
Außerdem ruft ein Körper ein "Crushed" -Ereignis auf, wenn alle seine AABB-Ecken etwas in einem Frame überlappen.
Dies ist der vollständige Quellcode meines Spiels. Es ist in drei Projekte unterteilt. SFMLStart ist eine einfache Bibliothek, die die Eingabe, das Zeichnen und das Aktualisieren von Entitäten verwaltet. SFMLStartPhysics ist die wichtigste, bei der die Klassen SSSPBody und SSSPWorld vorhanden sind. PlatformerPhysicsTest ist das Spielprojekt, das die gesamte Spielelogik enthält.
Und dies ist die "Update" -Methode in der SSSPBody -Klasse, kommentiert und vereinfacht. Sie können sich das nur ansehen, wenn Sie keine Lust haben, das gesamte SFMLStartSimplePhysics-Projekt zu betrachten. (Und selbst wenn Sie dies tun, sollten Sie sich dies trotzdem ansehen, da es kommentiert ist.)
Das GIF zeigt zwei Probleme.
- Wenn Körper in einer anderen Reihenfolge platziert werden, treten unterschiedliche Ergebnisse auf. Die Kisten auf der linken Seite sind identisch mit den Kisten auf der rechten Seite, nur in umgekehrter Reihenfolge (im Editor).
- Beide Kisten sollten nach oben geschoben werden. In der Situation links werden keine Kisten befördert. Auf der rechten Seite ist nur einer von ihnen. Beide Situationen sind nicht beabsichtigt.
Erstes Problem: Reihenfolge der Aktualisierung
Das ist ziemlich einfach zu verstehen. In der Situation links wird die oberste Kiste vor der anderen aktualisiert. Selbst wenn die Kiste auf der Unterseite die Geschwindigkeit auf die andere "überträgt", muss sie auf das nächste Bild warten, um sich zu bewegen. Da es sich nicht bewegte, ist die Geschwindigkeit der unteren Kiste auf 0 gesetzt.
Ich habe keine Ahnung, wie ich das beheben soll. Ich würde es vorziehen, wenn die Lösung nicht davon abhängt, die Aktualisierungsliste zu "sortieren", da ich das Gefühl habe, dass ich im gesamten Design der Physik-Engine etwas falsch mache.
Wie gehen die wichtigsten Physik-Engines (Box2D, Bullet, Chipmunk) mit der Update-Bestellung um?
Zweites Problem: Nur eine Kiste wird zur Decke befördert
Ich verstehe noch nicht, warum das passiert. Was die "Feder" -Entität tut, ist die Geschwindigkeit des Körpers auf -4000 einzustellen und sie auf der Feder selbst neu zu positionieren. Auch wenn ich den Neupositionierungscode deaktiviere, tritt das Problem weiterhin auf.
Meine Idee ist, dass, wenn die untere Kiste mit der oberen Kiste kollidiert, ihre Geschwindigkeit auf 0 gesetzt wird. Ich bin nicht sicher, warum dies passiert.
Trotz der Möglichkeit, wie jemand auszusehen, der beim ersten Problem aufgibt, habe ich den gesamten Quellcode des Projekts oben gepostet. Ich habe nichts, um es zu beweisen, aber glauben Sie mir, ich habe mich sehr bemüht, das zu beheben, aber ich konnte einfach keine Lösung finden und ich habe noch keine Erfahrung mit Physik und Kollisionen. Ich habe mehr als eine Woche lang versucht, diese beiden Probleme zu lösen, und jetzt bin ich verzweifelt.
Ich glaube nicht, dass ich alleine eine Lösung finden kann, ohne viele Features aus dem Spiel zu streichen (z. B. Geschwindigkeitsübertragung und Federn).
Vielen Dank für die Zeit, die Sie mit dem Lesen dieser Frage verbracht haben, und noch mehr, wenn Sie versuchen, eine Lösung oder einen Vorschlag zu finden.
Antworten:
Die Reihenfolge der Aktualisierungsprobleme ist bei normalen Motoren für die Impulsphysik durchaus üblich. Sie können das Anwenden der Kraft nicht einfach verzögern, wie Vigil vorschlägt. Sie würden die Energieerhaltung unterbrechen, wenn ein Objekt gleichzeitig mit zwei anderen kollidiert. Normalerweise schaffen sie es jedoch, etwas zu schaffen, das ziemlich real zu sein scheint, obwohl eine andere Reihenfolge der Aktualisierung zu einem erheblich anderen Ergebnis geführt hätte.
In jedem Fall gibt es für Ihren Zweck genügend Schluckauf in einem Impulssystem, so dass ich vorschlagen würde, dass Sie stattdessen ein Massenfedermodell bauen.
Die Grundidee ist, dass anstatt zu versuchen, eine Kollision in einem Schritt aufzulösen, Sie eine Kraft auf die kollidierenden Objekte ausüben. Diese Kraft sollte der Überlappungsmenge zwischen den Objekten entsprechen. Dies ist vergleichbar damit, wie reale Objekte während einer Kollision ihre transformieren Bewegungsenergie in Verformung und dann wieder in Bewegung umwandeln. Das Tolle an diesem System ist, dass es Kraft ermöglicht, sich durch ein Objekt zu bewegen, ohne dass dieses Objekt hin und her springen muss, und dass es vernünftigerweise völlig unabhängig von der Reihenfolge der Aktualisierung durchgeführt werden kann.
Damit Objekte zum Stillstand kommen, anstatt auf unbestimmte Zeit herumzuspringen, müssen Sie eine Art Dämpfung anwenden. Sie können den Stil und das Gefühl Ihres Spiels stark beeinflussen, je nachdem, wie Sie es tun. Ein sehr grundlegender Ansatz wäre es jedoch, dies zu tun Wenden Sie eine Kraft auf zwei sich berührende Objekte an, die ihrer inneren Bewegung entsprechen. Sie können diese Kraft nur dann anwenden, wenn sie sich aufeinander zubewegen oder wenn sie sich voneinander entfernen. Mit letzterer können Sie das Zurückprallen von Objekten vollständig verhindern wenn sie auf dem Boden aufschlagen, machen sie aber auch ein wenig klebrig.
Sie können auch einen Reibungseffekt erzielen, indem Sie ein Objekt in der senkrechten Richtung einer Kollision bremsen. Das Ausmaß der Bremsung sollte dem Ausmaß der Überlappung entsprechen.
Sie könnten das Konzept der Masse ziemlich einfach umgehen, indem Sie alle Objekte auf die gleiche Masse bringen, und unbewegliche Objekte funktionieren wie unendliche Massen, wenn Sie die Beschleunigung einfach vernachlässigen.
Ein bisschen Pseudocode, nur für den Fall, dass das oben Genannte nicht klar genug ist:
Der Punkt der Eigenschaften addXvelocity und addYvelocity ist, dass diese nach Abschluss der Kollisionsbehandlung zur Geschwindigkeit ihres Objekts hinzugefügt werden.
Bearbeiten:
Sie können Aufgaben in der folgenden Reihenfolge ausführen, in der jeder Aufzählungspunkt für alle Elemente ausgeführt werden muss, bevor der nächste ausgeführt wird:
Außerdem ist mir klar, dass das Folgende in meinem ersten Beitrag möglicherweise nicht vollständig klar ist. Unter dem Einfluss der Schwerkraft überlappen sich Objekte, wenn sie aufeinanderliegen. Dies legt nahe, dass ihr Kollisionsfeld etwas höher als ihre grafische Darstellung sein sollte, um Überlappungen zu vermeiden visuell. Dieses Problem wird geringer sein, wenn die Physik mit einer höheren Aktualisierungsrate ausgeführt wird. Ich schlage vor, Sie versuchen, mit 120 Hz zu arbeiten, um einen angemessenen Kompromiss zwischen CPU-Zeit und physikalischer Genauigkeit zu erzielen.
Edit2:
Sehr grundlegender Ablauf der Physik-Engine:
acceleration = [Complicated formulas]
velocity += acceleration
position += velocity
quelle
Nun, du bist offensichtlich kein Mensch, der leicht aufgibt, du bist ein echter Mann aus Eisen, ich hätte meine Hände viel früher in die Luft geworfen, da dieses Projekt eine starke Ähnlichkeit mit einem Seetangwald hat :)
Zuallererst werden Positionen und Geschwindigkeiten überall festgelegt. Aus Sicht des Subsystems Physik ist dies ein Rezept für eine Katastrophe. Erstellen Sie außerdem beim Ändern integraler Elemente durch verschiedene Subsysteme private Methoden wie "ChangeVelocityByPhysicsEngine", "ChangeVelocityBySpring", "LimitVelocity", "TransferVelocity" oder ähnliches. Es wird die Möglichkeit hinzugefügt, Änderungen zu überprüfen, die von einem bestimmten Teil der Logik vorgenommen wurden, und diesen Geschwindigkeitsänderungen eine zusätzliche Bedeutung zu verleihen. Auf diese Weise wäre das Debuggen einfacher.
Erstes Problem.
Auf die Frage selbst. Jetzt wendest du nur Positions- und Geschwindigkeitsfixes in der Reihenfolge des Erscheinungsbilds und der Spiellogik an. Das funktioniert nicht für komplexe Interaktionen, ohne die Physik jeder komplexen Sache sorgfältig zu kodieren. Eine separate Physik-Engine wird dann nicht benötigt.
Um komplexe Interaktionen ohne Hacks durchzuführen, müssen Sie einen zusätzlichen Schritt zwischen der Erkennung von Kollisionen basierend auf Positionen, die durch Anfangsgeschwindigkeiten geändert wurden, und der endgültigen Änderung von Positionen basierend auf "Nachgeschwindigkeit" hinzufügen. Ich stelle mir vor, es würde so laufen:
Zusätzliche Dinge können auftauchen, wie z. B. das Ruckeln, die Weigerung, sich zu stapeln, wenn die FPS klein sind, oder andere ähnliche Dinge, seien Sie vorbereitet :)
Zweites Problem
Die vertikale Geschwindigkeit dieser beiden "Mitnahmekisten" ändert sich nie von Null. Seltsamerweise weisen Sie in der Update-Schleife von PhysSpring die Geschwindigkeit zu, aber in der Update-Schleife von PhysCrate ist sie bereits Null. Es ist möglich, eine Linie zu finden, in der die Geschwindigkeit nicht mehr stimmt, aber ich habe hier aufgehört zu debuggen, da es sich um "Reap What You Sew" handelt. Es ist an der Zeit, mit dem Programmieren aufzuhören und alles neu zu überdenken, wenn das Debuggen schwierig wird. Aber wenn es zu einem Punkt kommt, an dem selbst der Autor des Codes nicht mehr verstehen kann, was im Code vor sich geht, ist Ihre Codebasis bereits tot, ohne dass Sie es merken :)
Drittes Problem
Ich denke, dass etwas nicht stimmt, wenn Sie einen Teil von Farseer neu erstellen müssen, um einen einfachen kachelbasierten Plattformer zu erstellen. Persönlich würde ich Ihren aktuellen Motor für eine enorme Erfahrung halten und ihn dann für eine einfachere und direktere kachelbasierte Physik komplett ablegen. Dabei wäre es ratsam, Dinge wie Debug.Assert und vielleicht sogar Unit-Tests, oh der Horror, aufzugreifen, da es möglich wäre, unerwartete Dinge früher zu erkennen.
quelle
Ihr Problem ist, dass dies grundlegend falsche Annahmen über Bewegung sind, sodass das, was Sie erhalten, nicht der Bewegung ähnelt, wie Sie sie kennen.
Wenn ein Körper mit einem anderen kollidiert, bleibt der Impuls erhalten. Dies als "A Treffer B" im Vergleich zu "B Treffer A" zu betrachten, bedeutet, ein transitives Verb auf eine intransitive Situation anzuwenden. A und B kollidieren; Der resultierende Impuls muss dem anfänglichen Impuls entsprechen. Das heißt, wenn A und B die gleiche Masse haben, bewegen sie sich nun beide mit dem Mittelwert ihrer ursprünglichen Geschwindigkeiten.
Sie werden wahrscheinlich auch einen Kollisionsfehler und einen iterativen Löser benötigen, oder Sie werden auf Stabilitätsprobleme stoßen. Sie sollten wahrscheinlich einige der GDC-Präsentationen von Erin Catto durchlesen.
quelle
Ich denke, Sie haben wirklich großartige Anstrengungen unternommen, aber es scheint grundlegende Probleme mit der Struktur des Codes zu geben. Wie andere vorgeschlagen haben, kann es hilfreich sein, die Vorgänge in diskrete Teile zu unterteilen, z.
Durch das Trennen der Phasen werden alle Objekte nach und nach synchronisiert, und Sie haben nicht die Auftragsabhängigkeiten, mit denen Sie gerade zu kämpfen haben. Der Code erweist sich im Allgemeinen auch als einfacher und leichter zu ändern. Jede dieser Phasen ist ziemlich allgemein und es ist oft möglich, bessere Algorithmen zu ersetzen, nachdem Sie ein funktionierendes System haben.
Trotzdem ist jeder dieser Teile eine Wissenschaft für sich und kann viel Zeit in Anspruch nehmen, um die optimale Lösung zu finden. Es ist möglicherweise besser, mit einigen der am häufigsten verwendeten Algorithmen zu beginnen:
Ein guter (und offensichtlicher) Ausgangspunkt sind Newtons Bewegungsgesetze .
quelle