I have been working with the roller ball tutorial and I have been learning about networking by attempting to add movement replication to the balls so that it can be multiplayer.
I have also changed it so that the movement is not done through torque, but instead by simulating the rotation of the entire level (still needs a bit of work though).
I’ve got it kinda working, however there are a few problems:
When I test it with more than one player, it seems laggy, and I am unable to tell if it’s because of the network traffic is too much, or if my computer is losing FPS. So if someone knowledgeable in this area could tell me if I’m doing something unnecessary in my networking code I would be very grateful.
Another big problem is that if its run as a listen server (clarification because I don’t know exact terminology: Whenever the dedicated server is off, 1 player or otherwise.) then the host cannot move, but the rest can.
Video of singleplayer, dedicated server:
Video of 2 player, dedicated server:
Video of singleplayer, no dedicated server:
I apologize for the music in the videos, I always forget shadowplay captures it.
Relevant parts of SuperRollerRacerBall.h
public:
FRotator currentRotation = FRotator(0, 0, 0);
FVector movementForce = FVector(0, 0, 0);
float SpringArmYaw = 0.f;
float rotationScale = 30.0f;
FVector targetPosition;
protected:
/** Called for side to side input */
void MoveRight(float Val);
/** Called to move ball forwards and backwards */
void MoveForward(float Val);
/** Called to apply forces to ball*/
void ApplyMovementForce(FVector force, float rotationYaw);
UFUNCTION(Server, WithValidation, Reliable)
void Server_ApplyMovementForce(FVector force, float rotationYaw);
bool Server_ApplyMovementForce_Validate(FVector force, float rotationYaw);
void Server_ApplyMovementForce_Implementation(FVector force, float rotationYaw);
UFUNCTION(NetMulticast, Reliable)
void Multicast_InterpolatePhysicsPosition(FVector position, FVector velocity, FRotator rotation);
void Multicast_InterpolatePhysicsPosition_Implementation(FVector position, FVector velocity, FRotator rotation);
Relevant parts of SuperRollerRacerBall.cpp
void ASuperRollerRacerBall::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
if (!Ball->IsSimulatingPhysics()) {
Ball->SetSimulatePhysics(true);
}
//Lerp Spring Arm Camera
{
float lerpSpeed = 2.0f;
currentRotation.Yaw = Ball->GetPhysicsLinearVelocity().Rotation().Yaw;
FQuat targetRotation = FQuat(currentRotation + defaultSpringArmRotation);
SpringArm->RelativeRotation = FQuat::Slerp(SpringArm->RelativeRotation.Quaternion(), targetRotation, lerpSpeed * DeltaSeconds).Rotator();
SpringArmYaw = SpringArm->RelativeRotation.Yaw;
}
//Lerp position to server's
if (Role < ROLE_Authority) {
if (FVector::Dist(targetPosition, GetActorLocation()) > 50) {
SetActorLocation(FMath::VInterpTo(GetActorLocation(), targetPosition, DeltaSeconds, 4.0f));
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 2.0f, FColor::Green, FString::SanitizeFloat(FVector::Dist(targetPosition, GetActorLocation())));
}
}
ApplyMovementForce(movementForce, SpringArmYaw);
}
void ASuperRollerRacerBall::MoveRight(float Val)
{
movementForce.Y = FMath::Sin(FMath::DegreesToRadians(Val * -rotationScale)) * Ball->BodyInstance.MassScale * GetWorld()->GetGravityZ();
currentRotation.Roll = Val * -rotationScale;
}
void ASuperRollerRacerBall::MoveForward(float Val)
{
movementForce.X = FMath::Sin(FMath::DegreesToRadians(Val * -rotationScale)) * Ball->BodyInstance.MassScale * GetWorld()->GetGravityZ();
currentRotation.Pitch = Val * rotationScale;
}
void ASuperRollerRacerBall::ApplyMovementForce(FVector force, float rotationYaw)
{
if (Role < ROLE_Authority) {
FVector rotatedMovementForce = force.RotateAngleAxis(rotationYaw, FVector(0, 0, 1));
FVector xComp = FVector(rotatedMovementForce.X, 0, 0);
FVector yComp = FVector(0, rotatedMovementForce.Y, 0);
FVector normal = FVector::CrossProduct(xComp, yComp);
normal.Normalize();
float radians = FMath::Acos(FVector::DotProduct(normal, FVector::UpVector));
rotatedMovementForce.Z = FMath::Abs(FMath::Cos(FMath::DegreesToRadians(radians))) * Ball->BodyInstance.MassScale * GetWorld()->GetGravityZ() - GetWorld()->GetGravityZ();
Ball->AddForce(rotatedMovementForce, NAME_None, true);
Server_ApplyMovementForce(force, rotationYaw);
}
}
bool ASuperRollerRacerBall::Server_ApplyMovementForce_Validate(FVector force, float rotationYaw) {
return true;
}
void ASuperRollerRacerBall::Server_ApplyMovementForce_Implementation(FVector force, float rotationYaw) {
ApplyMovementForce(force, rotationYaw);
FVector rotatedMovementForce = force.RotateAngleAxis(rotationYaw, FVector(0, 0, 1));
FVector xComp = FVector(rotatedMovementForce.X, 0, 0);
FVector yComp = FVector(0, rotatedMovementForce.Y, 0);
FVector normal = FVector::CrossProduct(xComp, yComp);
normal.Normalize();
float radians = FMath::Acos(FVector::DotProduct(normal, FVector::UpVector));
rotatedMovementForce.Z = FMath::Abs(FMath::Cos(FMath::DegreesToRadians(radians))) * Ball->BodyInstance.MassScale * GetWorld()->GetGravityZ() - GetWorld()->GetGravityZ();
Ball->AddForce(rotatedMovementForce, NAME_None, true);
Multicast_InterpolatePhysicsPosition(GetActorLocation(), Ball->GetPhysicsLinearVelocity(), GetActorRotation());
}
void ASuperRollerRacerBall::Multicast_InterpolatePhysicsPosition_Implementation(FVector position, FVector velocity, FRotator rotation)
{
targetPosition = position;
//Ball->SetAllPhysicsLinearVelocity(velocity); //I don't know if this is necessary or not.
Ball->SetWorldRotation(rotation);
}