Hello! I’ve been working on a Netcode for Entities project and I’m struggling to implement certain gameplay mechanics that I’d like to be client predicted. When I can get them to work, the implementation I came up with doesn’t feel quite right. I’ll give you a few examples of what I’m trying to do below and the kinds of things I am running into.
Before I get into it, here are some basic questions that may be related - Does prediction only work on input or command data? Further, does input/command data need to be written to every simulation tick?
Example 1: Point and Click Movement
The end goal is to have a system that allows a player to click anywhere in the game world and move their character to the selected position. I’d want this system to be client predicted so the player immediately begins to move towards the destination. I did get this to work, but my solution just seems “okay” so I’d be curious to know if there is a better way to do this.
On the client, have a target position component that I set whenever the user clicks into the game world.
[GhostComponent(PrefabType = GhostPrefabType.PredictedClient)]
public struct ClientTargetPosition : IComponentData
{
public float3 Value;
}
Also on the client, I have a system that runs in the GhostInputSystemGroup which sets an IInputComponentData every frame - seems like it doesn’t work if I only set this whenever the user clicks.
[GhostComponent(PrefabType = GhostPrefabType.AllPredicted)]
public struct InputTargetPosition : IInputComponentData
{
[GhostField(Quantization = 0)] public float3 Value;
}
Then finally my move system is a predicted system that moves the LocalTransform towards the value stored in the input component.
Again, this all seems to work fine, I’d just be curious if there is any way to optimize this so maybe the input component doesn’t need to get written to every frame or otherwise. Using command or input data seems like the correct approach over an RPC as users could spam-click, so having a constant and reliable communication stream of input seems to make sense to me.
Example 2: Time-Based Attack System
This one I seem to be struggling with a bit more as I’m trying to “predict” non-input data. End goal is to have a player target another player. Once the target player is within range, the attacking player can apply an attack to the targeted player. At that time, a cooldown timer begins where the attacking player cannot execute any more attacks. If the attacking player keeps the targeting player selected, the attacking player will automatically attack as soon as the cooldown expires. I would also want this to be predicted so that as soon as an attack is executed, damage effects can be spawned into the world, etc.
I initially tried using a GhostComponent + GhostField for a countdown timer that I would tick down and reset in a predicted system, however the server values just override the client values in the GhostUpdateSystem. To my understanding, this is the correct behavior as GhostFields basically just display the values from the server and can’t be set directly. The result in the game is that whenever the timer expires, the client performs an attack, but then the GhostUpdateSystem keeps setting the cooldown timer back to a value that will apply the attack, so the client preforms the attack multiple frames in succession until the tick where the server cooldown is reset gets received by the client.
Another implementation I tried is something I came across in the Asteroids sample project, where rather than using a countdown timer, the server tick where the cooldown is complete is stored. However, this has the same effect as the timer implementation.
public struct MeleeAttackCooldown : IComponentData
{
[GhostField] public NetworkTick Value;
}
So what would be the best way to implement a predicted system like this? Am I going to need to use some command data or input data for this as well?
Example 3: Destroy Entity System
Because entities cannot be destroyed client-side, I’ve implemented an entity destruction system that can have the effect of predicting entity destruction, let me know what you think of it and if there are any ways I can improve. Use case for this would be dealing the final blow to an enemy which should make them immediately disappear.
When I need to destroy an entity, I add a DestroyEntityTag to the entity in a predicted system. Then I have another predicted system that will destroy the entity on the server side, or move the entity offscreen client side.
This is another one that seems to work fine, but the main issue I see with this is that I’m unsure how it would react to handling the case where a client predicts the destruction of an entity, but the server rejects the destruction.
foreach (var (transform, entity) in SystemAPI.Query<RefRW<LocalTransform>>().WithAll<DestroyEntityTag, Simulate>().WithEntityAccess())
{
if (state.World.IsServer())
{
ecb.DestroyEntity(entity);
}
else
{
transform.ValueRW.Position = new float3(1000f, 1000f, 1000f);
}
}
Anyways, still just a bit unsure if I am thinking about prediction correctly so any feedback or guidance related to anything mentioned above would be tremendously appreciated. Happy to post any more code if you want to have a look. Thank you!!