Raycast not colliding with object moved using transform.position

When my turret shoots it first checks to see if the players future position is not obstructed by an object.

It does this by first calculating the future position of the player by taking its current velocity, multiplying it by the time it would take the projectile to reach it, then adding that to the players current position.

Vector3 predictedPosition = target.GetComponent<Velocity>().GetFuturePosition(leadingAccuracy, timeToReachTargetIdeal);

My “shootcheck” object is then placed at this predicted position.

shootCheck.transform.SetPositionAndRotation(predictedPosition, Quaternion.identity);

A raycast is then shot from the projectile origin to the predicted position.

if (Physics.Raycast(origin.position, predictedDeltaDirection, out hit, predictedDeltaMagnitude, shootCheckLayer)) {
            Debug.Log(hit.transform.name);

            if (hit.transform.CompareTag("ShootCheck")) {
                Debug.DrawRay(origin.position, predictedDeltaDirection * hit.distance, Color.green, 10);
                Debug.Log("the path is CLEAR");

                //Shoot projectile
                GameObject o = Instantiate(projectile, origin.position, Quaternion.identity);
                //Set velocity of the instantiated projectile object
                o.GetComponent<Rigidbody>().velocity = GetPredictiveProjectileVelocity(origin, target, leadingAccuracy, projectileVelocityInitial, spread);
            }
            else {
                Debug.DrawRay(origin.position, predictedDeltaDirection * hit.distance, Color.red, 10);
                Debug.Log("the path Is NOT clear");
            }

        }
        else {
            Debug.DrawRay(origin.position, predictedDeltaDirection * predictedDeltaMagnitude, Color.blue, 10);
            Debug.Log("The raycast missed the object");
        }

Now the problem is that the raycast isn’t always detecting a collision with this “shootCheck” object.

Strangely it works when the player is moving parallel away to the direction of the ray but NOT towards.

Gif of walking towards cannon

Gif of walking away from cannon

Gif of walking and running

When strafing it almost never hits when the firerate of the gun is low.

When strafing it hits like 20% of the time when the firerate is high.

Gif of strafing with high firerate.

Because changing the Transform ONLY changes the Transform. Unity components don’t know about this change when you change it. Even renderers only read it when they render. Physics is no different, it only sees them when the simulation runs.

The whole point of the Rigidbody(2D) is to perform a physics simulation then write to the Transform. The Rigidbody(2D) is the authority on the pose. By changing the Transform you’re bypassing that. Physics will sync your changes to Transform back to the Rigidbody(2D) when the simulation runs.

Use the Rigidbody(2D) API to cause changes. Physics runs (by default) during the fixed-update so doesn’t run per-frame.

If you must cause an instant change in position/rotation then again, use the Rigidbody(2D) API so Rigidbody.position/rotation.

1 Like

A related question, how interpolation is handled exactly? My understanding is that in each Update() the rigidbody interpolates between the previous two physics poses based in the delta time from the latest fixedTime, then writes the result to the transform.

Yes, each update the Transform is modified to be at a pose in-between the last Rigidbody(2D) pose to the current Rigidbody(2D) pose (from the last simulation step). The Rigidbody(2D) itself or any physics isn’t modified so this process is completely outside the physics engine(s) and only a Unity thing affecting Transform poses only; nothing in the physics simulation changes.

This is done with a simple lerp (position) and slerp (rotation).

It’s why Interpolation is a “historic” movement whereas extrapolation is a “future” (speculative) movement (from current Rigidbody(2D) + current velocity to a future pose).

In terms of calculating the “fraction” we’re at in-between these two poses; it keeps the last simulation time and simulation time-delta. It then uses that alongside the current dynamic time to calculate an interpolated or extrapolated pose. We do all this off the main-thread so as to reduce its impact (go wide) but also unfortunately have to block until it’s complete. For a small number of interpolated bodies this ends up running on the main-thread anyway.

1 Like

I had a feeling it was something to do with the physics step but you made it so clear for me to understand. Thank you so much!

Using your information, I added a rigidbody to the “ShootCheck” object. Disabled gravity from it. And instead of using transform.position to move it, I used rigidbody.position to move it.

shootCheckRigidbody.position = predictedPosition;

Gif of successful shootcheck hit detection
https://gyazo.com/8ce1c480fe839eb191dee7ea83ae4dc6

1 Like