Projectiles seem extrapolated (overshooting target) while their transform position is 1-2 updates "behind"

I noticed that my projectiles are overshooting the target, eg they “come out the other side”. After investigating, I found that the actual positions of the projectile and impact point are correct - it’s just the visualization of the projectile (a sphere) that seems ahead of the logical position by one or two steps.

See this image, yellow is the projectile, cubes are tracers I spawn every update, taken in the editor with playmode paused:

Assuming that pausing does not occur in-between rendering, this image indicates that the projectile position is visually not one but two steps ahead of its logical update - or maybe one if we assume that the cube in that gap has been instantiated but will not be rendered until the next frame. But definitely ahead.

The effect is quite noticable in the video, notice how the yellow projectile pops up to the left of the target but the tracers all stack up to the right as it should be:

The projectile is a simple sphere, no collider, and no Rigidbody:

The projectile is moved manually in FixedUpdate (I also tried Update and LateUpdate):

private void FixedUpdate()
{
	var currentTime = Time.time;
	var deltaTime = Time.deltaTime;
	RaycastHit hit = default;

	var projectiles = m_Spawner.Projectiles;
	var projectileCount = projectiles.Count;
	for (var i = projectileCount - 1; i >= 0; i--)
	{
		var projectile = projectiles[i];
		var projectileTransform = projectile.Transform;
		var runtimeData = projectile.RuntimeData;
		var isEndOfLife = currentTime >= runtimeData.TimeToDie;

		if (isEndOfLife)
			m_Spawner.DespawnProjectile(i);
		else
		{
			var forward = projectileTransform.forward;
			var lastPosition = projectileTransform.position;
			var distanceTravelled = projectile.Data.Speed * deltaTime;

			var travelRay = new Ray(lastPosition, forward);
			var layerMask = projectile.Data.CollidesWithLayers;
			var triggerInteraction = projectile.Data.TriggerInteraction;
			if (PhysicsExt.ClosestHit(travelRay, out hit, distanceTravelled, layerMask, triggerInteraction))
			{
				// use less than full distance to keep impact fx a little off-surface (prevent z-fighting)
				var hitPosition = lastPosition + forward * (hit.distance * 0.99f);
				projectileTransform.position = hitPosition;
				m_Spawner.DespawnProjectile(i);
				m_Spawner.SpawnEffect(projectile.Data.ImpactPrefab, hitPosition, Quaternion.LookRotation(hit.normal));

				SpawnTracer(hitPosition, projectileTransform.rotation, 2f);
			}
			else
			{
				var newPosition = lastPosition + forward * distanceTravelled;
				projectileTransform.position = newPosition;

				SpawnTracer(newPosition, projectileTransform.rotation);
			}
		}
	}
}

For good measure I even instantiated a tracer cube every Update() too.

I also tried disabling post-processing and a low-end quality setting to see if some clever rendering feature might try to smooth out fast-moving things. I also tried limiting the framerate to 30 and vsync on/off.

Note: yes, this is a networked sample but a) the projectiles aren’t network synchronized and b) the effect is clearly visible locally.

What could be causing such a behaviour?

Instead of doing the collision check from the last position you should do it from the current position. So you’re checking a step ahead.

I did that too, thinking that would be the fix, but it still overshoots. I reverted it back to use the previous position to better see the effect. It also doesn’t affect the image above because if there’s no collision, it doesn’t matter whether to use the current or last position for the raycast.

With look-ahead collision checks it seems the projectile is still one frame ahead, with look-behind it’s probably two steps. But clearly ahead either way, and visible on the other side of the target.

I’m out of ideas what the hell is going on. :slightly_frowning_face:
I keep thinking Unity tries to be clever and renders the projectile ahead. I also verified frameCount to see that when I set the position and when SetActive(false) runs (the moment the object is released back into the pool) within the same frame. Logically, the projectile is never in a position ahead of its travel direction but that’s where I can see it.

I’m still thinking maybe it’s interpolation to 120 Hz while update runs at 60 or 50 but tried to rule that out with vsync and targetFramerate - but maybe not correctly.

Interpolation only works for rigidbodies and so I was a little surprised to see you running your projectiles in FixedUpdate.

Could your DespawnProjectile method have a delay on it?. The projectile could be being moved again in the next frame despite the projectile being dead. And because Raycast doesn’t detect colliders that the ray starts within the projectile is briefly being allowed to pass through the target. On line 31 try moving the bullet off screen instead of moving it to the hit point.

I moved it to FixedUpdate to provide an even motion, with Update the tracer cubes are spaced unevenly as expected. Either way, I tried them all.

I checked that but the moment I register a hit and the time the OnDisable runs is in the same frame according to Time.frameCount.

I gave that a shot but no difference.

Made a build to see if this is just an editor glitch but no, I also see the bullet passing through in builds.