For this recipe, we will create a building that spawns units at a fixed time interval at a particular location.
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;
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();
BeginPlay
function:RootComponent = BuildingMesh; SpawnPoint->AttachTo(RootComponent); SpawnPoint->SetRelativeLocation(FVector(150, 0, 0)); GetWorld()->GetTimerManager().SetTimer(SpawnTimerHandle, this, &ABarracks::SpawnUnit, SpawnInterval, true);
SpawnUnit
function:void ABarracks::SpawnUnit() { FVector SpawnLocation = SpawnPoint->GetComponentLocation(); GetWorld()->SpawnActor(UnitToSpawn, &SpawnLocation); }
EndPlay
function:void ABarracks::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); GetWorld()->GetTimerManager().ClearTimer(SpawnTimerHandle); }
UPROPERTY() UParticleSystemComponent* VisualRepresentation;
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;
void ABarracksUnit::BeginPlay() { Super::BeginPlay(); SpawnPoint->AttachTo(RootComponent); }
Tick
function to get the spawned actor moving:SetActorLocation(GetActorLocation() + FVector(10, 0, 0));
#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); }
FObjectFinder
. We also set the class to spawn using the StaticClass
function to retrieve a UClass*
instance from a class type.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.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.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.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.3.144.86.121