Custom Movement - How do I fix Slow-Mo Animations when sprinting? (Please help)

SLOW-MO ANIMATION WHILE SPRINTING (I DON’T WANT THIS) PLEASE HELPPP

So I want to create sprinting for multiplayer and I don’t want to use the “Set Max Walk Speed” with some run on server events because this introduces what people call rubber banding for clients that have above 30 ping.

Avoiding that I created a Character Movement Component, I created the sprint logic and I call it from blueprints with a Input Action. It works and the speed updates and I get no rubber banding or lag for any clients or servers.

So my problem, the problem is that my sprinting animations look Slow-Mo almost when I’m sprinting. The animations only appear to be Slow-Mo for simulated proxies, or what I’m saying is that for the owning player your animations look fine, and the server player looks fine as well, but if any client or the server player looking at other clients animations (ONLY WHEN SPRINTING) the animations appear Slow-Mo.

I am using a walk/run blend space, with basic animations blueprint (using third person template). I also have a crouch blend space, and if I sprint while crouched it will also make the animations appear Slow-Mo.

  • Blend Space:

  • Animation Blueprint:

Event Graph:

Walk/Run (state):
Play rate = 1.0
Loop = True

AnimGraph:

  • Character Blueprint:
    The character blueprint has a parent class of “Custom Movement Character” which is a c++ class.

Event Graph

  • Custom Movement Character Class (The character parent class):

CustomMovementCharacter.cpp:

#include "CustomMovementCharacter.h"
#include "SprintCharacterMovementComponent.h"

ACustomMovementCharacter::ACustomMovementCharacter(const FObjectInitializer& ObjectInitializer)
	:Super(ObjectInitializer.SetDefaultSubobjectClass<USprintCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
{
	PrimaryActorTick.bCanEverTick = true;
	bReplicates = true;
	GetCharacterMovement()->SetIsReplicated(true);

}

void ACustomMovementCharacter::BeginPlay()
{
	Super::BeginPlay();
	
}

void ACustomMovementCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

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

}

CustomMovementCharacter.h:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CustomMovementCharacter.generated.h"

UCLASS()
class TERRORKIT_API ACustomMovementCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	ACustomMovementCharacter(const FObjectInitializer& ObjectInitializer);

protected:
	virtual void BeginPlay() override;

public:	
	virtual void Tick(float DeltaTime) override;
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};
  • Custom Character Movement Component (SprintCharacterMovementCompent):

SprintCharacterMovementComponent.cpp:

#include "SprintCharacterMovementComponent.h"

#include "GameFramework/Character.h"

#include "Net/UnrealNetwork.h"


USprintCharacterMovementComponent::USprintCharacterMovementComponent()
	: bWantsToSprint(false)
{
	MaxSprintSpeed = 525.0f;
	CustomWalkSpeed = 250.0f;
	bWantsToSprint = false;
	SetIsReplicatedByDefault(true);
}

void USprintCharacterMovementComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(USprintCharacterMovementComponent, bWantsToSprint);
}



void USprintCharacterMovementComponent::SetSprinting(bool Sprint)
{
	bWantsToSprint = Sprint;

}

void USprintCharacterMovementComponent::UpdateFromCompressedFlags(uint8 Flags)
{
	Super::UpdateFromCompressedFlags(Flags);

	bWantsToSprint = (Flags&FSavedMove_Character::FLAG_Custom_0) != 0;
}

FNetworkPredictionData_Client* USprintCharacterMovementComponent::GetPredictionData_Client() const
{
	if (!ClientPredictionData)
	{
		if (USprintCharacterMovementComponent* TempComponent = const_cast<USprintCharacterMovementComponent*>(this))
		{
			TempComponent->ClientPredictionData = new FSprintNetworkPredictionData_Client_Character(*this);
		}
	}
	return ClientPredictionData;
}

float USprintCharacterMovementComponent::GetMaxSpeed() const
{
	return bWantsToSprint ? MaxSprintSpeed : CustomWalkSpeed;
}

void FSprintSavedMove_Character::Clear()
{
	Super::Clear();
	bSavedWantsToSprint = false;
}

uint8 FSprintSavedMove_Character::GetCompressedFlags() const
{
	uint8 Result =  Super::GetCompressedFlags();

	if (bSavedWantsToSprint)
	{
		Result |= FLAG_Custom_0;
	}
	
	return Result;
}

void FSprintSavedMove_Character::SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel,
	class FNetworkPredictionData_Client_Character& ClientData)
{
	Super::SetMoveFor(C, InDeltaTime, NewAccel, ClientData);

	if (C)
	{
		if (USprintCharacterMovementComponent* MovementComponent = Cast<USprintCharacterMovementComponent>(C->GetMovementComponent()))
		{
			bSavedWantsToSprint = MovementComponent->bWantsToSprint;
		}
	}
}

void FSprintSavedMove_Character::PrepMoveFor(ACharacter* C)
{
	Super::PrepMoveFor(C);
	
	if (C)
	{
		if (USprintCharacterMovementComponent* MovementComponent = Cast<USprintCharacterMovementComponent>(C->GetMovementComponent()))
		{
			MovementComponent->bWantsToSprint = bSavedWantsToSprint;
		}
	}
}

bool FSprintSavedMove_Character::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter,
	float MaxDelta) const
{
	if (bSavedWantsToSprint != ((FSprintSavedMove_Character*)&NewMove)->bSavedWantsToSprint)
	{
		return false;
	}
	return Super::CanCombineWith(NewMove, InCharacter, MaxDelta);
}

FSavedMovePtr FSprintNetworkPredictionData_Client_Character::AllocateNewMove()
{
	return FSavedMovePtr(new FSprintSavedMove_Character());
}

SprintChracterMovementComponent.h:


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "SprintCharacterMovementComponent.generated.h"


UCLASS()

class TERRORKIT_API USprintCharacterMovementComponent : public UCharacterMovementComponent
{
	GENERATED_BODY()
public:
	USprintCharacterMovementComponent();
	virtual void UpdateFromCompressedFlags(uint8 Flags) override;
	virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
	virtual float GetMaxSpeed() const override;

	UPROPERTY(Replicated)
	bool bWantsToSprint;

	UPROPERTY(EditDefaultsOnly, Category = "MultiplayerMovement")
	float CustomWalkSpeed = 250.0f;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiplayerMovement")
	float MaxSprintSpeed = 525.0f;
	
	UFUNCTION(BlueprintCallable, Category = "MultiplayerMovement")
	void SetSprinting(bool Sprint);
};

class FSprintSavedMove_Character : public FSavedMove_Character
{
public:
	typedef FSavedMove_Character Super;

	uint8 bSavedWantsToSprint : 1;

	virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override;
	virtual void Clear() override;
	virtual uint8 GetCompressedFlags() const override;
	virtual void SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, class FNetworkPredictionData_Client_Character& ClientData) override;
	virtual void PrepMoveFor(ACharacter* C) override;
};



class FSprintNetworkPredictionData_Client_Character : public FNetworkPredictionData_Client_Character
{
public:
	typedef FNetworkPredictionData_Client_Character Super;
	FSprintNetworkPredictionData_Client_Character(const UCharacterMovementComponent& CharacterMovementComponent) : Super(CharacterMovementComponent){}
	virtual FSavedMovePtr AllocateNewMove() override;
};
  • Here’s a video of the problem:

    The server is on the right side of the screen and the client is on the left. You’ll notice that for the server everything is fine on the animations side of things, but for the client it’s fine for it’s self, but for others its not, it looks Slow-Mo.

    Sorry for the long post, this is the only thing standing in the way for me finishing up the sprinting system. Please help, thank you in advance.