Spawning Actors in Unreal Engine with C++
Spawning actors is definitly a great tool to have in have the toolbox. In this example we'll spawn a few different types of actors using C++.
Software Versions: Unreal Engine 5.5.1 | Rider 2024.3.2
Project Name: MyProject
This post will walk through spawning three different actors in C++. We'll spawn a physics actors, projectile actors, and a deferred particle actor via an overlap event. Watch the video above for the final demo. Typically it's recommend to clone the GD Tactics Unreal Project for a more complete understanding of the process.
I'm going to start by creating some simple classes to setup my demo environment. Click to jump ahead to the spawning logic. I'm going to start by creating the spawn interface, then the trigger actor, followed by the spawn actor classes, and then the spawning logic will bring it all together.
I started by creating a simple interface for actors to implement to listen for spawn events. The .cpp
file is empty. I go more in depth with interfaces in this create an interface post.
MySpawnActorInterface.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MySpawnActorInterface.generated.h"
UINTERFACE()
class UMySpawnActorInterface : public UInterface
{
GENERATED_BODY()
};
class MYPROJECT_API IMySpawnActorInterface
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "MySpawnActorInterface")
void SpawnActor();
};
Next, I created a trigger actor that on overlap will Execute_SpawnActor
every second 10 times. I discuss more about timers in the timers post. The actor has simple logic, but when dragged into the world it will need an actor connected to its MySpawnedActor
property, we'll create the MySpawnActor
class later on.
MySpawnActorTrigger.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySpawnActorTrigger.generated.h"
class AMySpawnActor;
class UBoxComponent;
UCLASS()
class MYPROJECT_API AMySpawnActorTrigger : public AActor
{
GENERATED_BODY()
public:
AMySpawnActorTrigger();
UPROPERTY(EditAnywhere)
USceneComponent* MyRoot;
UPROPERTY(EditAnywhere)
UBoxComponent* MyBoxComponent;
UPROPERTY(EditAnywhere)
AMySpawnActor* MySpawnedActor;
int32 SpawnCounter = 0;
int32 MaxSpawnCount = 10;
UPROPERTY()
FTimerHandle TimerHandle;
UFUNCTION(BlueprintCallable)
void StartTimer();
UFUNCTION(BlueprintCallable)
void MyTimerFunction();
UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};
MySpawnActorTrigger.cpp
#include "MySpawnActorTrigger.h"
#include "Components/BoxComponent.h"
#include "MySpawnActorInterface.h"
#include "MySpawnActor.h"
AMySpawnActorTrigger::AMySpawnActorTrigger()
{
MyRoot = CreateDefaultSubobject<USceneComponent>(TEXT("MyRoot"));
MyRoot->bVisualizeComponent = true;
RootComponent = MyRoot;
MyBoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("MyBoxComponent"));
MyBoxComponent->SetupAttachment(RootComponent);
MyBoxComponent->OnComponentBeginOverlap.AddDynamic(this, &AMySpawnActorTrigger::OnBeginOverlap);
}
void AMySpawnActorTrigger::StartTimer()
{
GetWorldTimerManager().SetTimer(TimerHandle, this, &AMySpawnActorTrigger::MyTimerFunction, 1.0f, true, 0.f);
}
void AMySpawnActorTrigger::MyTimerFunction()
{
if (SpawnCounter < MaxSpawnCount)
{
SpawnCounter++;
IMySpawnActorInterface::Execute_SpawnActor(MySpawnedActor);
}
else
{
GetWorldTimerManager().ClearTimer(TimerHandle);
MyBoxComponent->OnComponentBeginOverlap.RemoveAll(this);
}
}
void AMySpawnActorTrigger::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor != nullptr && OtherActor != this)
{
if (IsValid(MySpawnedActor) && MySpawnedActor->Implements<UMySpawnActorInterface>())
{
StartTimer();
}
}
}
For this example I want spawn three different types of actors. We'll need a physics actor, projectile actor, and a particle actor. The physics actor is below, short and sweet.
MySpawnActorPhysics.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySpawnActorPhysics.generated.h"
UCLASS()
class MYPROJECT_API AMySpawnActorPhysics : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMySpawnActorPhysics();
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UStaticMeshComponent* MyStaticMeshComponent;
};
MySpawnActorPhysics.cpp
#include "MySpawnActorPhysics.h"
AMySpawnActorPhysics::AMySpawnActorPhysics()
{
MyStaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyStaticMeshComponent"));
MyStaticMeshComponent->SetSimulatePhysics(true);
RootComponent = MyStaticMeshComponent;
}
Next is the projectile actor. This actor is very similar to the first person project template's projectile actor. This projectile actor will resemble a missile and not be affected gravity. I added a simple accelration function that increase's the projectile speed every 0.1f
seconds.
MySpawnActorProjectile.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySpawnActorProjectile.generated.h"
class UBoxComponent;
class UProjectileMovementComponent;
UCLASS()
class MYPROJECT_API AMySpawnActorProjectile : public AActor
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
public:
// Sets default values for this actor's properties
AMySpawnActorProjectile();
UPROPERTY(EditAnywhere)
UBoxComponent* MyCollisionComp;
UPROPERTY(EditAnywhere)
UProjectileMovementComponent* MyMovementComp;
UFUNCTION()
void Accelerate();
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
};
MySpawnActorProjectile.cpp
#include "MySpawnActorProjectile.h"
#include "Components/BoxComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
AMySpawnActorProjectile::AMySpawnActorProjectile()
{
MyCollisionComp = CreateDefaultSubobject<UBoxComponent>(TEXT("MyCollisionComp"));
MyCollisionComp->SetBoxExtent(FVector(100.0f, 100.0f, 100.0f));
MyCollisionComp->BodyInstance.SetCollisionProfileName("Projectile");
MyCollisionComp->OnComponentHit.AddDynamic(this, &AMySpawnActorProjectile::OnHit);
MyCollisionComp->SetWalkableSlopeOverride(FWalkableSlopeOverride(WalkableSlope_Unwalkable, 0.f));
MyCollisionComp->CanCharacterStepUpOn = ECB_No;
RootComponent = MyCollisionComp;
MyMovementComp = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));
MyMovementComp->UpdatedComponent = MyCollisionComp;
MyMovementComp->InitialSpeed = 50.f;
MyMovementComp->MaxSpeed = 5000.f;
MyMovementComp->ProjectileGravityScale = 0.f;
InitialLifeSpan = 6.0f;
}
void AMySpawnActorProjectile::BeginPlay()
{
// Call the base class
Super::BeginPlay();
FTimerHandle TimerHandle;
GetWorldTimerManager().SetTimer(TimerHandle, this, &AMySpawnActorProjectile::Accelerate, 0.1f, true, 0.f);
}
void AMySpawnActorProjectile::Accelerate()
{
FVector NewVelocity = MyMovementComp->Velocity * 1.5f;
MyMovementComp->Velocity = NewVelocity;
}
void AMySpawnActorProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
Destroy();
}
The last actor to create is a particle actor that uses a NiagaraComponent
. To set up Niagara
in your project ensure you have the Niagara plugin enabled and that Niagara
is added to your build script. Refer to this previous Niagara Variable post for more details.
The spawn particle class will be another simple actor class with some minor differences to help us practice deferring spawn actors. Deferring spawn actors allows us to set class variables or preform logic before spawn. The particle actor will utilize the the BeginPlay
function to update a variables accordingly before they're spawned into the world.
MySpawnActorParticle.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySpawnActorParticle.generated.h"
class UNiagaraComponent;
UCLASS()
class MYPROJECT_API AMySpawnActorParticle : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMySpawnActorParticle();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
UPROPERTY()
USceneComponent* MyRoot;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UNiagaraComponent* MyNiagaraComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UMaterialInterface* MyMaterialInterface;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FVector ParticleColor;
};
MySpawnActorParticle.cpp
#include "MySpawnActorParticle.h"
#include "NiagaraComponent.h"
AMySpawnActorParticle::AMySpawnActorParticle()
{
MyRoot = CreateDefaultSubobject<USceneComponent>(TEXT("MyRoot"));
MyRoot->bVisualizeComponent = true;
RootComponent = MyRoot;
MyNiagaraComponent = CreateDefaultSubobject<UNiagaraComponent>(TEXT("MyNiagaraComponent"));
MyNiagaraComponent->SetupAttachment(RootComponent);
ParticleColor = FVector(1.f, 0.f, 0.f);
}
void AMySpawnActorParticle::BeginPlay()
{
Super::BeginPlay();
if (MyMaterialInterface != nullptr)
{
UMaterialInstanceDynamic* MyDynamicMaterialInstance = UMaterialInstanceDynamic::Create(MyMaterialInterface, MyNiagaraComponent);
if (MyDynamicMaterialInstance != nullptr)
{
MyDynamicMaterialInstance->SetVectorParameterValue(FName("Base Color"), ParticleColor);
MyNiagaraComponent->SetVariableMaterial(FName("MyMaterial"), MyDynamicMaterialInstance);
}
}
}
Spawning Actors
All of the spawning logic will live inside the MySpawnActor
class. The actor will listen for the Execute_SpawnActor
event coming from the trigger class by inheriting MySpawnActorInterface
to fire its SpawnActor_Implementation()
to run the spawn function accordingly to what boolean
is true
. If bSpawnProjectileActor
is true
, it'll execute SpawnProjectileActor
. Simple to the Unreal FPS template for spawning actors, we create references to actor classes we want to spawn inside the header. So we have TSubclassOf<Type>
of each type of actor class we want to spawn.
MySpawnActor.h
#pragma once
#include "CoreMinimal.h"
#include "MySpawnActorInterface.h"
#include "GameFramework/Actor.h"
#include "MySpawnActor.generated.h"
class AMySpawnActorParticle;
class AMySpawnActorPhysics;
class AMySpawnActorProjectile;
UCLASS()
class MYPROJECT_API AMySpawnActor : public AActor, public IMySpawnActorInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMySpawnActor();
UPROPERTY()
USceneComponent* Root;
UPROPERTY(EditAnywhere)
bool bSpawnPhysicsActor;
UPROPERTY(EditAnywhere);
bool bSpawnProjectileActor;
UPROPERTY(EditAnywhere)
bool bSpawnParticleEffect;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class AMySpawnActorPhysics> MySpawnActorPhysicsClass;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class AMySpawnActorProjectile> MySpawnActorProjectileClass;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class AMySpawnActorParticle> MySpawnActorParticleClass;
UFUNCTION()
void SpawnPhysicsActor();
UFUNCTION()
void SpawnProjectileActor();
UFUNCTION()
void SpawnParticleEffect();
UFUNCTION()
FVector RandomColor();
void SpawnActor_Implementation() override;
};
The physics and projectile actors will be very similar in their spawn implementation. Both actors first set a spawn location and spawn rotation, the phsics actor creates some randomeness in it's rotation to prevent perfect stacking, but the projectile actor simply uses GetActorRotation()
. Then both spawning functions create a FActorSpawnParameters
variable and set the collision method to AdjustIfPossibleButDontSpawnIfColliding
.
Once the variabls are created we can then call World->SpawnActor<AActor>
and pass the variables in. Each SpawnActor<AActor>
function's first argument will be a TSubclassOf<class ...
declared in the header file.
void AMySpawnActor::SpawnPhysicsActor()
{
if (MySpawnActorPhysicsClass != nullptr)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
const FVector SpawnLocation = GetActorLocation();
// Set Rotation: Pitch and Yaw randomized to prevent perfect stacking.
const FRotator SpawnRotation = FRotator(FMath::RandRange(0.0f, 360.0f), 0.f, FMath::RandRange(0.0f, 360.0f));
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
World->SpawnActor<AActor>(MySpawnActorPhysicsClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
}
}
}
void AMySpawnActor::SpawnProjectileActor()
{
if (MySpawnActorProjectileClass != nullptr)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
const FVector SpawnLocation = GetActorLocation();
const FRotator SpawnRotation = GetActorRotation();
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
World->SpawnActor<AActor>(MySpawnActorProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
}
}
}
Deferring an actor is slightly different. We follow the same steps as before, but we start by creating an FTranform
value instead of a location FVector
and rotation FRotator
. When our variables are ready we then call World->SpawnActorDeferred<Type>
passing in the type of actor we want to spawn. We use SpawnActorDeferred
when we want to potentially perform logic or adjust class variables before spawn. Deferring the actor does not actually spawn our actor, but rather allows us the opportunity to edit the actor before entering the game world. In this case we'll randomly set the MySpawnActorParticle
's ParticleColor
property to have a different color every time it spawns. When we are done updating our deferred actor it is our responsibility to then call UGameplayStatics::FinishSpawningActor
to finally spawn our actor.
void AMySpawnActor::SpawnParticleEffect()
{
if (MySpawnActorParticleClass != nullptr)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
FTransform SpawnTransform(GetActorLocation());
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AMySpawnActorParticle* MyDeferredActor = World->SpawnActorDeferred<AMySpawnActorParticle>(MySpawnActorParticleClass, SpawnTransform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
if (MyDeferredActor)
{
// We can now access class variables before spawning
MyDeferredActor->ParticleColor = RandomColor();
UGameplayStatics::FinishSpawningActor(MyDeferredActor, SpawnTransform);
}
}
}
}
FVector AMySpawnActor::RandomColor()
{
float Red = FMath::RandRange(0.f, 1.f);
float Blue = FMath::RandRange(0.f, 1.f);
float Green = FMath::RandRange(0.f, 1.f);
return FVector(Red, Blue, Green);
}
With that we've finished the C++ logic for spawning actors in the game world. In the demo I proceeded to create Blueprint actors from our newly created classes to create the spawned actors with meshes, rotator components, niagara systems, etc. All spawn actor Blueprints are located in the /Content/Gym/Blueprints/SpawnActors directory.
Cloning the project will likely provide a better understanding of how everything comes together. Hopefully this helps.
Harrison McGuire