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 aBoxCollider2D
. 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()
andTime.deltaTime
didn't make any difference. - I tried not returning in the
Update()
and instead simply resetting thetimestep
to0
whilestop
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 sinceUpdate()
is skipped entirely (I also checked that with aDebug.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;
}
}
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 inUpdate()
, which returns right at the start whilebusy
is set. Whatever happens after the collsision is (correctly) detected is caused by Unity. – scenia