0
\$\begingroup\$

I've got a basic car-based game I'm starting in Unity. Currently the scene has only a floor, a car (player-controlled), and one building.

Bumping into the building at low to medium speeds works as expected. When getting the car up to max speed and smashing into the building, though, weird results happen.

Originally, the car was being pushed through the floor and falling off the map. I changed the floor from a plane to a cube that was stretched to be broad and flat (scale 2 height), and that helped. Now the car seems to clip right through the building at top speed, sometimes flipping itself into the air when it emerges.

So what gives?

The car and building are both imported .fbx models and both have box colliders stretched to the outer bounds of the models. The car has a rigid body with gravity. The building used to have a rigid body, but I removed it once I realized it was making the building fly into the air on impact (which was definitely not what I wanted).

Here are the relevant parts of the movement code:

  bool key_up = false;
  float top_speed = 1f;
  float current_speed = 0f;
  float acceleration = .01f;

  void Update ()
    {
    if (Input.GetKeyDown (KeyCode.UpArrow) && !key_up) key_up = true;
    if (Input.GetKeyUp (KeyCode.UpArrow) && key_up) key_up = false;

    if (key_up)
      {
      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;
      }

    transform.Translate (0f, 0f, current_speed);
    }

Update:

After switching the camera to a fixed position (rather than following the car), I've gotten better observations. The car will clip right through at any speed if I keep pushing forward. There's initial resistance from the collision detection and some stuttering from the car, but it always breaks through and then flips into the air on its way out.

I've added the following function to the car code:

void OnCollisionEnter (Collision collision_info)
  {
  current_speed = 0f;
  }

current_speed is what's being used to translate the car forward each frame. This has slowed the bug down, but the car always eventually pushes through if I hold down the gas long enough. It looks as if Unity's collision detection isn't moving the car back far enough on each frame that the car is colliding. Must I handle that portion on my own?

\$\endgroup\$
  • 1
    \$\begingroup\$ You may want to check out this video on common physics engine quirks and fixes. Some of the symptoms you describe sound like what Foddy describes in the section "Unexpected Behaviours." To diagnose your specific issues, it would help to see how you've implemented your car movement, and configured the car and obstacle colliders / bodies in your scene. \$\endgroup\$ – DMGregory Nov 24 '17 at 1:20
  • \$\begingroup\$ @DMGregory, This video is definitely helpful with some tricks to overcome the jitter and clipping problems. Tunneling is a normal problem even with 2D games, but the building is over twice as thick as the car's length and it's not moving THAT fast, so I have a hard time believing that's what's happening here. At any rate, thanks. \$\endgroup\$ – Nightmare Games Nov 24 '17 at 3:29
  • \$\begingroup\$ One common error in Unity is to move a physics object with transform.position rather than the Rigidbody. This can teleport it through the skin of a collider and lead to assorted physics glitches, so if you're doing that it could be the source of the tunneling even at low speeds. This is one reason I recommended showing more of your movement code. \$\endgroup\$ – DMGregory Nov 24 '17 at 3:32
  • \$\begingroup\$ I haven't been using transform.position, but I have been using transform.Translate(). I'm not sure if that's also villainous or not... \$\endgroup\$ – Nightmare Games Nov 24 '17 at 3:44
  • \$\begingroup\$ Not villainous, just out of the physics engine's control, so it's treated like a teleport and can cause some non-physical side-effects as a result. \$\endgroup\$ – DMGregory Nov 24 '17 at 3:45
0
\$\begingroup\$

The main problem I was having was the transform.Translate function I was using to move the car. Doing it that way caused the physics engine to be slightly behind each frame, basically making it play catch up with my code. I changed this to move the car's rigid body directly and not the car itself. Specifically, I used Rigidbody.MovePosition().

I should note that after changing the movement code, I had to apply a frictionless physic material to the car's box collider to get it to move across the floor correctly.

I also put my movement code into UpdateFixed() rather than Update(), which also helps the physics engine stay up-to-date.

With these three fixes, I no longer needed any of the other band-aid solutions I'd added, including changing the fixed timestep, messing with the box collider sizes, or trying to use continuous collision detection.

Here's the new movement code:

public class car_input : MonoBehaviour
  {
  bool key_up = false;
  bool key_down = false;
  bool key_left = false;
  bool key_right = false;

  float top_speed = 1f;
  float current_speed = 0f;
  float acceleration = .02f;

  Rigidbody body;

  //////////////////////////////////////////////////

  void Start ()
    {
    body = GetComponent<Rigidbody> ();
    }

  //////////////////////////////////////////////////

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

    float turning_rotation = current_speed * 2.5f;
    if (key_left) transform.Rotate (0f, -turning_rotation, 0f);
    if (key_right) transform.Rotate (0f, turning_rotation, 0f);

    Vector3 movement = transform.forward * current_speed;
    body.MovePosition (body.position + movement);
    }
  }
\$\endgroup\$

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.