#Question About Playing Visual Effects With Client Prediction

24 messages · Page 1 of 1 (latest)

maiden kelp
#

I'm wondering what the optimal way to start and stop playing visual effects with client side prediction? One of my visual effects loops until you stop it in code. I'm worried if I start & stop it in the replicate function it will call the visual effect to start playing many times per second and cause performance inefficiencies.

Currently I am checking to play a visual effect like this
if (!playerController.currentReplicateState.ContainsReplayed() || (!playerController.IsOwner && playerController.currentReplicateState.IsFuture()))

And this works but the visual effect plays late when ping is increased. Also the if statement sometimes is skipped by a large rollback occurs and the state is completely skipped in my state machine. I could simulate playing the visual effect to a certain tick but after reading about the performance problems it could cause I am concerned about implementing that.

Are there any examples how to start/stop visual effects with prediction v2? I want to make sure it's as optimized and accurate as possible. My game is designed to be very competitive and every tick matters.

Also I am on the most recent version of Fishnet Pro.

maiden kelp
#

Bump. If you need any more information or anything lmk

maiden kelp
#

@haughty acorn @steep steppe Any help would be great

steep steppe
#

When you say the effect plays weight when the pain is increased do you mean on objects you are spectating? Because if so, presumably the effect would play when the client received the data.

maiden kelp
#

Nvm I found the reason why the VFX.stop is being skipped. Still curious if there is a solution to ensure the VFX plays on the correct frame. Maybe simulating the VFX to the currentTick?

steep steppe
#

Why would the data be received after the state though? Is it not in the state?

maiden kelp
# steep steppe Why would the data be received after the state though? Is it not in the state?

The states have substates to break it up more like a fighting game (startup, active, recovery). If the recovery ticks are as low at 3 or 4 then the substate is skipped and it exits the state without simulating through the state machine completely is my guess. The Exit() function of all states are always called so I can try triggering the VFX always in that function and it should work.

#

Currently I am checking approximately If substateIndex == 2 then and 2 is the third 0,1,2 so the recovery substate. Very rarely it skips the if statement completely if the rollback is relatively large and very different than the prediction. It then changes state before ever reaching index 2 of that substate

maiden kelp
maiden kelp
#

Yeah I am getting the issue even worse. On the state machine it changes state every reconcile so the Exit() function gets when I start drifting because of CSP resimulations. I tried checking if the state contains created but it also doesn't work. Do you know any fixes to ensure VFX starts and stops being played on the correct replicate state?

    {
        // Stop VFX using the same condition as in PlayerState.cs for consistency
        if (!playerStateMachine.playerController.currentReplicateState.ContainsReplayed() || (!playerStateMachine.playerController.IsOwner && playerStateMachine.playerController.currentReplicateState.IsFuture()))
        {
            // Stop all VFX from the scriptable object for index 0 of drift
            PlayerSubState driftSubState = playerStatesDictionary["Drift"].subStates[0];
            if (driftSubState.enableVisualEffect && driftSubState.vfxKeys != null)
            {
                foreach (string vfxKey in driftSubState.vfxKeys)
                {
                    if (vfxKey != "null")
                    {
                        playerStateMachine.playerController.playerDataHolder.playerVFXDictionary[vfxKey]?.Stop();

                        //Set the color of the drift VFX to yellow
                        //playerStateMachine.playerController.playerDataHolder.playerVFXDictionary["Drift"].SetGradient(new Color(1f, 1f, 0f), 1f); // Using RGB values for yellow
                    }
                }
            }
        }
    }```
maiden kelp
#

I tried ContainsTicked and that didn't work either sadly

steep steppe
#

You're using Appended order on the predictionmanager?

maiden kelp
#

I was getting some pretty bad rollback that happened very occasionally.

#

It was happening because I am using BoxChecks and SphereChecks for collision detection in the replicate function instead of typical colliders. This is so the system is expandable and customizable. The hit detection was happening on different ticks for client/server and the difference was around 1 tick or so. It very rarely resulted in a problem but when 1 tick is the difference between landing an attack or not then it resulted in a fairly large rollback. I was thinking inserted would help reduce the visual impact of that so it feels better for players

steep steppe
#

@maiden kelp Gotcha. Inserted ensures the data ALWAYS runs on the right tick as its put into the replicate history, and is only run when reconciled. Its done this way because data you receive is always behind due to latency, thus goes into history to be run during reconcile.

Appended will put it into the history for corrections but also runs it during OnTick asap.

#

Its possible for the tick to replicate over mutliple rollbacks if for example it is tick 100, and you reconcile to 97, then 98, so on

#

You can use appended order and only show vfx if !replayed or have some sort of check to prevent duplicate vfx...

#
private List((VFXType vfx, uint tick)) _playedVfxs = new();

//In replicate...
if (doVFXCondition && TryAddVFXPlayed(VFXType.Jump, data.GetTick()))
{
    //Do VFX!!
}

//some other method.
private bool TryAddVFXPlayed(VFXType vType, uint tick)
{
    for i... _playedVfxs.Count
    {
        (VFXType vfx, uint tick) entry = _playedVfxs[i];
        //Not same VFX or played on a diff tick.
        if (entry.vfx != vType)
          continue;
        if (entry.tick != tick)
          continue;

        //If here then matches, meaning already added.
        return false;
    }

    //No matches, add.
    _playedVfxs.Add((vType, tick));
}```
#

Then in reconcile remove old entries

private void RemoveVFXPlayed(uint tick)
{
    for i... _playedVfxs.Count
    {
        (VFXType vfx, uint tick) entry = _playedVfxs[i];
        if (entry.tick <= tick)
           _playedVfxs.RemoveAt(i--);
    }
}```
maiden kelp
# steep steppe You can use appended order and only show vfx if !replayed or have some sort of c...

Is there a solution for inserted? Ensuring all data is always on the right tick is extremely important because I'm developing a fighting game. If there is no solution I can switch back to appended but is there a solution to ensure important data is replicated accurately? Maybe important data should be in the reconcile struct and the replicate struct to ensure the prediction is as accurate as possible for combat?

steep steppe
#

This is for inserted