I’ve been stumbling on this issue for a while now, and I can’t find anything online to support the issue.
I have a c++ class which holds a sphere component, a spring arm and a camera. In multiplayer, when I spawn this pawn and possess it, the client does not receive the pawns camera and instead retains the player controller. As shown below:
(Server Left, Client Right)
If I swap out the pawn being spawned for a BP Pawn, or any other pawn for that matter, both the server and the client receive the correct camera.
I’ve no clue as to what could cause this, the player possession is exactly the same as the multiplayer template provided by epic.
Here is my header for the pawn:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "WRPlayer.generated.h"
class USpringArmComponent;
class UCameraComponent;
class USphereComponent;
class UWRPawnMovement;
class AWRCharacter;
UCLASS()
class WR_API AWRPlayer : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AWRPlayer();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera Movement")
UWRPawnMovement* PawnMovement;
// Called every frame
virtual void Tick(float DeltaTime) override;
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UCameraComponent* PlayerCam;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USphereComponent* CollisionSphere;
//Character Variables
AWRCharacter* Character;
//Default Camera Arm Length//
UPROPERTY(Category = "Movement Variables", BlueprintReadOnly)
float DefaultZoomLength;
UPROPERTY(Category = "Movement Variables", BlueprintReadOnly)
FRotator DefaultCameraRotation;
//replicated health variable
UPROPERTY()
int32 Health;
public:
//getters
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Getters")
FORCEINLINE class UCameraComponent* GetCamera() const { return PlayerCam; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Getters")
FORCEINLINE class USpringArmComponent* GetSpringArm() const { return SpringArm; }
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Getters")
float GetSpringArmLength();
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Getters")
FRotator GetCurrentRotation();
////setters
UFUNCTION(BlueprintCallable, Category = "Setters")
virtual void SetArmLength(float ChangeAmount);
UFUNCTION(BlueprintCallable, Category = "Setters")
virtual void SetArmRotation(FRotator ChangeAmount);
UFUNCTION(BlueprintCallable, Category = "Setters")
virtual void SetToDefaultZoom();
UFUNCTION(BlueprintCallable, Category = "Setters")
virtual void SetCharacterLocation(FVector WorldLocation);
};
CPP:
#include "WRPlayer.h"
#include "GameFramework/SpringArmComponent.h"
#include "Components/SphereComponent.h"
#include "Camera/CameraComponent.h"
#include "WRPawnMovement.h"
#include "WRCharacter.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
// Sets default values
AWRPlayer::AWRPlayer()
{
//Replicates
bReplicates = true;
//set defaults
DefaultZoomLength = 1300.0f;
DefaultCameraRotation = FRotator(-75.0, 0.0, 0.0);
//Set root component
CollisionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("Root Sphere Component"));
SetRootComponent(CollisionSphere);
CollisionSphere->InitSphereRadius(32.0f);
CollisionSphere->SetWorldScale3D(FVector(0.25, 0.25, 0.25));
//Default settings for inheriting control rotation
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
//create camera arm + attach to root
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Spring Arm Component"));
SpringArm->SetupAttachment(CollisionSphere);
SpringArm->TargetArmLength = DefaultZoomLength;
SpringArm->bUsePawnControlRotation = false;
SpringArm->SetUsingAbsoluteLocation(false);
SpringArm->SetRelativeRotation(DefaultCameraRotation);
SpringArm->bDoCollisionTest = false;
SpringArm->bEnableCameraLag = true;
SpringArm->bEnableCameraRotationLag = false;
SpringArm->bInheritPitch = false;
//create camera
PlayerCam = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera Component"));
PlayerCam->SetupAttachment(SpringArm);
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = true;
//attach movement component
PawnMovement = CreateDefaultSubobject<UWRPawnMovement>(TEXT("Camera Movement Component"));
}
// Called when the game starts or when spawned
void AWRPlayer::BeginPlay()
{
Super::BeginPlay();
//Character = Cast<AWRCharacter>(GetWorld()->SpawnActor(AWRCharacter::StaticClass()));
}
// Called every frame
void AWRPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
float AWRPlayer::GetSpringArmLength()
{
return SpringArm->TargetArmLength;
}
FRotator AWRPlayer::GetCurrentRotation()
{
return PlayerCam->GetRelativeRotation();
}
void AWRPlayer::SetArmLength(float ChangeAmount)
{
SpringArm->TargetArmLength += ChangeAmount;
}
void AWRPlayer::SetArmRotation(FRotator ChangeAmount)
{
//set min/max rotation amount
const FRotator RotationMax = FRotator(-25, 0.0, 0.0);
const FRotator RotationMin = DefaultCameraRotation;
//get rotation change
FRotator NewRotation =(SpringArm->GetRelativeRotation() + ChangeAmount);
//clamp pitch for new rotation
NewRotation = NewRotation.Pitch < RotationMin.Pitch ? RotationMin : NewRotation.Pitch < RotationMax.Pitch ? NewRotation : RotationMax;
//set new rotation
SpringArm->SetRelativeRotation(NewRotation);
}
void AWRPlayer::SetToDefaultZoom()
{
SpringArm->TargetArmLength = DefaultZoomLength;
SpringArm->SetRelativeRotation(DefaultCameraRotation);
}
void AWRPlayer::SetCharacterLocation(FVector WorldLocation)
{
Character->SetActorLocation(WorldLocation);
}