I’m developing a dueling shooter, currently prototyping abilities, but I’m having some issues with the networking corrections. Im working on the dash(dodge) ability. Followed some tutorial on YouTube with the custom movement replicated correctly, but my implementation of the dash ability is a little different. My idea is this: when the player presses button the GroundFriction is set to 0 so nothing can stop player from dashing, and after some delay (currently 0.45) the GroundFriction returns to normal and the velocity drops to MaxWalkSpeed so the player doesn’t dash further than i expect event if you dash while falling. But I am sure that I receive network corrections in StopDash as it corrects excatly at the end of the dash. This is felt even more strongly when you dash while in the air or even change direction during a dash, and even more when spamming this ability.
Here are my network emulation settings:
Perhaps you have some articles to better understand why this isn’t working my way, or explain it yourself, I’d appreciate any help. Let me know if you need to know anything else.
Here is my implementation:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "SGCharacterMovementComponent.generated.h"
UCLASS()
class SHOOTERGAME_API USGCharacterMovementComponent : public UCharacterMovementComponent
{
GENERATED_UCLASS_BODY()
class FSavedMove_SGCharacter : public FSavedMove_Character
{
public:
typedef FSavedMove_Character Super;
virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* Character, float MaxDelta) const override;
virtual void Clear() override;
virtual uint8 GetCompressedFlags() const override;
virtual void SetMoveFor(ACharacter* Character, float InDeltaTime, FVector const& NewAccel,
FNetworkPredictionData_Client_Character& ClientData) override;
virtual void PrepMoveFor(ACharacter* Character) override;
uint8 bSavedWantsToDash : 1;
};
class FNetworkPredictionData_Client_SGCharacter : public FNetworkPredictionData_Client_Character
{
public:
FNetworkPredictionData_Client_SGCharacter(const UCharacterMovementComponent& ClientMovement);
typedef FNetworkPredictionData_Client_Character Super;
virtual FSavedMovePtr AllocateNewMove() override { return FSavedMovePtr(new FSavedMove_SGCharacter()); }
};
public:
virtual void UpdateFromCompressedFlags(uint8 Flags) override;
virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
virtual void OnMovementUpdated(float DeltaTime, const FVector& OldLocation, const FVector& OldVelocity) override;
UPROPERTY(EditDefaultsOnly)
float DashStrength;
UFUNCTION(BlueprintCallable)
void Dash();
UFUNCTION()
void StopDash();
uint8 bWantsToDash : 1;
};
#include "SGCharacterMovementComponent.h"
#include "GameFramework/Character.h"
#include "Kismet/KismetMathLibrary.h"
USGCharacterMovementComponent::USGCharacterMovementComponent(const FObjectInitializer& ObjectInitializer) : Super(
ObjectInitializer)
{
DashStrength = 1400.f;
bWantsToDash = false;
}
void USGCharacterMovementComponent::UpdateFromCompressedFlags(uint8 Flags)
{
Super::UpdateFromCompressedFlags(Flags);
bWantsToDash = (Flags & FSavedMove_Character::FLAG_Custom_0) != 0;
}
FNetworkPredictionData_Client* USGCharacterMovementComponent::GetPredictionData_Client() const
{
check(IsValid(PawnOwner));
// check(PawnOwner->GetLocalRole() < ROLE_Authority);
if (!ClientPredictionData)
{
USGCharacterMovementComponent* MutableThis = const_cast<USGCharacterMovementComponent*>(this);
MutableThis->ClientPredictionData = new FNetworkPredictionData_Client_SGCharacter(*this);
MutableThis->ClientPredictionData->MaxSmoothNetUpdateDist = 92.f;
MutableThis->ClientPredictionData->NoSmoothNetUpdateDist = 140.f;
}
return ClientPredictionData;
}
void USGCharacterMovementComponent::OnMovementUpdated(float DeltaTime, const FVector& OldLocation,
const FVector& OldVelocity)
{
Super::OnMovementUpdated(DeltaTime, OldLocation, OldVelocity);
if (!CharacterOwner)
{
return;
}
if (bWantsToDash)
{
bWantsToDash = false;
GroundFriction = 0.f;
FVector DodgeVelocity = UKismetMathLibrary::Normal(Velocity) * DashStrength;
DodgeVelocity.Z = 0.0f;
CharacterOwner->LaunchCharacter(DodgeVelocity, false, false);
FTimerHandle Handle;
GetWorld()->GetTimerManager().SetTimer(Handle, this, &USGCharacterMovementComponent::StopDash, .45f);
}
}
void USGCharacterMovementComponent::Dash()
{
bWantsToDash = true;
}
void USGCharacterMovementComponent::StopDash()
{
GroundFriction = 8.f;
Velocity = UKismetMathLibrary::Normal(Velocity) * MaxWalkSpeed;
}
bool USGCharacterMovementComponent::FSavedMove_SGCharacter::CanCombineWith(
const FSavedMovePtr& NewMove, ACharacter* Character, float MaxDelta) const
{
if (bSavedWantsToDash != ((FSavedMove_SGCharacter*)&NewMove)->bSavedWantsToDash)
{
return false;
}
return Super::CanCombineWith(NewMove, Character, MaxDelta);
}
void USGCharacterMovementComponent::FSavedMove_SGCharacter::Clear()
{
Super::Clear();
bSavedWantsToDash = false;
}
uint8 USGCharacterMovementComponent::FSavedMove_SGCharacter::GetCompressedFlags() const
{
uint8 Result = Super::GetCompressedFlags();
if (bSavedWantsToDash)
{
Result |= FLAG_Custom_0;
}
return Result;
}
void USGCharacterMovementComponent::FSavedMove_SGCharacter::SetMoveFor(ACharacter* Character, float InDeltaTime,
FVector const& NewAccel,
FNetworkPredictionData_Client_Character&
ClientData)
{
Super::SetMoveFor(Character, InDeltaTime, NewAccel, ClientData);
USGCharacterMovementComponent* CharacterMovement = Cast<USGCharacterMovementComponent>(
Character->GetCharacterMovement());
if (CharacterMovement)
{
bSavedWantsToDash = CharacterMovement->bWantsToDash;
}
}
void USGCharacterMovementComponent::FSavedMove_SGCharacter::PrepMoveFor(ACharacter* Character)
{
Super::PrepMoveFor(Character);
USGCharacterMovementComponent* CharacterMovement = Cast<USGCharacterMovementComponent>(
Character->GetCharacterMovement());
if (CharacterMovement)
{
CharacterMovement->bWantsToDash = bSavedWantsToDash;
}
}
USGCharacterMovementComponent::FNetworkPredictionData_Client_SGCharacter::FNetworkPredictionData_Client_SGCharacter(
const UCharacterMovementComponent& ClientMovement)
: Super(ClientMovement)
{
}