Right about the time I started thinking I was getting the hang of Unreal and replication in general, I ran into an issue that I have not been able to overcome.
When spawning a new actor, I’m unable to get replication to the clients. I’ve spent a lot of time on this and just can’t figure it out.
PickupBase - this class is the base class for all future pickup classes in my game
RiflePickup - this class is a weapon pickup class derived from PickupBase
ShooterCPPGameMode - this is my Game Mode class and where I am spawning my pickups for now.
The spawning of my weapon pickups (RiflePickup) is working fine on the server, but it won’t replicate to the clients.
APickupBase.h
UENUM(BlueprintType)
enum class EWeapon : uint8
{
EW_AK47 UMETA(DisplayName = "AK-47"),
EW_AR15 UMETA(DisplayName = "AR-15"),
EW_SMG11 UMETA(DisplayName = "SMG-11"),
EW_ASVAL UMETA(DisplayName = "AS VAL"),
EW_SUB UMETA(DisplayName = "Sub")
};
UCLASS()
class SHOOTERCPP_API APickupBase : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
APickupBase();
virtual void Initialize(EWeapon weapon);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(Replicated)
UStaticMeshComponent* weaponMeshComponent;
UPROPERTY(Replicated)
UBoxComponent* boxComponent;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
};
APickupBase.cpp
#include "PickupBase.h"
#include "ConstructorHelpers.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "UnrealNetwork.h"
// Sets default values
APickupBase::APickupBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
bReplicates = true;
bReplicateMovement = true;
// Every weapon in the game will have a box collider, static mesh and an actual type to spawn for use
boxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Box Comp"));
boxComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
weaponMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Weapon Mesh Comp"));
weaponMeshComponent->SetupAttachment(boxComponent);
boxComponent->SetWorldScale3D(FVector(1.f, 1.0f, 1.0f));
boxComponent->SetBoxExtent(FVector(60.f, 32.f, 32.f));
}
void APickupBase::Initialize(EWeapon weapon)
{
// nothing to do here in the base class
}
// Called when the game starts or when spawned
void APickupBase::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void APickupBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void APickupBase::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(APickupBase, weaponMeshComponent);
DOREPLIFETIME(APickupBase, boxComponent);
}
ARiflePickup.h (notice this is derived from APickupBase)
#include "CoreMinimal.h"
#include "PickupBase.h"
#include "RiflePickup.generated.h"
/**
*
*/
UCLASS()
class SHOOTERCPP_API ARiflePickup : public APickupBase
{
GENERATED_BODY()
public:
ARiflePickup();
virtual void Initialize(EWeapon weapon) override;
protected:
// Weapon meshes
UStaticMesh* ak47Mesh;
UStaticMesh* ar15Mesh;
UStaticMesh* kaValMesh;
UStaticMesh* smg11Mesh;
UStaticMesh* ak47uMesh;
};
ARiflePickup.cpp (notice this is derived from APickupBase)
#include "RiflePickup.h"
#include "ConstructorHelpers.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "UnrealNetwork.h"
ARiflePickup::ARiflePickup()
{
bReplicates = true;
bReplicateMovement = true;
static ConstructorHelpers::FObjectFinder<UStaticMesh> ak47Asset(TEXT("/Game/FPS_Weapon_Bundle/Weapons/Meshes/Ka47/SM_KA47_X"));
ak47Mesh = ak47Asset.Object;
static ConstructorHelpers::FObjectFinder<UStaticMesh> ar15Asset(TEXT("/Game/FPS_Weapon_Bundle/Weapons/Meshes/AR4/SM_AR4_X"));
ar15Mesh = ar15Asset.Object;
static ConstructorHelpers::FObjectFinder<UStaticMesh> kaValAsset(TEXT("/Game/FPS_Weapon_Bundle/Weapons/Meshes/Ka_Val/SM_KA_Val_X"));
kaValMesh = kaValAsset.Object;
static ConstructorHelpers::FObjectFinder<UStaticMesh> smg11Asset(TEXT("/Game/FPS_Weapon_Bundle/Weapons/Meshes/SMG11/SM_SMG11_X"));
smg11Mesh = smg11Asset.Object;
static ConstructorHelpers::FObjectFinder<UStaticMesh> ak47uAsset(TEXT("/Game/FPS_Weapon_Bundle/Weapons/Meshes/KA74U/SM_KA74U_X"));
ak47uMesh = ak47uAsset.Object;
}
void ARiflePickup::Initialize(EWeapon weapon)
{
FVector meshOffset;
UStaticMesh* weaponMesh;
switch (weapon)
{
case EWeapon::EW_AK47:
{
weaponMesh = ak47Mesh;
meshOffset = FVector(-15.f, 0.f, 0.f);
break;
}
case EWeapon::EW_AR15:
{
weaponMesh = ar15Mesh;
meshOffset = FVector(-15.f, 0.f, 0.f);
break;
}
case EWeapon::EW_ASVAL:
{
weaponMesh = kaValMesh;
meshOffset = FVector(-15.f, 0.f, 0.f);
break;
}
case EWeapon::EW_SMG11:
{
weaponMesh = smg11Mesh;
meshOffset = FVector(-15.f, 0.f, 0.f);
break;
}
case EWeapon::EW_SUB:
{
weaponMesh = ak47uMesh;
meshOffset = FVector(-15.f, 0.f, 0.f);
break;
}
default:
{
weaponMesh = ak47Mesh;
meshOffset = FVector(-15.f, 0.f, 0.f);
break;
}
}
if (weaponMeshComponent)
{
weaponMeshComponent->SetStaticMesh(weaponMesh);
weaponMeshComponent->SetRelativeLocation(FVector(ForceInitToZero));
weaponMeshComponent->SetRelativeLocation(meshOffset);
weaponMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
AShooterCPPGameMode.h
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "PickupBase.h"
#include "ShooterCPPGameMode.generated.h"
UCLASS(minimalapi)
class AShooterCPPGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AShooterCPPGameMode();
virtual void BeginPlay() override;
protected:
UFUNCTION(Server, Reliable, WithValidation)
void SpawnWeaponToWorld(EWeapon weapon, FVector location, FRotator rotation);
};
AShooterCPPGameMode.cpp
#include "ShooterCPPGameMode.h"
#include "ShooterCPPCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "PickupBase.h"
#include "RiflePickup.h"
#include "PickupBase.h"
#include "Engine/World.h"
AShooterCPPGameMode::AShooterCPPGameMode()
{
// set default pawn class to our Blueprinted character
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
if (PlayerPawnBPClass.Class != NULL)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
}
void AShooterCPPGameMode::BeginPlay()
{
Super::BeginPlay();
UWorld* world = GetWorld();
if (world)
{
FVector location = FVector(-1000.f, -1000.f, 200.f);
FRotator rotation = FRotator::ZeroRotator;
// Spawning a single weapon to the works works on the server, but is not displayed on the client
SpawnWeaponToWorld(EWeapon::EW_AK47, location, rotation);
ARiflePickup* temp = (ARiflePickup*) GetWorld()->SpawnActor<APickupBase>(ARiflePickup::StaticClass(), location, rotation);
temp->Initialize(EWeapon::EW_AK47);
}
}
bool AShooterCPPGameMode::SpawnWeaponToWorld_Validate(EWeapon weapon, FVector location, FRotator rotation)
{
return true;
}
void AShooterCPPGameMode::SpawnWeaponToWorld_Implementation(EWeapon weapon, FVector location, FRotator rotation)
{
ARiflePickup* temp = (ARiflePickup*)GetWorld()->SpawnActor<APickupBase>(ARiflePickup::StaticClass(), location, rotation);
bool test = temp->HasAuthority();
temp->Initialize(EWeapon::EW_AK47);
}
So, in my GameMode class, I’m spawning a single pickup. It works perfectly on the server, but will not propigate to the client.
Can someone please tell me what I’m doing wrong?
Thanks in advance!