Client side Jittery movement

Hey everyone,

So I’m working on implementing a glide ability into my multiplayer game and I’ve noticed that when I activate the ability on the client, the movement appears jittery.

The way the ability works is that every tick, the game checks to see if a boolean called bIsDodging is true. If it is, it will set the Velocity of the player to a certain value every tick. I also decided to implement the ability within a custom Character Movement Component.

I’ve tried to remedy this by calling the ability on the server and then simulating it on the client but that didn’t work. I’ve tried using custom flags but honestly don’t know how they work too well. Does anyone have any ideas on how to fix this?

I had this exact same issue happen when I implemented my custom Character Movement Mode for climbing.

This is cause when the client side character is constantly moving differently then the server side character and the predictive movement in the Character Movement Component is trying to synch them up. This constant synch up looks like jittery movement.

I was able to fix my issue by always following this pattern for multiplayer movement:

Make an RPC call from the client to the server requesting some kind of movement. In my case it was to enter climbing mode. Then have the server side verify if this is allowed and if it is, set a replicated variable with a rep notify. In my case this was the IsClimbing boolean. Then let the value replicate from the server back to the client (this is automatic). When the rep notify is called on the client, then you can adjust movement related settings (movement speed in my case) if needed. However, any actual movement like Set Velocity or Add Movement type of actions should only happen on the server and never on the client. The goal is to keep the movement in synch so that the server isn’t constantly correcting the position of the client.

1 Like

Ah I see. So to make sure I’m getting this right, you set up a RepNotify on the IsClimbing boolean and the notify is called on the client that’s when you started to adjust stuff for movement?

Also I guess I shouldn’t try to do a local simulation when changing velocity but rather just let it replicate from the Server down to the Client? I read a forum post where one of the Epic staff said that he did that to smooth out the movement. Maybe something changed? I don’t really know.

In the rep notify on the client side I change settings that could affect future movement, such as the walking speed setting in the Character Movement Component, however you want to do all actual movement on the server only and then let it replicate down.

UE4 has a really great predictive movement system inside the Character Movement Component. The system smooths out the movement shown on the client and helps to counteract network lag. The predictive movement system builds a queue of all movement actions. This is how they are able to do network replay so easily. When I built by climbing system I had to add my climbing moves into the queue to keep it compatible with everything.

It seems for me I can’t really do the RepNotify route since I’m setting Velocity every tick. I understand that setting Velocity isn’t networked so I tried to follow something similar by replicating the boolean in control of the ability and setting the Velocity on the server and letting the Client replicate from that. Didn’t solve the jitter problem.

When you said that you “had to add my climbing moves into the queue”, what exactly did that entail? Did you custom flags, a custom SavedMove class, etc?

Yeah, I had to add custom flags. The C++ code is fairly long, but these are the methods in the CharacterMovementCompoonent I had to override:

UpdateFromCompressedFlags(uint8 Flags)
FSavedMove_MyMovement::Clear()
FSavedMove_MyMovement::GetCompressedFlags() const
FSavedMove_MyMovement::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* Character, float MaxDelta) const
FSavedMove_MyMovement::SetMoveFor(ACharacter* Character, float InDeltaTime, FVector const& NewAccel, class FNetworkPredictionData_Client_Character & ClientData)
FSavedMove_MyMovement::PrepMoveFor(class ACharacter* Character)
FNetworkPredictionData_Client_MyMovement::FNetworkPredictionData_Client_MyMovement(const UCharacterMovementComponent& ClientMovement)
FNetworkPredictionData_Client_MyMovement::AllocateNewMove()

There are only minor modifications in each of them. Mostly just updating my flags:

bWantsToClimb and bWantsToExitClimb

Here is where I got all the info from:

One more thought. Have you tried using AddMovementInput instead of setting the velocity directly? I think you are going to have more luck with this.

Yeah I decided to use AddMovementInput instead and I’m getting slightly better results. But now I’m dealing with a problem where when I try to get values from my movement component on the client, the AddMovementInput function refuses to work. I’ve tried replicating the variables of interest (i.e a boolean to indicate my character activated the ability, and some float values for directing the character). But no luck there.

EDIT: So, while I haven’t figured out the issue, I managed to figure out that for some reason, The server recognises that my boolean value is true (since I set on the server in the first place) but the client doesn’t know that (which is puzzling since I made sure the variable replicates). Any ideas on why this would happen?

Okay so update time. I think I should’ve made it clear in my previous post that the boolean variable was within my movement component and I was trying to replicate it within that class. Seems like it’s either not possible or too tedious to get to work.

As such I decided I’ll place my boolean within my character class and just work from there. The only issue I have with this is organisation, since my ability, which as I’m sure you would know, is a glide, has to do with movement.

Just wondering, when you implemented your climbing ability was all of your variable replication within your Character class or was it within the Movement Component?

Normally I do all of my replication in the character class. One of the things I learned the hard way is that some replication only works inside actors directly owned by the player controller. Trying to get a door to replicate was a nightmare and didn’t start working until I moved the door code to the player controller class. In the case of the climbing mode, my boolean flags are in the Character Movement Component because they needed to be there for the predictive movement queue.

If you set a replicated variable on the server, it should be set to the same value on the client within a few miliseconds. If the client doesn’t match the server this leads me to think that you have other code running that is interfering with the value on the client.

Timing is a big issue in network games, so I recommend setting lag of like 500 ms for testing. Sometimes I find that my code works with low lag, but then fails when the lag is higher. This shows that there is a fundamental flaw in the timing of my code that needs to be fixed. So be sure to test with high lag or your game will break as soon as you try to play it over the internet.

After I moved my boolean flag for my ability to my character class it seems to be working fine now. Seems like replication of variables within a Character movement component isn’t a thing.

Anyways I managed to get the ability working after a little bit of replication. Finally, after 3 days of hard work. Thanks for all the help buddy! See you in the next year!