Hello folks!
I am on this topic for a while now: Correctly networking our gameplay, which uses physics based movement. I started out creating my basic multiplayer setup, including an online testing environment using Steam, then tried to fit our game to work in a networked setup.
The game: Basically airhockey, where the player controls a paddle and needs to hit the puck to hit something with it.
My problem: I ended up with synchronization issues between the clients, meaning that the puck or even the other player is not at the same position in both clients.
How I am currently doing the movement:
I have my own MovementComponent derived from UPawnMovementComponent, which just adds a force to my paddle
void UPaddlePawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
{
return;
}
FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * MovementSpeed;
if (!DesiredMovementThisFrame.IsNearlyZero())
{
GetOwner()->FindComponentByClass<UPrimitiveComponent>()->AddForce(DesiredMovementThisFrame);
}
};
One step higher: I am adding to the InputVector in my own Pawn class, which is declared like this:
class MMUNREAL_GIT_API APaddlePawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
APaddlePawn();
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
// Called to get this pawn's movement component
virtual UPawnMovementComponent* GetMovementComponent() const override;
UFUNCTION()
void MoveForward(float AxisValue);
UFUNCTION()
void MoveRight(float AxisValue);
UFUNCTION(Server, Reliable, WithValidation)
void ServerMoveForward(float AxisValue);
UFUNCTION(Server, Reliable, WithValidation)
void ServerMoveRight(float AxisValue);
};
… and implemented like this:
// Called to bind functionality to input
void APaddlePawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
Super::SetupPlayerInputComponent(InputComponent);
InputComponent->BindAxis("MoveForward", this, &APaddlePawn::MoveForward);
InputComponent->BindAxis("MoveRight", this, &APaddlePawn::MoveRight);
}
UPawnMovementComponent* APaddlePawn::GetMovementComponent() const
{
return FindComponentByClass<UPawnMovementComponent>();
}
void APaddlePawn::MoveForward(float AxisValue)
{
if (!Controller || AxisValue == 0.0f) return;
if (IsLocallyControlled()) {
ServerMoveForward(AxisValue);
}
}
void APaddlePawn::MoveRight(float AxisValue)
{
if (!Controller || AxisValue == 0.0f) return;
if (IsLocallyControlled()) {
ServerMoveRight(AxisValue);
}
}
void APaddlePawn::ServerMoveForward_Implementation(float AxisValue) {
if (GetMovementComponent())
{
GetMovementComponent()->AddInputVector(GetActorForwardVector() * AxisValue);
}
}
bool APaddlePawn::ServerMoveForward_Validate(float AxisValue) {
return true;
}
void APaddlePawn::ServerMoveRight_Implementation(float AxisValue) {
if (GetMovementComponent())
{
GetMovementComponent()->AddInputVector(GetActorRightVector() * AxisValue);
}
}
bool APaddlePawn::ServerMoveRight_Validate(float AxisValue) {
return true;
}
I have set the replication settings in my Paddle-Blueprint & Puck-Blueprint like this: (Always relevant = true, Replicate Movement = true, Net Load on Client = true and of course Replicates = true)
With this setup, at first everything appears to work fine, but after playing a while the clients get out of sync (= player paddles as well as puck are at different locations)
If anybody might see something wrong with the code or architecture I’d really appreciate feedback! Currently I’m just really stuck. Have been trying different approaches, researching on the web, but nothing really seems to work.
Thank you!