Movement is not fluid in standalone (On the client) (stuttering)

Something weird is happening to my game on the client side when I run it in standalone.
Movement is not fluid.

When I move the character seems to shake.

In the editor the movement is fluid. Iit only happens in standalone on the client. Server run well.

I read somewhere that too high FPS (higher than the screen refresh rate) can cause stuttering.

But that’s not the case. My screen refreshes at 144Hz. I limited the FPS to 110 and the problem persists.

I’m trying to record a video to show here… But I did it several times and the cuts are not noticeable in the video.

It’s a little noticeable, but the reality is more serious. I think the recording frequency compensates for the shake or something like that.

I think there is not bottleneck. CPU at 13%, GPU 40%, RAM 20%
The problem must be something else.

In any case, I have no idea what is going on.
Does anyone know?

Thank you so much

enable server correction debug
shownetcorrections 1

1 Like

Hi @Rev0verDrive

The command “p.NetShowCorrections 1” shows two capsules (one red and one green).
And a message in the console.

[2024.12.09-04.21.55:986][417]LogNetPlayerMovement:

Warning: *** Client: Error for BP_BlueTeamCharacter_C_0
at Time=102.332 is 10.814 LocDiff(X=-7.084 Y=8.170 Z=0.000)

ClientLoc(X=5006.408 Y=198.262 Z=140.150)
ServerLoc(X=5013.492 Y=190.091 Z=140.150)

NewBase: L_Arena01:PersistentLevel.StaticMeshActor_3.StaticMeshComponent0

NewBone: None
ClientVel(X=548.951 Y=-242.182 Z=0.000)
ServerVel(X=463.707 Y=-355.755 Z=0.000)
SavedMoves 3

I can imagine there is a difference in position between the server and the client. Right?

I assumed that replicating movement would take care of this.

This is the character replication settings:

And this is my movement function:


void AMyPlayer::InputMove(const FInputActionValue &Value)
{		
	// input is a Vector2D
	const FVector2D MovementVector = Value.Get<FVector2D>();

	if (!IsValid(Controller)) return;
	
	// find out which way is forward
	const FRotator Rotation = Controller->GetControlRotation();
	const FRotator YawRotation(0, Rotation.Yaw, 0);

	// get forward vector
	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

	// get right vector 
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

	// add movement 
	AddMovementInput(ForwardDirection, MovementVector.Y);
	AddMovementInput(RightDirection, MovementVector.X);
}

The error occurs in this scope

AMyPlayer::InputMove => No Authority / Client / ROLE_AutonomousProxy /

I tried to run the function on the server only

	UFUNCTION(Server,Unreliable)
	void InputMove(const FInputActionValue &Value);

and also in multicast.

	UFUNCTION(NetMulticast,Unreliable)
	void InputMove(const FInputActionValue &Value);

But it didn’t fix the problem.
What is wrong? What should I do?

Thank you so much!!

The stutter/jitter is server correction. essentially snapping you to the authoritative position (servers).

Are you running custom movement or using the character movement component (CMC)?

I’m basically using UCharacterMovementComponent

Just derive the class to have the configuration written in a file (in case the bluprint gets corrupted and don’t lose the data).

class UTheCharacterMovement : public UCharacterMovementComponent

This is my configuration:

UTheCharacterMovement::UTheCharacterMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	PrimaryComponentTick.bCanEverTick = true;
	SetIsReplicatedByDefault(true);
	MovementMode = EMovementMode::MOVE_Walking;
	
	GravityScale = 1.0f;
	MaxAcceleration = 1000.0f;
	BrakingFrictionFactor = 1.0f;
	BrakingFriction = 6.0f;
	GroundFriction = 8.0f;
	BrakingDecelerationWalking = 500.0f;
	bUseControllerDesiredRotation = false;
	bOrientRotationToMovement = false;
	RotationRate = FRotator(0.0f, 720.0f, 0.0f);
	bAllowPhysicsRotationDuringAnimRootMotion = false;
	GetNavAgentPropertiesRef().bCanCrouch = true;

	bCanWalkOffLedges = true;
	bCanWalkOffLedgesWhenCrouching = true;
	bUseFlatBaseForFloorChecks=true;
	PerchRadiusThreshold=60.0f;
	
	MaxWalkSpeed = 600.0f;
	MaxWalkSpeedCrouched = 300.0f;
	MinAnalogWalkSpeed = 0.0f;
	MaxSwimSpeed = 300.0f;
	MaxFlySpeed = 600.0f; 
	MaxCustomMovementSpeed=600.0f;
	
	SetCrouchedHalfHeight(65.0f);

	GetNavAgentPropertiesRef().AgentRadius=-1.0f;
	GetNavAgentPropertiesRef().AgentHeight=-1.0f;
	GetNavAgentPropertiesRef().AgentStepHeight=-1.0f;
	GetNavAgentPropertiesRef().NavWalkingSearchHeightScale=0.5f;
	GetNavAgentPropertiesRef().bCanCrouch=true;
	GetNavAgentPropertiesRef().bCanJump=true;
	GetNavAgentPropertiesRef().bCanWalk=true;
	GetNavAgentPropertiesRef().bCanSwim=true;
	GetNavAgentPropertiesRef().bCanFly=false;

	bRunPhysicsWithNoController = false;	

}

Everything else is untouched.

I use it this way on my character:

AMyCharacterBase::AMyCharacterBase(const FObjectInitializer& ObjectInitializer): Super
(
	ObjectInitializer.SetDefaultSubobjectClass<UCharacterCapsuleComponent>(ACharacter::CapsuleComponentName)
	.SetDefaultSubobjectClass<UCharacterSkeletalMehs>(ACharacter::MeshComponentName)
	.SetDefaultSubobjectClass<UTheCharacterMovement>(ACharacter::CharacterMovementComponentName)
)

Is there any way to make the position update in smaller time intervals?
Maybe that way the stuttering will be less noticeable.

Here’s the default movement for CMC.

CMC uses client-side prediction. You input movement, client applies it, then stores the moves. At next client → server update the buffered moves are sent to the server and the clients results for each. Server applies the same moves and compares results. If there’s a precision error it corrects, otherwise sends an Ack.

Somewhere you are applying a move, or velocity change that server isn’t being notified about, hence it not matching up and correcting.

My bet is movement speed is different. Do a print string of the CMC -> Max Walk Speed for both client and server.

Docs has a detailed overview of CMC.

1 Like

Ok, this is exactly the same as my C++ code I posted above. This point is correct then.

Thank you very much for this explanation.

I think this is not the problem.

I looked in my code where I modified the speed and it was not modified.

I only change it when the character is on a moving platform… this is not the case.
Anyway, now that I know this, I’ll make sure that the speed on the moving platform on the server and client are the same. Thanks for the tip.

I did the test and the speed on the client and server are the same. However, the stuttering persists.

Maybe the problem is something else.

Thank you so much!!

I did the same test you suggested.
But this time instead of using MaxWalkSpeed ​​I used the character’s Velocity vector.

I used this code this time

I found that the client updates four times faster than the server (it has four times more ticks than the server)

So the client has more velocity data than the server.
But the velocity of the server and client are the same If you look for every four ticks on the client.

Could this be the problem? The server is not fast enough?

Server should be running at a 30Hz or 60Hz stable. Client should be updating the server at roughly 60Hz. You want a 1:1 or 2:1 client:server ratio here. If the server updates more often than client it starts getting whacky.

First section (Server CPU Usage) covers setting ClientNetSendMoveDeltaTime
https://dev.epicgames.com/community/learning/knowledge-base/mo9O/unreal-engine-character-movement-optimizations?locale=es-es

1 Like

Hi @Rev0verDrive
Yes!! The first optimizations in that document has largely fixed the problem.
Now the movement corrections are not on every tick.
It’s not completely fixed but it’s a big improvement.

I will continue applying these optimizations to see if I can improve even more.

Thanks a lot.

You can also tweak Network Smoothing. Focus on Smooth Location/Rotation times. Do small adjustments and test. You want Loc/Rots to be updated as quickly as possible. Too fast adds jitter though.

Location: 0.125, 0.15
Rotation: 0.075, 0.08 - 0.1

Don’t go crazy with these. You’ll start adding to desync.

1 Like

Ok, i will try it too
Thank you so much!!

Totally forgot to mention that you should test your character movement on an empty barebones level. This will help troubleshoot whether its the character or other actors/overhead causing issues. Also, no UI.

1 Like

Ok, I’ll do it like that.
Thank you so much

Hi @Rev0verDrive

I think I finally found the problem. It was something I didn’t expect.
For some reason the whole problem was caused by replicating the capsule component.
I was replicating a variable in the capsule component so I needed to replicate the component as well.
Then I stopped using that variable and forgot not to replicate the component.


This fixed the movement prediction issue.

SetIsReplicatedByDefault(false);


Thank you so much to the person who made the last comment on this post:

https://www.reddit.com/r/unrealengine/comments/19e7yla/comment/kjd635j/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

Now it is the capsule itself that makes strange movements. :rofl:

Animations look smooth and I don’t see any stuttering from the client side… but the capsule will be hidden in the game so… I’m not sure if I should be worried about the capsule stuttering.

I feel better now (after three days looking for a solution to this problem).
I’ve been close to implementing my own movement prediction.

.
.
.

Thank you very much for your help @Rev0verDrive

.
.
.
.
.
.


In case anyone is interested in implementing their own movement prediction.
I’ll leave links here…


The best example I found was this project on github:

I managed to compile by making some changes in Target.cs… but all Blueprints don’t work anymore in UE5.5… so don’t waste time compiling… it won’t work… but the code is readable.

Then this is the YouTube playlist. I only saw the first and the second video, but it looks really good.

And this is in perfect Chinese… but Google can translates it.


Greetings to all