Warum verhält sich die Physik in der Einheit nicht konsequent?

7

Ich mache ein Spiel, in dem KI-Spieler einen Ball werfen.

Bildschirmfoto

Im Screenshot sehen Sie die Arena, die in der Mitte durch ein Netz getrennt ist. Jede Seite der Arena hat 6 Spieler, die zu einem von 2 Teams gehören. Im Moment hat einer der Spieler auf der rechten Seite den Ball (der Spieler oben rechts). Er wird den Ball auf die linke Seite werfen.

Nach meinem Code sollte der Ball immer an der gleichen Stelle landen, da der Spieler immer die gleiche Kraft auf den Ball ausübt. Mir ist jedoch aufgefallen, dass der Ball nicht immer an derselben Stelle landet. Ich habe markiert, wo ich den Ball landen gesehen habe, mit mehreren roten Kreisen.

Interessanterweise landet der Ball auf meinem Haupt-PC fast immer auf dem Kreis ganz rechts. Es landet selten auf den anderen Kreisen, aber das ist nicht geschehen. Auf 3 anderen PCs, auf denen ich dieses Spiel getestet habe, landet der Ball jedoch meistens auf den Beinen des Spielers oben links, manchmal auf seinem Kopf oder hinter ihm, aber niemals auf dem Kreis ganz rechts (vor ihm) ).

Mein Spiel hängt stark von der Physik ab. Die KI soll berechnen, wie der Ball geworfen werden soll, damit er an einem bestimmten Ort landet, und je nach Position und Bewegung des Balls erraten, wo er landen wird. Wenn sich die Physik nicht immer auf allen Maschinen konsistent verhält, wie soll die KI all dies genau berechnen?

Hier ist der Code, der für das Werfen zuständig ist:

private IEnumerator Serve()
{
    var ball = GameObject.Find("Ball").GetComponent<BallController>();
    if (ball.ServingPlayer != this)
        yield break;

    yield return new WaitForSeconds(3);

    Vector3 shootDirection = transform.forward.normalized;
    var q = Quaternion.AngleAxis(-45, transform.right);

    ball.RigidBody.AddForce(q * shootDirection * 20f, ForceMode.VelocityChange);
}

Wie Sie sehen können, wird der Ball immer "nach vorne" (in Richtung des Netzes) in einem Winkel von 45 ° nach oben geworfen, wobei ein normalisierter Vektor und eine konstante Geschwindigkeit ( 20f) verwendet werden.

Vor jedem Wurf wird die Position der Spieler auf genau die Position (und Drehung) zurückgesetzt, die sie auf dem Screenshot sehen.

player.transform.localEulerAngles = new Vector3(0, playerRotation, 0);
player.transform.position = startingposition + posOffset;

In diesem Fall playerRotationist entweder 90foder -90fabhängig davon, auf welcher Seite sich der Spieler befindet, startingpositionseine "horizontale" Position (relativ zum Netz) und posOffsetseine "vertikale" Position.

Der Ball hat seine Geschwindigkeit und Winkelgeschwindigkeit zurückgesetzt, aber nicht seine Drehung (da es sich um eine Kugel handelt, sollte seine Drehung nichts beeinflussen). Seine Position ist auch auf einen festen Ort relativ zu dem Spieler eingestellt, der den Ball bedienen muss.

rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
transform.position = ServingPlayer.ServingPosition;

Hier rbist der Ball RigidBody.

Schließlich beeinflusse ich die Bewegung des Balls in keiner Weise. Ich übe beim Servieren einfach eine Kraft darauf aus, was nur einmal vorkommt. 3 Sekunden nachdem der Ball gelandet ist, wird das Match zurückgesetzt und alles wiederholt sich.

Der Fluss ist im Grunde wie folgt (Pseudocode):

Start()

ResetCourt()
StartCoroutine(Players[0].Serve())
event BallLanded()
wait for 3 seconds

// repeat starting with ResetCourt()

Ich brauche wirklich die Flugbahn des Balls, um jedes Mal auf jeder möglichen Maschine reproduzierbar zu sein. Natürlich mache ich hier etwas falsch, aber was?

Nolonar
quelle
2
Meine Vermutung; Die Physik-Engine hat keinen festen Zeitschritt, was zu einem leicht unterschiedlichen Verhalten führt, je nachdem, wie schnell Ihre CPU läuft. Könnte Ihr Physik-Zeitschritt mit Ihrem Rendering-Zeitschritt verknüpft sein?
Richard Tingle
@ RichardTingle. Ich denke du bist auf etwas. Ich habe mir die Zeiteinstellungen meines Projekts angesehen und mich entschlossen, mit dem festen Zeitschritt herumzuspielen. Durch Erhöhen von 0,02 (50 Updates pro Sekunde) auf 0,04 verhielt sich mein Haupt-PC eher wie die 3 Laptops, auf denen ich getestet habe. Wenn Sie den Wert auf 0,01 setzen, landet der Ball viel früher. Ich bin mir nicht sicher, wie ich damit umgehen soll, aber es ist definitiv ein Anfang.
Nolonar
2
@ Nolonar: Sie betreten jetzt den unterhaltsamen Bereich der Diskrepanzen bei der numerischen Integration. :) Größere Integrationsschritte führen zu größeren Ungenauigkeiten. Kleine Integrationsschritte erfordern mehr Schritte und damit mehr Rechenleistung, um das Endergebnis zu berechnen. Eine Physik-Engine kann schnell, genau oder für allgemeine Zwecke verwendet werden: Wählen Sie zwei aus. :)
Sean Middleditch

Antworten:

7

Ich glaube nicht, dass Sie etwas falsch machen. Der Grund dafür, dass das Ergebnis anders ist, ist, dass die Unity-Physik-Engine FixedUpdate je nach Hardware- und Systemlast unterschiedlich schnell aufruft. Viele physikalische Berechnungen verwenden den Zeitschritt bei jedem Aufruf, um das Ergebnis zu glätten. Die Antwort finden Sie hier: http://answers.unity3d.com/questions/11839/making-fixedupdate-fixed-timestep-independent.html

Tartle-Assistent
quelle
3
Ja, das Problem war in der Tat, dass Unity nicht mit der gleichen Geschwindigkeit aktualisiert wurde. Um dies zu beheben, ging ich zu Bearbeiten > Projekteinstellungen > Zeit . Dort musste ich " Maximum Allowed Timestep " auf den gleichen Wert wie " Fixed Timestep " setzen, um sicherzustellen, dass der Zeitschritt unabhängig von der Systemlast immer gleich ist. Jetzt zeigen alle meine Maschinen immer das gleiche Verhalten. Vielen Dank.
Nolonar