Networked Character movement in C++ is jerky

Hi all,

I’m seeing unpredictable and jerky character movement when in a networked scenario and was wondering what approaches you guys have taken to work around it.

I have taken various approaches and seen differing results, none of them to an acceptable standard. In this scenario, I have two players: one is the server, and one the connecting client. When the server player moves, it is smooth for both him and the client - no problems there. When the client player moves, it is not smooth, and jerky for one (or usually both) of the players. Presumably this is the server attempting to correct the position of the client.

My current process is as follows: I have a custom component for movement:


UCLASS()
class CUSTOM_API UCustomCharacterMovement : public UCharacterMovementComponent
{

Within this, I have a function set up to request movement:



UFUNCTION()
    void MoveCharacter(ARoguelikeCharacter* character, const FVector& direction, float value);
    void MoveCharacterInternal(ARoguelikeCharacter* character, const FVector& direction, float value);

    UFUNCTION(Server, Reliable, WithValidation)
    void ServerMoveCharacter(ARoguelikeCharacter* character, const FVector& direction, float value);
    virtual bool ServerMoveCharacter_Validate(ARoguelikeCharacter* character, const FVector& direction, float value) { return true; };
    virtual void ServerMoveCharacter_Implementation(ARoguelikeCharacter* character, const FVector& direction, float value);


This is very basic: the character calls “MoveCharacter” - if they have authority, they call MoveCharacterInternal, else they call the server RPC function, which in turn calls the internal method. The body is as such:



    character->AddMovementInput(direction, value);


This process is begun at the press of a button - so if the player presses or holds W for example to walk forward, this function is called per tick.

So it is very basic. This annoyingly results in jerky movement - the client character will often realign to new locations as the server decides where it should be.

Is there a better approach? What solutions have other people taken?

Wait… why are you manually calling RPC’s?

Character Movement already does prediction and replay so you don’t need to do any of this, you simply call add movement input from the owning character pawn, and the movement component takes care of the rest. Whatever you’re doing here is fighting the already existing system. CMC is already networked.

Yeah - I’ve probably ■■■■■■ that up through progressive attempts. My original was just to call the movement function, and that worked roughly the same - occasional jittery character movement, hence my attempts to farm it out to the server explicitly. Good to know that that is already catered for, though.

Basically you don’t need to talk to the component at all. In the character, you just call ‘AddMovementInput()’. Character Movement does everything else.

Example from one of my projects:




void AHT_Character::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    PlayerInputComponent->BindAxis(TEXT("Character_WalkForward"),    this, &AHT_Character::OnMoveForward);
}

void AHT_Character::OnMoveForward(const float Val)
{
    AddMovementInput(GetActorForwardVector(), Val);
}