Ich bin gegen ein anderes Problem in meinem kleinen springenden Ballspiel angekommen.
Mein Ball springt gut herum, bis auf die letzten Momente, in denen er zur Ruhe kommt. Die Bewegung des Balls ist für den Hauptteil glatt, aber gegen Ende ruckelt der Ball eine Weile, während er sich auf dem Boden des Bildschirms niederlässt.
Ich kann verstehen, warum das passiert, aber ich kann es nicht glätten.
Für Ratschläge wäre ich dankbar.
Mein Update-Code lautet:
public void Update()
{
// Apply gravity if we're not already on the ground
if(Position.Y < GraphicsViewport.Height - Texture.Height)
{
Velocity += Physics.Gravity.Force;
}
Velocity *= Physics.Air.Resistance;
Position += Velocity;
if (Position.X < 0 || Position.X > GraphicsViewport.Width - Texture.Width)
{
// We've hit a vertical (side) boundary
// Apply friction
Velocity *= Physics.Surfaces.Concrete;
// Invert velocity
Velocity.X = -Velocity.X;
Position.X = Position.X + Velocity.X;
}
if (Position.Y < 0 || Position.Y > GraphicsViewport.Height - Texture.Height)
{
// We've hit a horizontal boundary
// Apply friction
Velocity *= Physics.Surfaces.Grass;
// Invert Velocity
Velocity.Y = -Velocity.Y;
Position.Y = Position.Y + Velocity.Y;
}
}
Vielleicht sollte ich auch darauf hinweisen Gravity
, Resistance
Grass
und Concrete
sind alle vom Typ Vector2
.
Antworten:
Hier sind die Schritte aufgeführt, die zur Verbesserung Ihrer Physiksimulationsschleife erforderlich sind.
1. Zeitschritt
Das Hauptproblem, das ich mit Ihrem Code sehen kann, ist, dass es die physikalische Schrittzeit nicht berücksichtigt. Es sollte offensichtlich sein, dass etwas nicht stimmt,
Position += Velocity;
da die Einheiten nicht übereinstimmen. EntwederVelocity
ist eigentlich keine Geschwindigkeit oder es fehlt etwas.Auch wenn Ihre Geschwindigkeits- und Schwerkraftwerte so skaliert sind, dass jeder Frame in einer Zeiteinheit abläuft
1
(was bedeutet, dass z. B.Velocity
tatsächlich die zurückgelegte Distanz in einer Sekunde ist), muss die Zeit implizit irgendwo in Ihrem Code erscheinen (indem Sie die Variablen so festlegen, dass ihre Namen spiegeln wider, was sie wirklich speichern) oder explizit (durch Einführung eines Zeitschritts). Ich glaube, am einfachsten ist es, die Zeiteinheit anzugeben:Und verwenden Sie diesen Wert überall dort, wo er benötigt wird:
Beachten Sie, dass jeder anständige Compiler die Multiplikationen um ein Vielfaches vereinfacht
1.0
, so dass der Teil die Dinge nicht langsamer macht.Jetzt
Position += Velocity * TimeStep
ist noch nicht ganz genau (siehe diese Frage, um zu verstehen, warum), aber es wird wahrscheinlich vorerst tun.Dies muss auch Zeit in Betracht ziehen:
Es ist etwas schwieriger zu beheben; Ein möglicher Weg ist:
2. Doppelte Updates
Überprüfen Sie nun, was Sie beim Bouncen tun (nur relevanter Code wird angezeigt):
Sie können sehen, dass
TimeStep
während des Abprallens zweimal verwendet wird. Dies gibt dem Ball im Grunde doppelt so viel Zeit, um sich selbst zu aktualisieren. Dies sollte stattdessen geschehen:3. Schwerkraft
Überprüfen Sie jetzt diesen Teil des Codes:
Sie fügen die Schwerkraft für die gesamte Dauer des Rahmens hinzu. Aber was ist, wenn der Ball während dieses Rahmens tatsächlich springt? Dann wird die Geschwindigkeit umgekehrt, aber die hinzugefügte Schwerkraft lässt den Ball vom Boden weg beschleunigen! Daher muss die überschüssige Schwerkraft beim Abprallen entfernt und in der richtigen Richtung wieder hinzugefügt werden.
Es kann vorkommen, dass bereits die erneute Hinzufügung der Schwerkraft in die richtige Richtung zu einer zu starken Beschleunigung der Geschwindigkeit führt. Um dies zu vermeiden, können Sie entweder die Gravitationsaddition überspringen (immerhin ist es nicht so viel und es dauert nur einen Frame) oder die Geschwindigkeit auf Null klemmen.
4. Festcode
Und hier ist der vollständig aktualisierte Code:
5. Weitere Ergänzungen
Um die Stabilität der Simulation noch weiter zu verbessern, können Sie Ihre Physiksimulation mit einer höheren Frequenz ausführen. Dies wird durch die obigen Änderungen, die mit sich bringen , trivial gemacht
TimeStep
, da Sie Ihren Rahmen nur in so viele Teile aufteilen müssen, wie Sie möchten. Zum Beispiel:quelle
velocity += gravity
es falsch ist und nurvelocity += gravity * timestep
Sinn ergibt . Es kann am Ende das gleiche Ergebnis liefern, aber ohne einen Kommentar mit der Aufschrift "Ich weiß, was ich hier tue" bedeutet es immer noch einen Codierungsfehler, einen schlampigen Programmierer, einen Mangel an Kenntnissen über Physik oder nur einen Prototyp-Code, der benötigt wird verbessert werden.gravity
tatsächlich… nicht die Schwerkraft ist. Aber ich kann das in der Post klarer machen.Fügen Sie ein Häkchen hinzu, um das Abprallen mit einer minimalen vertikalen Geschwindigkeit zu stoppen. Und wenn Sie den minimalen Sprung bekommen, legen Sie den Ball in den Boden.
quelle
Ich denke also, das Problem, warum dies passiert, ist, dass sich Ihr Ball einem Limit nähert. Mathematisch gesehen bleibt der Ball niemals auf der Oberfläche stehen, er nähert sich der Oberfläche.
Ihr Spiel verwendet jedoch keine kontinuierliche Zeit. Es ist eine Karte, die eine Annäherung an die Differentialgleichung verwendet. Und diese Annäherung ist in dieser einschränkenden Situation nicht gültig (Sie können, aber Sie müssten kleinere und kleinere Zeitschritte machen, was meiner Meinung nach nicht machbar ist.
Physikalisch gesehen ist es so, dass der Ball, wenn er sich sehr nahe an der Oberfläche befindet, daran haftet, wenn die Gesamtkraft unter einer bestimmten Schwelle liegt.
@Zhen Antwort wäre in Ordnung, wenn Ihr System homogen ist, was nicht ist. Es hat eine gewisse Schwerkraft auf der y-Achse.
Daher würde ich sagen, dass die Lösung nicht darin bestehen sollte, dass die Geschwindigkeit unter einem bestimmten Schwellenwert liegt, sondern dass die nach der Aktualisierung auf den Ball ausgeübte Gesamtkraft unter einem bestimmten Schwellenwert liegt.
Diese Kraft ist der Beitrag der von der Wand auf den Ball ausgeübten Kraft + der Schwerkraft.
Die Bedingung sollte dann so ähnlich sein
if (newVelocity + Physics.Gravity.Force <Schwelle)
Beachten Sie, dass newVelocity.y eine positive Größe ist, wenn sich der Sprung auf der Bodenwand befindet, und die Schwerkraft eine negative Größe ist.
Beachten Sie auch, dass newVelocity und Physics.Gravity.Force nicht dieselben Dimensionen haben, wie Sie geschrieben haben
Das heißt, ich gehe wie Sie von delta_time = 1 und ballMass = 1 aus.
Hoffe das hilft
quelle
Sie haben eine Positionsaktualisierung in Ihrer Kollisionsprüfung, sie ist redundant und falsch. Und es fügt dem Ball Energie hinzu und hilft ihm so möglicherweise, sich ständig zu bewegen. Zusammen mit der Schwerkraft, die bei einigen Bildern nicht angewendet wird, entsteht eine merkwürdige Bewegung. Entfernen Sie es.
Jetzt sehen Sie möglicherweise ein anderes Problem: Der Ball bleibt außerhalb des festgelegten Bereichs "stecken" und springt ständig vor und zurück.
Eine einfache Möglichkeit, dieses Problem zu lösen, besteht darin, zu überprüfen, ob sich der Ball in die richtige Richtung bewegt, bevor Sie ihn ändern.
Also solltest du machen:
In:
Ähnliches gilt für die Y-Richtung.
Damit der Ball gut anhält, muss die Schwerkraft irgendwann gestoppt werden. Ihre aktuelle Implementierung stellt sicher, dass der Ball immer wieder auftaucht, da die Schwerkraft ihn nicht bremst, solange er sich im Untergrund befindet. Sie sollten immer die Schwerkraft anwenden. Dies führt jedoch dazu, dass der Ball nach dem Absetzen langsam in den Boden sinkt. Eine schnelle Lösung hierfür ist, nach dem Anwenden der Schwerkraft den Ball anzuhalten, wenn er sich unter der Oberfläche befindet und sich nach unten bewegt:
Diese Änderungen insgesamt sollten Ihnen eine anständige Simulation geben. Beachten Sie jedoch, dass es sich immer noch um eine sehr einfache Simulation handelt.
quelle
Haben Sie eine Mutator-Methode für alle Geschwindigkeitsänderungen, dann können Sie innerhalb dieser Methode die aktualisierte Geschwindigkeit überprüfen, um festzustellen, ob sie sich langsam genug bewegt, um sie in den Ruhezustand zu versetzen. Die meisten mir bekannten physikalischen Systeme bezeichnen dies als "Restitution".
Bei der obigen Methode begrenzen wir das Abprallen, wenn es sich entlang derselben Achse wie die Schwerkraft befindet.
Eine weitere Überlegung wäre, festzustellen, wann ein Ball auf den Boden gestoßen ist, und die Geschwindigkeit entlang der Schwerkraftachse auf Null zu setzen, wenn er sich zum Zeitpunkt der Kollision relativ langsam bewegt.
quelle
Eine andere Sache: Sie multiplizieren mit einer Reibungskonstante. Ändern Sie das - senken Sie die Reibungskonstante, fügen Sie jedoch eine feste Energieabsorption für einen Sprung hinzu. Dadurch werden die letzten Sprünge viel schneller gedämpft.
quelle