Announcement

Collapse
No announcement yet.

Replication Hell - the gift that keeps on giving

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    #16
    Here is the working and much simplified code. A few things of note:

    The RiflePickup class is basically empty now. All the default settings (collider and mesh) are still in the base class and I have no special features written yet for the rifle specifically. What is not shown below are 4 blueprint classes - one for each weapon in my game. This is where I set the default mesh and collider. In the GameMode class, I then spawn each BP and since the base class is marked for replication, it just works.

    PickupBase.h
    Code:
    UCLASS()
    class SHOOTERCPP_API APickupBase : public AActor
    {
        GENERATED_BODY()
    
    public:    
        // Sets default values for this actor's properties
        APickupBase();
    
        // the item to be spawned from this pickup
        UPROPERTY(EditDefaultsOnly, Category = WeaponToSpawn)
        TSubclassOf<AActualBase> weaponToSpawn;    
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;    
    
        UPROPERTY(BlueprintReadWrite, VisibleAnywhere)
        UStaticMeshComponent* weaponMeshComponent;
    
        UPROPERTY(BlueprintReadWrite, VisibleAnywhere)
        UBoxComponent* boxComponent;    
    
    public:    
        // Called every frame
        virtual void Tick(float DeltaTime) override;
    
    private:    
    
    };
    PickupBase.cpp
    Code:
    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 pickup in the game will have a box collider, static mesh and an actual type to spawn after pickup (set in header)
        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));        
    }
    
    // Called when the game starts or when spawned
    void APickupBase::BeginPlay()
    {
        Super::BeginPlay();
    }
    
    // Called every frame
    void APickupBase::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);
    }
    RiflePickup.h
    Code:
    UCLASS()
    class SHOOTERCPP_API ARiflePickup : public APickupBase
    {
        GENERATED_BODY()
    
    public:
        ARiflePickup();
    
    
    protected:    
    
    };
    RiflePickup.cpp
    Code:
    ARiflePickup::ARiflePickup()
    {
        bReplicates = true;
        bReplicateMovement = true;        
    }
    ShooterCPPGameMode.h
    Code:
    UCLASS(minimalapi)
    class AShooterCPPGameMode : public AGameModeBase
    {
        GENERATED_BODY()
    
    public:
        AShooterCPPGameMode();
        virtual void BeginPlay() override;
    
    protected:
    
        TSubclassOf<ARiflePickup> weaponAK47;
        TSubclassOf<ARiflePickup> weaponAR15;
        TSubclassOf<ARiflePickup> weaponSMG11;
        TSubclassOf<ARiflePickup> weaponSUB;
    };
    ShooterCPPGameMode.cpp
    Code:
    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;        
        }    
    
        static ConstructorHelpers::FClassFinder<ARiflePickup> AK47(TEXT("/Game/ThirdPersonCPP/Blueprints/WeaponPickup/Rifle_Pickup_AK47"));
        static ConstructorHelpers::FClassFinder<ARiflePickup> AR15(TEXT("/Game/ThirdPersonCPP/Blueprints/WeaponPickup/Rifle_Pickup_AR15"));
        static ConstructorHelpers::FClassFinder<ARiflePickup> SMG11(TEXT("/Game/ThirdPersonCPP/Blueprints/WeaponPickup/Rifle_Pickup_SMG11"));
        static ConstructorHelpers::FClassFinder<ARiflePickup> SUB(TEXT("/Game/ThirdPersonCPP/Blueprints/WeaponPickup/Rifle_Pickup_SUB"));    
    
        weaponAK47 = AK47.Class;
        weaponAR15 = AR15.Class;
        weaponSMG11 = SMG11.Class;
        weaponSUB = SUB.Class;    
    }
    
    void AShooterCPPGameMode::BeginPlay()
    {
        Super::BeginPlay();
    
        if (GetWorld())
        {
            FVector location = FVector(-1000.f, -1000.f, 200.f);
            FRotator rotation = FRotator::ZeroRotator;        
    
            ARiflePickup* temp = GetWorld()->SpawnActor<ARiflePickup>(weaponAK47, location, rotation);
    
            location += FVector(0.0f, 200.f, 0.f);
            ARiflePickup* temp2 = GetWorld()->SpawnActor<ARiflePickup>(weaponAR15, location, rotation);
    
            location += FVector(0.0f, 200.f, 0.f);
            ARiflePickup* temp3 = GetWorld()->SpawnActor<ARiflePickup>(weaponSMG11, location, rotation);
    
            location += FVector(0.0f, 200.f, 0.f);
            ARiflePickup* temp4 = GetWorld()->SpawnActor<ARiflePickup>(weaponSUB, location, rotation);        
    
        }
    }

    Comment


      #17
      Is this really necessary or you are just playing safe? Shouldn't you be calling super thingy!
      Originally posted by CashCache View Post
      Code:
      ARiflePickup::ARiflePickup()
      {
      bReplicates = true;
      bReplicateMovement = true;
      }

      Furthermore, you don't need to set the replication properties client side actually.
      Last edited by The-Cowboy; 06-03-2019, 10:19 PM.

      Comment


        #18
        Is this really necessary or you are just playing safe? Shouldn't you be calling super thingy!
        This is not needed and I have since removed it. As long as it's set in the base class it should work.

        As for calling the Base/Master/Super class, I don't need to explicitly call it since I'm using the default constructor. If I add a new constructor, or add parameters to the existing constructor, I would then need to explicitly call the super.


        Furthermore, you don't need to set the replication properties client side actually.
        I don't understand? I'm setting this here so the derived blueprint is already set by default. Is that what you are asking or am I missing something?

        Comment


          #19
          Originally posted by CashCache View Post
          Thanks for the detailed response - you got me on the right track and I learned a lot while doing it.
          No worries, hope it helps If in doubt, study the content examples Epic have for C++ - they're quite good (if a little dated now!)

          Comment


            #20
            Originally posted by CashCache View Post
            I don't understand? I'm setting this here so the derived blueprint is already set by default. Is that what you are asking or am I missing something?
            I would use the Epic's routine SetReplicates(true) which has a special check for the Authority role. That way this setting doesn't get applied to the clients!

            I mean I trust
            Code:
            void AActor::SetReplicates(bool bInReplicates)
            {
                if (Role == ROLE_Authority)
                {
                    // Only call into net driver if we actually changed
                    if (bReplicates != bInReplicates)
                    {
                        // Update our settings before calling into net driver
                        RemoteRole = bInReplicates ? ROLE_SimulatedProxy : ROLE_None;
                        bReplicates = bInReplicates;
            
                        // This actor should already be in the Network Actors List if it was already replicating.
                        if (bReplicates)
                        {
                            // GetWorld will return nullptr on CDO, FYI
                            if (UWorld* MyWorld = GetWorld())        
                            {
                                MyWorld->AddNetworkActor(this);
                                ForcePropertyCompare();
                            }
                        }
            
                    }
                }
                else
                {
                    UE_LOG(LogActor, Warning, TEXT("SetReplicates called on actor '%s' that is not valid for having its role modified."), *GetName());
                }
            }
            and not reinventing the wheel (giving joys of OOP)!
            Last edited by The-Cowboy; 06-04-2019, 05:28 AM.

            Comment

            Working...
            X