How can I replicate character rotation?


I’ve been trying to get rotation replicating properly in my prototype. I’ve tried a variety of methods and while the rotation seems to be replicating well from the server to the client, the client’s character seems to be maybe double-transforming for some reason. You can see my problem in the video below where the server is the controlling the character on the left and the client is controlling the character on the right.

Below is the relevant graph from the blueprint of the character which derives from Character. The SmoothRoll event is called on Tick in the same blueprint, and PawnRotation is set in the player controller blueprint. VertSpeed is set on Tick.

I’ve tried many different methods but I must be missing something. If you can suggest the most efficient way I might make the client’s character behave like the server’s, I would appreciate it.


Guess: Maybe because it is locally controlled?

Hi unit23.

The character is controlled by its player controller. Whether a character has authority or not, it will eventually execute the same multicast event that executes SetActorRotation as depicted. If you think being locally controlled is the problem, how might one alter the character to not be locally controlled?

Thanks for your time.

Any suggestions would be appreciated.

Thanks for your time.

Have you tried setting the Pawn Rotation variable only from the server? Without context, it appears it could be an issue where your client is setting it, then the client gets a replicated value from the server, rinse and repeat.

Along the same line of thinking, you’re setting the rotation locally, and then multicasting. The client and server will always have disparity here. In your example, the client sets it to where it is now, the server multicasts, and even with 20ms round trip delay (right in PIE a small amount of delay between the client and server is still present), by the time the client executes the multicast RPC, it will be setting it to the rotation that it WAS, not where it should be right now. Since the client and server will never have instantaneous communication, multicasts will always result in a client seeing things as they were, x milliseconds ago.

Your paradigm is not flawed, you’re doing things how you should be. You’re just not accounting for the delay between the client and server.

The easy approach is to simply not execute the multicast logic on the owning client, since they are already locally rotating. This will make the client see it as it should be, and the server/other clients will see the same but with a slight delay. On your multicast RPC, you could try only executing if the pawn is not locally owned.

The more difficult approach is to account for the client’s lag on the server, and sending the multicast to where the rotation should be in x milliseconds, where x is their one way trip time. This works for some games, and not others. Example, dodging. Client A fires at Client B, Client B dodges. If the server sets the position of Client B based on their lag, Client A sees at least some “teleportation” occur. In scenarios like this, a more refined approach is necessary, such as speeding up an animation by x milliseconds instead of simply setting the position. That, of course, comes with the drawback that higher latency players end up dodging much quicker for other clients, and can lead to “wtf?” moments.

Without context, it really looks to me like you’ve just got to account for the disparity between the rotation as it exists on the server and client. For now, testing the easy approach will take the least amount of time, and if it runs smoothly at that point you know this is the issue.

That sounds like some really good advice. I’ll give it a shot implementing it this weekend.

Thanks so much.

Any time, I hope it helps in this scenario. It’s always hard to tell what the exact cause is. Let me know how it goes :slight_smile:

I return with news.

After cleaning up the redundant code, my graph now looks like this:

I don’t understand why but it seems that the jittering is gone. It returns if I remove the IsLocallyControlled branch. I also don’t understand why a check of local control on the authority should affect the jittering of the remote.

Replication works with the above setup. Sometimes tidying your code fixes problems one couldn’t solve before.

I have a new problem however. It would seem that my rotation is not frame rate independent and I don’t know how to make that so. As you can see, I’ve clamped the value of VertSpeed in an attempt to stop the character from over-rotating on slower clients but this leaves me with the slower clients rotating more suddenly than I’d like. You can see the current state of the rotation in this video where the server is on the left and client is on the right:

I think the source of the problem lies in the function I use to set VertSpeed here:

As you can see, if a computer has a slower frame rate, more time will pass between ticks, and thus a larger number will be read into the Target pin of FInterpTo. Alas, I don’t know how to normalize this value. It seems this questioner HERE had the same issue though.

Thanks again for your time. All advice is appreciated.

You’re doing essentially what I said in my response. The multicast is not run on authority only, it’s run on all. Like mentioned in my prior post, you were conducting the rotation locally, and then the server was multicasting the exact same thing so the local player would bounce back-and-forth between its own logic and the multicast. Making sure the multicast does not occur on the local player removes the back-and-forth.

Interpolation nodes already account for framerate, which is why it accepts the delta time. Why are you accounting for delta seconds in your target? In the screenshot, you’re accounting for delta time twice, which will result in exactly what you’ve stated. Either the target should account for delta time, or the interpolation. Not both.

Even removing the locality check after the multicast event fires still sees the rotation replicating properly. That’s what really confuses me. I thought I needed that second check but it seems not.

I tried removing the nodes I circled in the image in my last post but without those nodes, the character just spins around and around as I hold the direction key. It looks like it might be not taking the shortest route for interpolation and instead resetting to 0° from say, 1° by spinning °359 instead of just -1°. I’ll investigate more this evening.

Thanks again for the help and patience. Many of these concepts are new to me.