What is the best way to smooth player movement for network hiccups? Interp? Ease? (no physics)

So my online multiplayer game has simple 6DOF movement using the floating pawn movement class. When you move in any direction, the speed is 3500 - no acceleration, no physics force. You just go 3500 immediately. I set up my simple server movement replication like so and it works perfectly with a good connection:

But of course, on worse CPUs/network connections it can look jittery. For my case, what is the best way to “smooth” the jitter so that when the affected character’s movement replicates to other clients it doesn’t jitter or teleport but rather moves smoothly in the predicted vector? I imagine it’s one of the VInterp or Ease functions, but I am at a loss where to get a sort of “previous actor location” so that the two vectors I am easing from and to are legit. In other words, Interp and Ease require two vectors to smooth. I can get the current actor location, but what is the other vector I am trying to get? Is there another way to accomplish the fakery I’m trying to achieve?

For good measure, here are three ways that aren’t working really, but are doing something. The Ease function seems to smooth the best but I am basically using the same location from a few ticks before to simulate a “previous” actor location.

To sum up what I am trying to achieve: I don’t want my players to see enemies teleport/jitter when there are network or client updating hiccups. I’d rather see a smooth “catching up” to where the enemy now is. (I think some call this rubber banding?)

Thanks!

I’m going to write a comment, rather than answer, because I am not an expert on Multiplayer (yet…) but, I know that there is a practice where you actually MOVE the player on the client at the same time as the SERVER.

This doesn’t really answer your question, so I apologize, but this resource may help you understand some things if you haven’t already used this: networking | Gaffer On Games

Also this post may help you: Lack of Movement Components for Networking - Feedback for Unreal Engine team - Unreal Engine Forums

Yep, I scrubbed those already. I appreciate the info though! I’ll take what I can get. I do hope there is a more simple solution exposed in BP that can get what I need. Hoping someone from Epic will see this and take a crack at it! :slight_smile:

Hi Matt!

Well there are a few approaches, but I’m going to suggest one that I think will be pretty straightforward and fairly robust in most situations.

First of all, I’m assuming you’re talking about smoothing the location of other players you see when playing, not your own player. Those are two different problems really (and the first is the easier to solve).

First of all, you’ll want to be able to detect whether the client has been moved by a network update. In blueprints this is a little messy with Blueprint-only projects right now as we do not have a callback to tell you the network update happened. The most reliable way to do this is probably have your own replicated location, rotation, and possibly velocity set by the server each update, and catch the RepNotify event for that struct on the client (In blueprints select “RepNotify” under “Replication” for your variable). When the client detects that the network location from the server arrives, this is your “new” server location. You can then choose how to use this new location.

One approach (the simplest) would be to turn off the “Replicate Movement” flag on the actor, and do the interpolation yourself to the server location on the client. Turning off this flag means the server won’t do the normal replicate and change of location for the client that happens automatically. Then you could do an interpolation in the tick for the client from current position to the server replicated position, however you like (EaseIn, Lerp, etc). (By the way, make sure you are factoring in the Alpha or DeltaTime parameters to your Ease or Lerp functions!)

Alternatively, you can leave replicated movement on (meaning the root component will teleport to the server location when it’s updated), but interpolate another part of your pawn to that location instead. For instance, say you have a sphere collision root, and an attached mesh. You could let the server teleport the sphere, and update the relative location of the mesh to “undo” the server change, essentially leaving it in the spot in the world. In code this looks like this (I’ve simplified it to make it more readable I hope, even if you aren’t a programmer):

    	// The mesh doesn't move, but the collision root does so we have a new offset.
    	FVector NewToOldVector = (OldLocation - NewLocation);
    	float Distance = NewToOldVector.Size();
    	if (Distance > MaxSmoothNetUpdateDistance)
    	{
    		// We would move too far, so make the offset zero (warp to the root)
    		MeshTranslationOffset = FVector::ZeroVector;
    	}
    	else
    	{
    		MeshTranslationOffset = MeshTranslationOffset + NewToOldVector;	
    	}

Then each tick you decay the relative offset of the mesh towards zero, meaning you try to move it back to the root position. This is similar to how the engine code does this for Characters (UCharacterMovementComponent::SmoothCorrection figures out MeshTranslationOffset based on the change in location, and SmoothClientPosition_Interpolate decays it toward zero with some simple code (again simplified):

   		if (DeltaSeconds < SmoothLocationTime) // SmoothLocationTime is about 0.125 secs
    	{
    		// Slowly decay translation offset
    		MeshTranslationOffset = MeshTranslationOffset * (1.f - DeltaSeconds / SmoothLocationTime);
    	}
    	else
    	{
    		MeshTranslationOffset = FVector::ZeroVector;
    	}
    			
    	FVector NewRelTranslation = GetComponentToWorld().InverseTransformDirection(MeshTranslationOffset) + DefaultMeshOffset;
    	Mesh->SetRelativeLocation(NewRelTranslation);

The advantage here is that your are moving the invisible gameplay-relevant collision to exactly match the server collision as soon as possible, but slowly moving the visual representation (the mesh) over time to reduce choppiness.

Both approaches mentioned above should be compatible with continuing to update locations based on a velocity during Tick, which will further maintain the smooth look on clients.

Hope this helps!

(Note: in code, detecting the network change in location is much easier, you override PostNetReceiveLocationAndRotation() and you can record the change in location, knowing this came from a network update).

2 Likes

Thank you, . Both make sense to me and being a blueprint-only hopeful, this will take time to digest. But if nothing else, I have some good engineers who’d get this to happen. Thanks for this!

Hello there, EchoHeadMatt. If possible I would really love to speak with you about some of the things you have been doing in your project as I really think they might help me with my significant road block in my own. Please let me know if it would be possible to have a brief discussion on character replication and movement. Thanks so much!

Would this work with physics based movement that uses Set Linear Physics Velocity to move the pawn? I am trying to replicate this using the Physics based UFO from content examples.