The Prediction to Interpolation switching feature has a noticeable jitter regardless of the TransitionDurationSeconds value. It seems like the projectile stops for few frames before the interpolation smoothing begins.
i am using netcode 1.2.1 and Unity 2023.2.20f1
Thanks in advance !
public struct PredictionSwitchingRequest : IComponentData
{
public Entity Target;
public float TransitionDurationSeconds;
}
/// <summary>
/// This system is responsible of Switching Predicted Ghosts from Predicted to Interpolated in dynamic way and waits for the ghost to be classified before managing the request.
/// </summary>
[BurstCompile]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
[UpdateBefore(typeof(GhostPredictionSwitchingSystem))]
public partial struct PredictionSwitchingRequestManagerSystem : ISystem
{
private EndSimulationEntityCommandBufferSystem.Singleton _endSimulationEntityCommandBufferSystem;
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<GhostPredictionSwitchingQueues>();
state.RequireForUpdate<EndSimulationEntityCommandBufferSystem.Singleton>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
this._endSimulationEntityCommandBufferSystem =
SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
var ghostPredictionSwitchingQueues = SystemAPI.GetSingletonRW<GhostPredictionSwitchingQueues>();
var interpolationSwitchingJob = new HandlePredictionToInterpolationSwitchingJob
{
ecbParallel = _endSimulationEntityCommandBufferSystem.CreateCommandBuffer(state.WorldUnmanaged)
.AsParallelWriter(),
convertToInterpolatedQueueParallelWriter = ghostPredictionSwitchingQueues.ValueRW.ConvertToInterpolatedQueue,
ghostInstanceLookup = state.GetComponentLookup<GhostInstance>(isReadOnly: true),
entityStorageInfoLookup = state.GetEntityStorageInfoLookup()
};
interpolationSwitchingJob.ScheduleParallel();
}
[BurstCompile]
public partial struct HandlePredictionToInterpolationSwitchingJob : IJobEntity
{
public NativeQueue<ConvertPredictionEntry>.ParallelWriter convertToInterpolatedQueueParallelWriter;
public EntityCommandBuffer.ParallelWriter ecbParallel;
[ReadOnly] public ComponentLookup<GhostInstance> ghostInstanceLookup;
[ReadOnly] public EntityStorageInfoLookup entityStorageInfoLookup;
private void Execute(Entity requestEntity, [ChunkIndexInQuery] int sortKey,
PredictionSwitchingRequest predictionSwitchingRequest)
{
//Target Entity Has been destroyed before its Classification
if (!entityStorageInfoLookup.Exists(predictionSwitchingRequest.Target))
{
//Destroy the Request
ecbParallel.DestroyEntity(sortKey, requestEntity);
return;
}
//Ghost still not Classified
if (ghostInstanceLookup[predictionSwitchingRequest.Target].ghostId == 0)
return;
//Add the Convert Prediction Switching Entry to the Queue
convertToInterpolatedQueueParallelWriter.Enqueue(new ConvertPredictionEntry
{
TargetEntity = predictionSwitchingRequest.Target,
TransitionDurationSeconds = predictionSwitchingRequest.TransitionDurationSeconds
});
//Destroy the Request
ecbParallel.DestroyEntity(sortKey, requestEntity);
}
}
}
Speaking about switching interpollation bug, there is a bug in the rotation logic that is causing a big jitter issue. Has been reported by another user as well.
But in this case, i suspect things are a little different,
Because you are predicting the entity spawn (for what I understood in this code):
the authoritative entity whence received is snapped and the predicted up to the current tick (GhostUpdate).
then classified and matched with a predicted spawned entity (GhostSpawnClassificationSystem)
then presented on screen (first rendering)
Then, one frame later, the prediction switching logic of yours should technically kick-in, and converting the entity to interpolated (and smoothing things out).
Now, the jitter can be created for multiple reasons here:
Either the re-simulation cause a different result
Either is no re-simulated correctly (custom code in the GhostUpdate?)
Either the entity was not matched correctly (for some reason) and the current predicted one destroyed
The logic itself (in term of queuing) seems correct at a first glance. Could you try to log some extra info about what happen to entity once it is matched and queued to be interpolated?
Also, could you please print in the SwitchPredictionSmoothingJob about what are the interpolation begin and end state?
Regarding the PredictionSwitchingRequestManagerSystem, it’s a temporary system that holds switching requests until the ghost is finally classified. This can be used for locally spawned ghosts like projectiles or area of effects…
The source of the problem has been identified :
The ProjectileMovementSystem is running in the PredictedFixedStepSimulationSystemGroup, and moving it to the PredictedSimulationSystemGroup resolved the jitter issue. It appears that Prediction Switch Smoothing does not support entities moving in the PredictedFixedStepSimulationSystemGroup.
Edit:
Is there any solution to keep some ghosts moving in the PredictedFixedStepSimulationSystemGroup and still benefit from the Prediction Switching feature?
May I ask, do you prefer predictionswitching rather than just fully predicting projectiles? Our game does not have more than ~5-10 projectiles (ARPG esque) at a single time so the cost isn’t too bad, but we have weird pop-in / resim location effect due to classification system, we’re unsure on what to do currently and I’m wondering if predictionswitching when classified would prevent this “popping” location resim/reset every now and then when classification occurs.
Sidenote: @CMarastoni some new classification sample with fully predicted gameplay (in a competitive context) would be very appreciated
in our case we prefer to use the Prediction Switching for both performance and minimize miss predictions.
however we still have a short term support for local prediction of Projectiles until their classification and switching completion.
[quote[
Is there any solution to keep some ghosts moving in the PredictedFixedStepSimulationSystemGroup and still benefit from the Prediction Switching feature?
[/quote[
Prediction Smoothing works on any entity that is switching prediction in between Interpolate ↔ Predicted. It does not matter if the systems that update them is inside the PredictedFixedStepSimulationSystemGroup.
The problem with PredictedFixedStepSimulationSystemGroup is that it is running a fixed time step, so it does not execute for partial ticks. And this can cause by itself some jitter.
Inside the PredictedFixedStepSimulationSystemGroup there is also physics running and physics also has its own interpolation (via write group as well).
I presume (I will verify) there is some missing filtering options on the PredictionSwitchingSmooting system that is preventing it to run in case the predicted/interpolated objects has physics components and the rigid body is using smoothing (either interpolated or predicted).
I believe we’re not discussing the same type of jitter. The issue is more like a sudden “pop,” where the projectile reverts to its initial position and starts the interpolation again from the beginning.
@CMarastoni
I even tried to interpolate the ghost in between the fixedsteps using the character controller package logic ( I changed it to be more generic instead of just interpolating characters ) but the result is the same. however when the ghost movement moves to the PredictedSimulationSystemGroup the stutter as shown in the video disappears.
We’ve successfully implemented the projectile interpolation switching feature and resolved the jitter issue. The jitter was caused by the ghost not having enough data to interpolate/extrapolate. Our workaround involves waiting a few ticks after the classification to prevent the ghost from starting with a hard jitter.
Currently, the netcode implementation of this feature instantly makes the ghost interpolated and starts interpolating/extrapolating between the current ghost position and the server position. However, this approach isn’t ideal for scenarios like projectiles switching mid-air. The extrapolation can negatively affect the required behavior, causing projectiles to pass through obstacles until the ghost destruction data is received from the server.
For the moment, we’ve dropped this feature. We might implement our own switching feature where the ghost will stay predicted during the transition phase until it reaches the server position, ensuring no weird artifacts occur.
Conclusion: This current version of predicted/interpolated switching doesn’t work well for ghosts whose behavior can be unpredictable due to hit detection or physics.