1
\$\begingroup\$

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:

car inspector

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.

\$\endgroup\$
  • \$\begingroup\$ I'm not sure why you have the key_down, key_left, etc. variables, Input.GetKeyDown does the exact same thing. \$\endgroup\$ – Bálint Feb 23 '18 at 2:55
  • \$\begingroup\$ @Bálint That's how I've always done it in other game engines. I'm still figuring out Unity. \$\endgroup\$ – Nightmare Games Feb 23 '18 at 2:59
  • 1
    \$\begingroup\$ For troubleshooting physics issues, I recommend checking out Bennett Foddy's GDC 2015 talk on understanding & mitigating common simulation errors. Can you tell us a bit more about the "several alternative" solutions you've tried? Without more details, it's hard to know whether an answer we'd propose is one you've already tried and rejected. \$\endgroup\$ – DMGregory Feb 23 '18 at 3:00
  • \$\begingroup\$ Just curious, did you try to clamp velocity and angluarVelocity? \$\endgroup\$ – Candid Moon _Max_ Jul 13 '18 at 15:56
  • \$\begingroup\$ @CandidMoon: The velocity never exceeded the limits set by top_speed. Doing a straight clamp on a value never seems to work for me in Unity, although my current code (now months later) does manually limit both angularVelocity and rotations. Really, it was the gravity and/or scale of my models that caused the problem. \$\endgroup\$ – Nightmare Games Jul 16 '18 at 21:48
0
\$\begingroup\$

It was the gravity.

Based on the video DMGregory linked to, I realized my game has "floaty" physics, as the speaker describes it.

I did some research and found out Unity lets the user specify the amount of gravity in the world under Edit -> Project Settings -> Physics.

I changed mine from -9.81 to -30 and now the game runs great. No more flying cars. Maybe my city was modeled on a much smaller scale than what Unity was expecting. It makes me wonder what the correct Unity/Blender units-to-feet ratio would be for a game to feel realistic with the default gravity.

I also found a bug in my physics code related to a function I wrote to flip the car with a key press if it's been turned upside-down or on its side. I wasn't reducing the vertical velocity in certain situations, resulting in a lot of excess bounciness when using that function.

\$\endgroup\$
  • 3
    \$\begingroup\$ The correct scale for realistic gravity at the default settings is 1 unit = 1 meter. Then the default gravity represents exactly the 9.81 m/s2 average acceleration due to gravity on the Earth's surface. \$\endgroup\$ – DMGregory Feb 23 '18 at 11:47
  • 1
    \$\begingroup\$ One other thing you may want to consider is moving the object with velocity or AddForce instead of MovePosition — this gives the physics engine a bit more leeway to resolve glancing collisions with a nudge to the velocity, rather than moving to the exact position specified even if it means interpenetrating another collider and taking a large restitution impulse from it. \$\endgroup\$ – DMGregory Feb 23 '18 at 12:28

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.