If you move colliders from scripting or by animation, there needs to be at least one FixedUpdate executed so that the physics library can update it’s data structures, before a RayCast/SphereCast will hit the collider at it’s new position.
After I read that above, I did some tests to see if I can RayCast against moving colliders correctly.
Here is my test code below:
using UnityEngine;
using System.Collections;
public class RayCastTest : MonoBehaviour {
public Transform wall;
void RayCast(float duration = 0.0f)
{
RaycastHit hitInfo;
if(Physics.Raycast(transform.position, transform.forward, out hitInfo, 40.0f))
{
Debug.DrawRay(hitInfo.point, hitInfo.normal * 20.0f, Color.white, duration);
}
}
void RayCast2MovingWall()
{
wall.position += new Vector3(0, 0, 1.0f);
RayCast(30.0f);
wall.position += new Vector3(0, 0, 1.0f);
RayCast(30.0f);
wall.position += new Vector3(0, 0, 1.0f);
RayCast(30.0f);
wall.position += new Vector3(0, 0, 1.0f);
RayCast(30.0f);
}
void Start()
{
RayCast2MovingWall();
}
void Update () {
Debug.DrawRay(transform.position, transform.forward * 20.0f, Color.blue);
}
}
What I try to do above is that repeatedly move a collider and RayCast to it within one frame.
But I find the RayCast results (postions of hit points) are always correct like this picture below.
Is my method to produce the issue correct?
I did found other people’s posts reporting different result.
I am using Unity Personal 5.3.4f1.
Raycast against moving colliders has no delay now?
No need to wait at least one FixedUpdate now? Updating collider’s positions effects immediately in physic engine now?
That post is most likely related to Unity 4. Unity 5 was in beta stage at that time (January 2015) if I recall correctly.
The physics engine was updated from PhysX 2.8 to PhysX 3.3 in Unity 5. One area of major improvements was the scene queries. As seen in your test, it seems that this part has been greatly improved and works as expected now. The documentation just needs an update on this.
Thank you very much for your suggestion.
I followed your steps. Your “Second test” produces the problem: Raycast hits the old position of collider.
I tested on both Unity5.3 and Unity4.5. Both versions show exact the same result.
If I use WaitForFixedUpdate with kinematic Rigidbody in test, there is no problem.
The snippet code below shows the updated testing script with one Raycast only (simpler).
using UnityEngine;
using System.Collections;
public class RayCastTest : MonoBehaviour {
public Transform wall;
void RayCast(float duration = 0.0f)
{
RaycastHit hitInfo;
if(Physics.Raycast(transform.position, transform.forward, out hitInfo, 40.0f))
{
Debug.DrawRay(hitInfo.point, hitInfo.normal * 20.0f, Color.white, duration);
}
}
void RayCast2MovingWall()
{
wall.position += new Vector3(0, 0, 1.0f);
RayCast(30.0f);
}
void RayCast2MovingWallwRbMove()
{
//using rb.position or wall.position causes the same result
// rb.MovePosition (rb.position + new Vector3 (0, 0, 1.0f));
rb.MovePosition (wall.position + new Vector3 (0, 0, 1.0f));
//seems not useful to avoid the problem
// col.enabled = false;
// col.enabled = true;
RayCast(30.0f);
}
public Rigidbody rb;
public Collider col;
void Awake()
{
rb = wall.GetComponent<Rigidbody> ();
col = wall.GetComponent<Collider> ();
}
IEnumerator RayTestCoroutine() {
rb.MovePosition (wall.position + new Vector3 (0, 0, 1.0f));
yield return new WaitForFixedUpdate();
RayCast(30.0f);
}
void Start()
{
//choice-1
// RayCast2MovingWall();
//choice-2
//only this choice produces the problem if rd is set as kinematic
RayCast2MovingWallwRbMove();
//choice-3
// StartCoroutine(RayTestCoroutine());
}
void Update () {
Debug.DrawRay(transform.position, transform.forward * 20.0f, Color.blue);
}
}
The picture below shows the problematic test case (choice-2).
The Raycast hits the old position of collider of wall.
Notice that if we set a Rigidbody as kinematic, we suppose should not move it with Rigidbody.MovePosition?
Thus, only the wrong usage case can produce the delay problem?
In addition, I guess if we set transform.position property, Unity Physic Engine always gets notified immediately, which causes corresponding collider state updating immediately?
When a rigidbody is kinematic then the physics doesn’t affect it. It must be moved and rotated via scripting. The correct method is using MovePosition and MoveRotation:
Affects other rigidbodies
Collisions are detected and callbacks are triggered
Velocity and angular velocity are calculated according to the movement
On the other side, modifying the transform means just “teleporting” the rigidbody to the new pose. Nothing of the above happens, except collisions if triggered by other rigidbodies.
What is a surprise to me is that modifying the transform also feeds the new pose to the physics engine immediately.
The rigidbody has the Interpolate option. With this option, the transform is modified on each Update frame, so the movement looks smooth (otherwise it moves at FixedUpdate rate). However, the Interpolate option does not feed the pose to the physics engine on each Update. I thought that was by design, as maybe the overhead of moving the physics pose on each Update might be significant. But as modifying the transform just works, I wonder if this is just a bug or a potential improvement. Definitely, resolving it would fix some issues I’m experiencing about raycasts on fast moving objects.
You can try this:
Move the kinematic rigidbody via MovePosition
Throw a Raycast on each Update frame
Repeat with Interpolate enabled
You’ll see the same exact result in both cases: the hit point is correct only after each FixedUpdate. But it will be a perfect repro case for submitting to Unity devs. I’ve had recently a conversation with @yant regarding this issue, and I think this case will help him to see the point.
I’ve just had an idea for the test with Interpolate enabled: What if we also modify the transform on each Update? Just set wall.position = wall.position. Hopefully that will trigger the pose to be fed to the physics system.
I redo the test with Interpolate enabled. As you said, I get the same result.
I thought the right way to move kinematic rigidbody is just teleporting it by modifying its transform.
I’ve learned something new from your last reply. This is really interesting, since many developers may have the same thought as I have.
There’s probably a drawback though - setting transform directly is probably lower performance, as it would have to update it’s structures right away, and then again at fixed update.
Moving static colliders has no penalty as for the update to PhysX 3.3 (it was pretty expensive in Unity 4 / PhysX 2.8). Of course, feeding the pose to the physics scene on each Update will be less efficient than doing it on the physics time step. Still, I think it’s worth exploring the possibility of the interpolated rigidbodies modifying the physics pose on each update.
Yes, I’ve also tested this case. The transform can be overridden sporadically, but the physics pose remains controlled by the rigidbody.
I reviewed official documentation about kinematic Rigidbody again.
It looks like the suggested way to move kinematic Rigidbody is still modifying its transform directly?