Creating a building that spawns units

For this recipe, we will create a building that spawns units at a fixed time interval at a particular location.

How to do it...

  1. Create a new Actor subclass in the editor, as always, and then add the following implementation to the class:
    UPROPERTY()
    UStaticMeshComponent* BuildingMesh;
    UPROPERTY()
    UParticleSystemComponent* SpawnPoint;
    
    UPROPERTY()
    UClass* UnitToSpawn;
    
    UPROPERTY()
    float SpawnInterval;
    
    UFUNCTION()
    void SpawnUnit();
    
    UFUNCTION()
    void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
    
    UPROPERTY()
    FTimerHandle SpawnTimerHandle;
  2. Add the following to the constructor:
    BuildingMesh = CreateDefaultSubobject<UStaticMeshComponent>("BuildingMesh");
    SpawnPoint = CreateDefaultSubobject<UParticleSystemComponent>("SpawnPoint");
    SpawnInterval = 10;
    auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
    if (MeshAsset.Object != nullptr)
    {
      BuildingMesh->SetStaticMesh(MeshAsset.Object);
      BuildingMesh->SetCollisionProfileName(UCollisionProfile::Pawn_ProfileName);
    
    }
    auto ParticleSystem =
    ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("ParticleSystem'/Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem'"));
    if (ParticleSystem.Object != nullptr)
    {
      SpawnPoint->SetTemplate(ParticleSystem.Object);
    }
    SpawnPoint->SetRelativeScale3D(FVector(0.5, 0.5, 0.5));
    UnitToSpawn = ABarracksUnit::StaticClass();
  3. Add the following to the BeginPlay function:
    RootComponent = BuildingMesh;
    SpawnPoint->AttachTo(RootComponent);
    SpawnPoint->SetRelativeLocation(FVector(150, 0, 0));
    GetWorld()->GetTimerManager().SetTimer(SpawnTimerHandle, this, &ABarracks::SpawnUnit, SpawnInterval, true);
  4. Create the implementation for the SpawnUnit function:
    void ABarracks::SpawnUnit()
    {
      FVector SpawnLocation = SpawnPoint->GetComponentLocation();
      GetWorld()->SpawnActor(UnitToSpawn, &SpawnLocation);
    }
  5. Implement the overridden EndPlay function:
    void ABarracks::EndPlay(const EEndPlayReason::Type EndPlayReason)
    {
      Super::EndPlay(EndPlayReason);
      GetWorld()->GetTimerManager().ClearTimer(SpawnTimerHandle);
    }
  6. Next, create a new character subclass, and add one property:
    UPROPERTY()
    UParticleSystemComponent* VisualRepresentation;
  7. Initialize the component in the constructor implementation:
    VisualRepresentation = CreateDefaultSubobject<UParticleSystemComponent>("SpawnPoint");
    auto ParticleSystem =ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("ParticleSystem'/Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem'"));
    if (ParticleSystem.Object != nullptr)
    {
      SpawnPoint->SetTemplate(ParticleSystem.Object);
    }
    SpawnPoint->SetRelativeScale3D(FVector(0.5, 0.5, 0.5));
    SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
  8. Attach the visual representation to the root component:
    void ABarracksUnit::BeginPlay()
    {
      Super::BeginPlay();
      SpawnPoint->AttachTo(RootComponent);
    }
  9. Lastly, add the following to the Tick function to get the spawned actor moving:
    SetActorLocation(GetActorLocation() + FVector(10, 0, 0));
  10. Verify against the following snippet, then compile your project. Place a copy of the barracks actor into the level. You can then observe it spawning the character at fixed intervals:
    #pragma once
    #include "GameFramework/Actor.h"
    #include "Barracks.generated.h"
    UCLASS()
    class UE4COOKBOOK_API ABarracks : public AActor
    {
      GENERATED_BODY()
      public:
      ABarracks();
      virtual void BeginPlay() override;
      virtual void Tick( float DeltaSeconds ) override;
    
      UPROPERTY()
      UStaticMeshComponent* BuildingMesh;
      UPROPERTY()
      UParticleSystemComponent* SpawnPoint;
    
      UPROPERTY()
      UClass* UnitToSpawn;
    
      UPROPERTY()
      float SpawnInterval;
    
      UFUNCTION()
      void SpawnUnit();
      UFUNCTION()
      void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
    
      UPROPERTY()
      FTimerHandle SpawnTimerHandle;
    };
    
    #include "UE4Cookbook.h"
    #include "BarracksUnit.h"
    #include "Barracks.h"
    
    // Sets default values
    ABarracks::ABarracks()
    {
      // 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;
      BuildingMesh = CreateDefaultSubobject<UStaticMeshComponent>("BuildingMesh");
      SpawnPoint = CreateDefaultSubobject<UParticleSystemComponent>("SpawnPoint");
      SpawnInterval = 10;
      auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
      if (MeshAsset.Object != nullptr)
      {
        BuildingMesh->SetStaticMesh(MeshAsset.Object);
        BuildingMesh->SetCollisionProfileName(UCollisionProfile::Pawn_ProfileName);
    
      }
      auto ParticleSystem = ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("ParticleSystem'/Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem'"));
      if (ParticleSystem.Object != nullptr)
      {
        SpawnPoint->SetTemplate(ParticleSystem.Object);
      }
      SpawnPoint->SetRelativeScale3D(FVector(0.5, 0.5, 0.5));
      UnitToSpawn = ABarracksUnit::StaticClass();
    }
    void ABarracks::BeginPlay()
    {
      Super::BeginPlay();
      RootComponent = BuildingMesh;
      SpawnPoint->AttachTo(RootComponent);
      SpawnPoint->SetRelativeLocation(FVector(150, 0, 0));
      GetWorld()->GetTimerManager().SetTimer(SpawnTimerHandle, this, &ABarracks::SpawnUnit, SpawnInterval, true);
    }
    
    void ABarracks::Tick( float DeltaTime )
    {
      Super::Tick( DeltaTime );
    }
    void ABarracks::SpawnUnit()
    {
      FVector SpawnLocation = SpawnPoint->GetComponentLocation();
      GetWorld()->SpawnActor(UnitToSpawn, &SpawnLocation);
    }
    
    void ABarracks::EndPlay(const EEndPlayReason::Type EndPlayReason)
    {
      Super::EndPlay(EndPlayReason);
      GetWorld()->GetTimerManager().ClearTimer(SpawnTimerHandle);
    }
    
    #pragma once
    
    #include "GameFramework/Character.h"
    #include "BarracksUnit.generated.h"
    
    UCLASS()
    class UE4COOKBOOK_API ABarracksUnit : public ACharacter
    {
      GENERATED_BODY()
    
      public:
      ABarracksUnit();
    
      virtual void BeginPlay() override;
      virtual void Tick( float DeltaSeconds ) override;
    
      virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
    
      UPROPERTY()
      UParticleSystemComponent* SpawnPoint;
    };
    
    #include "UE4Cookbook.h"
    #include "BarracksUnit.h"
    
    ABarracksUnit::ABarracksUnit()
    {
      PrimaryActorTick.bCanEverTick = true;
      SpawnPoint = CreateDefaultSubobject<UParticleSystemComponent>("SpawnPoint");
      auto ParticleSystem =ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("ParticleSystem'/Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem'"));
      if (ParticleSystem.Object != nullptr)
      {
        SpawnPoint->SetTemplate(ParticleSystem.Object);
      }
      SpawnPoint->SetRelativeScale3D(FVector(0.5, 0.5, 0.5));
      SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    }
    void ABarracksUnit::BeginPlay()
    {
      Super::BeginPlay();
      SpawnPoint->AttachTo(RootComponent);
    }
    
    void ABarracksUnit::Tick( float DeltaTime )
    {
      Super::Tick( DeltaTime );
      SetActorLocation(GetActorLocation() + FVector(10, 0, 0));
    }
    void ABarracksUnit::SetupPlayerInputComponent(class UInputComponent* InputComponent)
    {
      Super::SetupPlayerInputComponent(InputComponent);
    }

How it works...

  1. Firstly, we create the barracks actor. We add a particle system component to indicate where the new units will be spawning, and a static mesh for the visual representation of the building.
  2. In the constructor, we initialize the components, and then set their values using FObjectFinder. We also set the class to spawn using the StaticClass function to retrieve a UClass* instance from a class type.
  3. In the BeginPlay function of the barracks, we create a timer that calls our SpawnUnit function at fixed intervals. We store the timer handle in a member variable in the class so that when our instance is being destroyed, we can halt the timer; otherwise, when the timer triggers again, we'll encounter a crash where the object pointer is dereferenced.
  4. The SpawnUnit function gets the world space location of the SpawnPoint object, then asks the world to spawn an instance of our unit class at that location.
  5. BarracksUnit has code in its Tick() function to move forward by 10 units every frame so that each spawned unit will move to make room for the next one.
  6. The EndPlay function override calls the parent class implementation of the function, which is important if there are timers to cancel or deinitialization performed in the parent class. It then uses the timer handle stored in BeginPlay in order to cancel the timer.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.86.121