4
votes

I have a very weird issue in my 2D Unity game, which I was able to reduce to the following core problem/minimal reproducing test case. Follow these steps to reproduce (Unity 5.1.1f1):

  • Create a player object (Cube) at location (0,0,0).
  • Remove the BoxCollider Component.
  • Attach the following C# script, Unity will automatically add the required Components and thereby make it a Rigidbody Collider.
  • Set the isKinematic flag.
  • Add another Cube to the scene at location (2,0,0).
  • Remove the BoxCollider Component and add a BoxCollider2D. This makes this cube a static Collider.
  • Set the isTrigger flag.
  • Run the scene.

Expected behavior:
The player cube accelerates towards the other cube and stops moving once it touches it.

Observed behavior:
The player cube accelerates towards the other cube, then continues moving at constant speed.

Additional implementation details:
I originally moved all objects by translating their transform and didn't use Rigidbodies at all because I didn't need collision detection. Now I do, so I want Rigidbodies. I dived into online resources and found out I'm supposed to use rigidbody.MovePosition() rather than transform.Translate() or transform.position. I changed my script, and the above error appeared.
Going back to transform.position fixes the issue, but that's not a good solution, as it involves bad practice which, according to what I read, produces significant CPU loads.

Failed attempts to solve:

  • Switching to Update() and Time.deltaTime didn't make any difference.
  • I tried not returning in the Update() and instead simply resetting the timestep to 0 while stop is set. No change.
  • Fiddling with the Inspector and doing stuff like freezing position on the rigidbody or setting the player objects to also be a trigger had no effect at all. Changing anything on the Rigidbody component while the game is running (after the collision) makes the cube stop immediately. Literally anything, even setting its mass to 0.
  • I also tried setting velocity to 0, resulting in no change. Which does make sense since Update() is skipped entirely (I also checked that with a Debug.Log() by the way).

So at this point, I'm down to barely 30 lines of code and I still have no idea what's causing this. Since the involved objects are a static Trigger collider and a kinematic rigidbody collider, both with no physics materials, there should be nothing that makes this thing move once the flag is set. But it does move.


SimpleController2D.cs

using UnityEngine;
using System.Collections;

[RequireComponent (typeof (BoxCollider2D), typeof (Rigidbody2D))]
public class SimpleController2D : MonoBehaviour {

    public Vector3 velocity = Vector3.zero;

    private Transform thisTransform;
    private Rigidbody2D thisRigidbody;

    public bool stop = false;

    void Awake () {
        thisTransform = GetComponent<Transform> ();
        thisRigidbody = GetComponent<Rigidbody2D> ();
    }

    void FixedUpdate() {
        float timestep = Time.fixedDeltaTime; // temporarily stored for ease of access
        if (stop) {
            return; // freeze on hit
        }

        velocity.x += timestep; // accelerate
        /* add a second slash (/) to toggle between transform and rigidbody
        thisTransform.position += velocity * timestep; /*/
        thisRigidbody.MovePosition ((Vector3)thisRigidbody.position + velocity*timestep); //*/
    }

    void OnTriggerEnter2D(Collider2D col) {
        stop = true;
    }
}
1
"The player cube falls onto the lower cube, then slowly drifts upwards...I'm using a self-made physics engine ... It also works, unlike Unity's own physics engine, which kept doing weird stuff" - that's a pretty bold statement considering your "engine" is contra to Newton's Laws and seems to have an implementation of anti-gravity. My advice would be to go back and find out what you did wrong with Unity Physics considering quite a few people are using it without issueMickyD
It worked as long as I used static Colliders. Since I introduced Rigidbodies, the physics engine messes up stuff. The movement is also comparable to the one happening when a Collider collides with itself using the Unity physics engine. But as you can see, there's nothing like that going on. I included the entire relevant code (and also all of the alternative engine. Please feel free to point me to the line on the provided code that "is contra to Newton's Laws"). The engine is perfectly fine, the issue is somewhere in this code.scenia
"Please feel free to point me to the line on the provided code that "is contra to Newton's Laws")." - I don't need to. It was by your own admission of "The player cube falls onto the lower cube, then slowly drifts upwards...I'm handling gravity and collisions the way you can see in the code". Did I misinterpret? Wishing you wellMickyD
You did. Setting busy = true disables the entire engine. At that point, the object is supposed to be entirely immobile because it's affected by nothing. If there was something wrong with my engine, the cube would misbehave while affected by it, but it starts misbehaving as soon as it turns the engine off and, presumably, Unity's engine takes over. As I said, my entire engine is in this code and as you can easily see, everything happens in Update(), which returns right at the start while busy is set. Whatever happens after the collsision is (correctly) detected is caused by Unity.scenia
I was able to reduce the test case to a simpler version. It doesn't change direction any more, but the core issue of the kinematic rigidbody moving without anyone telling it to remains.scenia

1 Answers

3
votes

Solution

This is a bug in Unity 5.1.1f1 and was fixed in the patch release 5.1.1p2 and later.

Get it here: http://unity3d.com/unity/qa/patch-releases?version=5.1

What happened?

You can even reduce the problem to a single MovePosition call. MovePosition uses the physics engine to move the object. Therefore Unity calculates the velocity necessary to reach the target position within the next physics update. Version 5.1.1f1 fails to reset the velocity to zero after reaching the position, so the object will just continue moving with the calculated velocity.