I'm building a car driving game in Unity 2017.3.1f1.
The car movement works fine over a flat surface and even over gently rolling terrain. It's a city environment, and there are lots of small lips and angles where the street meets the sidewalk, where terrain ends, and around ramps and other features.
When the car hits one of these small seams in the ground, 60% of the time it flies up in the air over the buildings, rotating like crazy. It often flies off the game world. When going off a ramp, sometimes the car will continue driving forward and rotating up on the x-axis in mid-air, as if it's going through an invisible loop-the-loop.
I'm not sure what's causing the extreme physics reactions.
Here's the car object:
And here's the car's movement code:
void Update ()
{
if (Input.GetKeyDown (KeyCode.UpArrow) && !key_up) key_up = true;
if (Input.GetKeyUp (KeyCode.UpArrow) && key_up) key_up = false;
if (Input.GetKeyDown (KeyCode.DownArrow) && !key_down) key_down = true;
if (Input.GetKeyUp (KeyCode.DownArrow) && key_down) key_down = false;
if (Input.GetKeyDown (KeyCode.LeftArrow) && !key_left) key_left = true;
if (Input.GetKeyUp (KeyCode.LeftArrow) && key_left) key_left = false;
if (Input.GetKeyDown (KeyCode.RightArrow) && !key_right) key_right = true;
if (Input.GetKeyUp (KeyCode.RightArrow) && key_right) key_right = false;
}
//////////////////////////////////////////////////
void FixedUpdate ()
{
if (on_ground)
{
// driving
if (key_up)
{
if (current_speed < top_speed) current_speed += acceleration;
}
else if (key_down)
{
if (current_speed > -top_speed) current_speed -= acceleration;
}
else
{
if (current_speed > -.1f && current_speed < .1f) current_speed = 0f;
if (current_speed != 0f) current_speed = current_speed * 0.9f;
}
// turning
if (key_left) turning_rotation = current_speed * 2.5f * -1f;
if (key_right) turning_rotation = current_speed * 2.5f;
}
// rotation
transform.Rotate (0f, turning_rotation, 0f);
forward_movement = transform.forward * current_speed;
body.MovePosition (body.position + forward_movement);
// reset values
turning_rotation = 0f;
}
//////////////////////////////////////////////////
void OnCollisionStay ()
{
update_on_ground ();
}
//////////////////////////////////////////////////
void OnCollisionExit ()
{
update_on_ground ();
}
//////////////////////////////////////////////////
void update_on_ground ()
{
// check if car is grounded
Vector3 ray_start = transform.position + new Vector3 (0f, 1f, 0f);
if (Physics.Raycast (ray_start, Vector3.down, 1.1f))
{
if (!on_ground) on_ground = true;
}
else if (on_ground) on_ground = false;
}
}
Any ideas are appreciated.
NOTES:
There was a ton of extra code in here attempting various fixes, including trying to manually restrict rotations on the z and x axes, to reduce velocity while airborne, to use several alternative types of movement code, and to increase the car's mass to a ridiculous amount. None of them ever worked, so I've removed them here.
By request, other solutions I've tried:
Changing the car's mass seems to have no effect on anything.
Trying to clamp the car's rotations led down an enormous rabbit hole that would require a separate question to resolve, but the short version is that Euler angles don't seem to work correctly and I don't understand quaternions (yet).
Gradually reducing the car's velocity in mid-air helped slightly in keeping it airborne shorter, but not enough to stop it from launching upward like a rocket in the first place or bouncing like crazy when it hits the ground.
I've also messed around with using transform.translate() rather than RigidBody.MovePosition(), but this doesn't seem to affect the bounciness, gravity, or rotations of the car. In fact, it sometimes makes the collisions glitchier.
The last thing I tried was using the car's direction and velocity to figure out its global, separate x,y,z velocities and move it globally based on only the x and z. My thought process was that if the car is going off a ramp, then the nose is pointed up, which means it's technically still moving in that direction relative to itself (transform.forward) when it leaves the ramp, even though gravity is pushing it down on the global y axis. I got this working correctly, but it didn't have a noticeable impact on the physics.
Freezing the X and Z rotations in the car's Unity inspector actually does work and addresses all the physics problems. However, it looks pretty damn silly when the car goes up a ramp or over terrain and stays perfectly level no matter what.
All the world geometry is currently just built from Unity primitives.
velocity
andangluarVelocity
? \$\endgroup\$