7
\$\begingroup\$

I'm making a game in which AI players throw a ball.

Screenshot

In the screenshot, you can see the arena which is separated by a net in the middle. Each side of the arena has 6 players belonging to one of 2 teams. Right now, one of the players on the right side has the ball (the top-right player). He is going to throw the ball to the left side.

According to my code, the ball should always land on the same place, because the player always applies the same force to the ball. However, I have noticed that the ball doesn't always land in the same place. I have marked where I've seen the ball land with several red circles.

Interestingly, on my main PC, the ball almost always lands on the right-most circle. It rarely lands on the other circles, although that does happen. On 3 other PCs I've tested this game on, however, the ball most often lands on the legs of the top-left player, sometimes on his head or behind him, but never on the right-most circle (in front of him).

My game relies heavily on physics. The AI is supposed to calculate how to throw the ball to get it to land on a very specific location, as well as guess where it'll land depending on the ball's position and movements. If the physics don't behave consistently on all machines all the time, how is the AI supposed to calculate all of this accurately?

Here is the code in charge of throwing:

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);
}

As you can see, the ball is always thrown "forward" (facing the net) at a 45° upward angle, using a normalized vector and a constant speed (20f).

Before each throw, the position of the players is reset to the exact same location (and rotation) they are seen on the screenshot.

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

In this case, playerRotation is either 90f or -90f depending on which side the player is on, startingposition is their "horizontal" position (relative to the net), and posOffset is their "vertical" position.

The ball has its velocity and angular velocity reset, but not its rotation (since it's a sphere, its rotation shouldn't influence anything). Its position is also set to a fixed location relative to whichever player has to serve the ball.

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

Here, rb is the ball's RigidBody.

Finally, I am not influencing the ball's movement in any way. I simply apply a force to it when serving, which happens only once. 3 seconds after the ball lands, the match is reset and everything repeats.

The flow is basically like this (pseudocode):

Start()

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

// repeat starting with ResetCourt()

I really need the ball's trajectory to be reproducible every single time on every possible machine. Obviously I'm doing something wrong here, but what?

\$\endgroup\$
  • 2
    \$\begingroup\$ My guess; the physic engine doesnt have a fixed time step leading to slightly different behaviour depending on how fast your cpu is running. Could your physics time step have become linked to your rendering time step? \$\endgroup\$ – Richard Tingle Nov 17 '16 at 22:46
  • \$\begingroup\$ @RichardTingle. I think you're on to something. I had a look at the Time settings of my project, and decided to play around with the Fixed Timestep. Increasing it from 0.02 (50 updates per second) to 0.04 made my main PC behave more like the 3 laptops I tested on. Setting it to 0.01 made the ball land much sooner. Not sure how to deal with it, but it's definitely a start. \$\endgroup\$ – Nolonar Nov 17 '16 at 22:58
  • 2
    \$\begingroup\$ @Nolonar: you're now entering about the fun realm of numerical integration discrepancies. :) Larger integration steps produce larger inaccuracies. Small integration steps require more steps and hence more processing power to calculate the final result. A physics engine can be fast, accurate, or general purpose: pick two. :) \$\endgroup\$ – Sean Middleditch Nov 17 '16 at 23:08
7
\$\begingroup\$

I don't think you are doing anything wrong, the reason the result is different is because the Unity physics engine is calling FixedUpdate at a different rate depending on hardware and system load. Many physics calculations use the time step on each call too smooth out the result. See the answer here: http://answers.unity3d.com/questions/11839/making-fixedupdate-fixed-timestep-independent.html

\$\endgroup\$
  • 3
    \$\begingroup\$ Yes, the problem was indeed that Unity did not update at the same rate. To fix this, I went to Edit > Project Settings > Time. There, I had to set "Maximum Allowed Timestep" to the same value as "Fixed Timestep" to ensure the timestep would always be the same, regardless of system load. Now, all my machines exhibit the same behavior all the time. Thanks. \$\endgroup\$ – Nolonar Nov 17 '16 at 23:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.