Raycast to moving colliders has no delay now?

From the official documentation of RayCast and SphereCast:

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.

@Edy
Thanks for your reply.

I go to unity archive page to download Unity 4.6 and 4.5, then, re-do my tests again.
I found the results are still correct.

Thus, I am wondering if my test script is designed appropriately to reproduce this physic issue?

Oh! This is really interesting.

Maybe the pose of the static colliders is feed to the physics engine at each change, but the issue is related to rigid bodies?

Try this:

  • Add a Rigidbody to the wall
  • Mark it as kinematic.
  • First test: move the wall using transform.position, as you’re doing now.
  • Second test: move the wall using rigidbody.MovePosition.
1 Like

@Edy

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.
2715055--192650--raycast_delay.png

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.

1 Like

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.

I actually tried your idea before.
Please see the code and my comments (choice-2-1 and choice-2-2) below.

    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
//        col.enabled = false;
//        col.enabled = true;

        //choice-2-1
        //nothing changes.
        //this state below seems ignored by unity smartly.
//        wall.position = wall.position;

        //choice-2-2
        //this state below cancels/overwrites rb.MovePosition().
        //just like last rb.MovePosition() never happens before.
//        wall.position += new Vector3(0, 0, 0.0001f);

        RayCast(30.0f);
    }

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?

https://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html

https://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html

But… in our tests for kinematic Rigidbody, transform.position=newPosition has different result compared to rigidbody.MovePosition(newPosition).

@hippocoder

I add the reference for this here.

1 Like