I'm making a game in which AI players throw a ball.
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?