AActor:
- Can be placed in the world
- Can have a visual representation (Mesh, ...)
- Has function for spawning to the world
- Has functions for processing movement
- All classes inherited from
AActor
has anA
prefix at the start of class name
Inheritance Hierarchy
Spawn an Actor into the Scene (Level)
Spawn an Actor (and any its inherited class) to the world in runtime
There's a template TSubclassOf<ClassType>
that allows to store a variable
that represents a class of a UClass
type (and all its derived classes). UClass
has built in functionality that allows reflection to work between C++ and Blueprint.
Class information from TSubclassOf
is then used as the parameter for UWorld::SpawnActor
, respectively with its template SpawnActor<Type>(UClass*, Location, Rotation)
that actually spawn actors to the world in runtime.
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class ASpawnedObjectClass> SpawnedObjectClass;
GetWorld()->SpawnActor<ASpawnedObject>(ASpawnedObjectClass, spawnLocation, spawnRotation);
Of course, the class reference can be made right in the C++ class without the need to use Blueprint derived class, just refer to something like auto SpawnedObjectClass = ASpawnedObjectClass::StaticClass();
in such case.
Attach an actor into another actor
By spawning an actor into the world, regardless by what class it's being processed in the runtime, the actor acts independently based on own configuration. If the actor represents e.g. a tool, that should be tide to another AActor
, e.g. ACharacter
, the "Tool" AActor
must be attached into its parent Actor, e.g. ACharacter
.
Attaching to Meshes via Sockets
How to attach e.g. "Screwdriver" AActor
into a hand of ACharacter
?
If the "Screwdriver" or any other tool or even a gun or so (It really doesn't matter what kind of tool it is) is the part of the Skeleton
, there's necessary to hide it first. It will be later replaced by the AActor for tools, for which actually we need to define a point where the tool actor has to be attached. This can be done through a socket
. Socket is a pointer into which any Actor can be plugged. On Skeleton, the socket
can be placed by clicking at a bone by right mouse button and selecting Add socket
. If we hide some tool, because it's already a part of the Skeleton, we can add the socket
as a child of the bone (tool) that we hid. If we did not, we must find an ideal location for placong the socket - probably a hand.
#include "Screwdriver.h"
void AExampleCharacter::BeginPlay()
{
// Spawn the tool
Screwdriver = GetWorld()->SpawnActor(AScrewdriver)(ScrewdriverClass);
// Hide the tool that is part of the Skeleton
GetMesh()->HideBoneByName(TEXT("Screwdriver_R"), EPhysBodyOp::PBO_None);
// Attach Screwdriver into the socket point added to Skeleton
Screwdriver->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("SocketName"));
Screwdriver->SetOwner(this);
}
Spawn an Actor (and any its inherited class) to the world in editor
As we work right in the editor, out of runtime, we use UBlueprintFunctionLibrary
inherited class.
#if WITH_EDITOR
/**
* Editor Only - Will not work in packaged build.
*
* Spawn an actor into a world
*
* @param ActorClassPath Path of the actor class: "/Game/Folder/MyActor"
* @param WorldPath Path of the world: "/Game/Folder/MyWorld"
* @param bUseEditorWorld If true, will use the world currently open in the editor instead of the WorldPath
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The spawned actor
*/
UFUNCTION(BlueprintCallable, Category = "Spawn Actors In World")
static AActor* SpawnActorInWorld(FString ActorClassPath, FString WorldPath, bool bUseEditorWorld, bool& bOutSuccess, FString& OutInfoMsg);
#endif
#if WITH_EDITOR
#include "Engine/Blueprint.h" // Engine
#include "Engine/World.h" // Engine
#include "Editor/EditorEngine.h" // UnrealEd
AActor* UExampleClass::SpawnActorInWorld(FString ActorClassPath, FString WorldPath, bool bUseEditorWorld, bool& bOutSuccess, FString& OutInfoMsg)
{
// Load blueprint asset
UBlueprint* BlueprintActor = Cast<UBlueprint>(StaticLoadObject(UBlueprint::StaticClass(), nullptr, *ActorClassPath));
if (BlueprintActor == nullptr || !BlueprintActor->GeneratedClass->IsChildOf(AActor::StaticClass()))
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Spawn Actor In World Failed - Path doesn't lead to a valid Actor. '%s'"), *ActorClassPath);
return nullptr;
}
// Find world
UWorld* World = nullptr;
if (bUseEditorWorld){
World = GEditor->GetEditorWorldContext().World();
} else {
World = Cast<UWorld>(StaticLoadObject(UWorld::StaticClass(), nullptr, *WorldPath));
if (World == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Spawn Actor In World Failed - Path doesn't lead to a valid World. '%s'"), *WorldPath);
return nullptr;
}
}
// Spawn actor
AActor* SpawnedActor = World->SpawnActor(BlueprintActor->GeneratedClass);
// Success
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Spawn Actor In World Succeeded '%s'"), *ActorClassPath);
if (World != GEditor->GetEditorWorldContext().World())
{
OutInfoMsg += "\n--- Warning ---";
OutInfoMsg += "\nThe world used isn't the same as the world opened in the editor.";
OutInfoMsg += "\nIf you try to open it without saving it first, Unreal will crash.";
}
// Return the actor
return SpawnedActor;
}
#endif
if (Target.bBuildEditor == true)
{
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// New Modules - Editor Only
"UnrealEd",
}
);
}
/Game/Path/To/Asset
- access an asset in theContent
folder of the game/PluginName/Path/To/Asset
- access an asset in theContent
folder of selected plugin
Relative path to assets:
Get All Actors Of Class Examples
Get the names of all actors in the current editor world
#include "Kismet/BlueprintFunctionLibrary.h" // Engine
/**
* Get the names of all actors in the current editor world
*
* @param bUseGameplayStatics If want to use the function from GameplayStatics
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return Names of all the actors
*/
UFUNCTION(BlueprintCallable, Category = "Alex Quevillon - Get All Actors Of Class")
static TArray<FString> GetNamesOfAllActors(bool bUseGameplayStatics, bool& bOutSuccess, FString& OutInfoMsg);
#include "Kismet/GameplayStatics.h" // Engine
#include "EngineUtils.h" // Engine
#include "Engine/StaticMeshActor.h" // Engine
TArray<FString> UExampleClass::GetNamesOfAllActors(bool bUseGameplayStatics, bool& bOutSuccess, FString& OutInfoMsg)
{
// Get the world from somewhere. In this case, we take the editor world.
// But you can also get it from somewhere else. e.g. from any Actor or by loading the world asset with StaticLoadObject
UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
if (World == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Get Names Of All Actors Failed - World is not valid"));
return TArray<FString>();
}
// New list of names
TArray<FString> Names = TArray<FString>();
if (bUseGameplayStatics)
{
// List that'll receive the actors
TArray<AActor*> Actors = TArray<AActor*>();
// Get the actors
UGameplayStatics::GetAllActorsOfClass(World, AActor::StaticClass(), Actors);
// Loop through all the actors
for (AActor* Actor : Actors)
{
// Get the Actor's name
Names.Add(Actor->GetName());
}
}
else
{
// Directly loop through all the actors using TActorIterator
// The class in <> is used as filter when getting the actors. This can be any class you want (as long as it's an Actor)
// In this case, we simply use the AActor class directly to get all the possible actors.
for (TActorIterator<AActor> It(World); It; ++It)
{
// Get the real Actor from the Iterator
AActor* Actor = *It;
// Get the Actor's name
Names.Add(Actor->GetName());
}
}
// Return the names
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Get Names Of All Actors Succeeded"));
return Names;
}
Get the Mesh names of all static mesh actors in the current editor world
#include "Kismet/BlueprintFunctionLibrary.h" // Engine
/**
* Get the Mesh names of all static mesh actors in the current editor world
*
* @param bUseGameplayStatics If want to use the function from GameplayStatics
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return Names of all the actors
*/
UFUNCTION(BlueprintCallable, Category = "Alex Quevillon - Get All Actors Of Class")
static TArray<FString> GetMeshNamesOfAllStaticMeshActors(bool bUseGameplayStatics, bool& bOutSuccess, FString& OutInfoMsg);
#include "Kismet/GameplayStatics.h" // Engine
#include "EngineUtils.h" // Engine
#include "Engine/StaticMeshActor.h" // Engine
TArray<FString> UExampleClass::GetMeshNamesOfAllStaticMeshActors(bool bUseGameplayStatics, bool& bOutSuccess, FString& OutInfoMsg)
{
// Get the world
UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
if (World == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Get Mesh Names Of All Static Mesh Actors Failed - World is not valid"));
return TArray<FString>();
}
// New list of names
TArray<FString> Names = TArray<FString>();
if (bUseGameplayStatics)
{
// Get Actors
TArray<AActor*> Actors = TArray<AActor*>();
UGameplayStatics::GetAllActorsOfClass(World, AStaticMeshActor::StaticClass(), Actors);
// Loop through all static mesh actors and get static mesh names
for (AActor* Actor : Actors)
{
AStaticMeshActor* StaticMeshActor = Cast<AStaticMeshActor>(Actor);
Names.Add(StaticMeshActor->GetStaticMeshComponent()->GetStaticMesh()->GetName());
}
}
else
{
// Loop through all static mesh actors and get static mesh names
for (TActorIterator<AStaticMeshActor> It(World); It; ++It)
{
Names.Add(*It->GetStaticMeshComponent()->GetStaticMesh()->GetName());
}
}
// Return the names
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Get Mesh Names Of All Static Mesh Actors Succeeded"));
return Names;
}
Iterating over all Actors in a given level
Functions below are usable for all Run-Time instances of actors. Iterators in Unreal Engine are always accurate. More info about Iterators in the Iterators handbook
It may be made with TActorRange
class which is part of the "EngineUtils.h"
.
#include "EngineUtils.h"
#include "GameFramework/Controller.h"
void AExampleProject::EndGame(bool bIsPlayerWinner)
{
// foor loop through every controller in the world
for(AController* Controller : TActorRange<AController>(GetWorld()))
{
bool bIsWinner = Controller->IsPlayerController() == bIsPlayerWinner;
Controller->GameHasEnded(Controller->GetPawn(), bIsWinner);
}
}
Iterating over all AI characters to check whether there is still any alive. If not, end the game with Winner state..
// ExampleAIControlller.cpp
#include "ExampleCharacter.h"
bool AExampleAIController::IsDead() const
{
AExampleCharacter* ControlledCharacter = Cast<AExampleCharacter>(GetPawn());
if(ControlledCharacter != nullptr){
return ControlledCharacter->IsDead();
}
return true;
}
// ExampleGameMode.cpp
#include "ExampleAIController.h"
void AExampleGameMode::PawnKilled(APawn* PawnKilled)
{
Super::PawnKilled(PawnKilled);
for(AExampleAIController* Controller : TActorRange<AController>(GetWorld()))
{
if(!Controller->IsDead()) return;
}
EndGame(true);
}
General Actor Iterator
void AYourControllerClass::PrintAllActorsLocations()
{
//EngineUtils.h
for (TActorIterator<AActor> ActorItr(GetWorld()); ActorItr; ++ActorItr )
{
ClientMessage(ActorItr->GetName());
ClientMessage(ActorItr->GetActorLocation().ToString());
}
}
On Hit trigger
//.h file
UPROPERTY(VisibleAnywhere, Category = "Movement")
class UProjectileMovementComponent* ProjectileMC;
// .cpp file
#include "GameFramework/ProjectileMovementComponent.h"
AProjectile::BeginPlay()
{
ProjectileMesh->OnComponentHit.AddDynamic(this, &AProjectile::OnHit); //Collision Presets must be Enabled (Query and Physics) for handling Hit events
}
void AProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
if(!GetOwner() || OtherActor && OtherActor != this && OtherActor != GetOwner())
{
Destroy();
return;
}
// Apply Damage
// Spawn sound & particle effect
// Play Sound
Destroy();
}
Merge Actors With C++
The function below shows an example of merging all actors in a scene into a single merged actor.
#include "Kismet/BlueprintFunctionLibrary.h" // Engine
#if WITH_EDITOR
/**
* Editor Only - Will not work in packaged build.
*
* Merge Static Mesh Actors
*
* @param Actors The actors to merge
* @param DestinationPath The path of the merged StaticMesh asset: "/Game/Folder/MyStaticMesh"
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*
* @return The merged StaticMesh
*/
UFUNCTION(BlueprintCallable, Category = "Alex Quevillon - Merge actors")
static class UStaticMesh* MergeActors(TArray<class AStaticMeshActor*> Actors, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg);
#endif
#if WITH_EDITOR
#include "StaticMeshEditorSubsystem.h" // StaticMeshEditor (Editor Only)
#include "StaticMeshEditorSubsystemHelpers.h" // StaticMeshEditor (Editor Only)
#include "UnrealEdGlobals.h" // UnrealEd (Editor Only)
UStaticMesh* UExampleClass::MergeActors(TArray<AStaticMeshActor*> Actors, FString DestinationPath, bool& bOutSuccess, FString& OutInfoMsg)
{
// Make sure there are actors to merge
if (Actors.Num() == 0)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Merge Actors Failed - Actors list is empty. '%s'"), *DestinationPath);
return nullptr;
}
// Get Subsystem
UStaticMeshEditorSubsystem* StaticMeshEditorSubsystem = GEditor->GetEditorSubsystem<UStaticMeshEditorSubsystem>();
if (StaticMeshEditorSubsystem == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Merge Actors Failed - Static Mesh Editor Subsystem is not valid. '%s'"), *DestinationPath);
return nullptr;
}
// Options
FMergeStaticMeshActorsOptions Options;
Options.bSpawnMergedActor = false;
Options.BasePackageName = DestinationPath;
// FJoinStaticMeshActorsOptions
Options.bDestroySourceActors = false;
Options.NewActorLabel = "";
Options.bRenameComponentsFromSource = true;
// FMeshMergingSettings
Options.MeshMergingSettings.TargetLightMapResolution = 256;
Options.MeshMergingSettings.GutterSize = 2;
Options.MeshMergingSettings.LODSelectionType = EMeshLODSelectionType::CalculateLOD;
Options.MeshMergingSettings.SpecificLOD = 0;
Options.MeshMergingSettings.bGenerateLightMapUV = true;
Options.MeshMergingSettings.bComputedLightMapResolution = false;
Options.MeshMergingSettings.bPivotPointAtZero = true;
Options.MeshMergingSettings.bMergePhysicsData = false;
Options.MeshMergingSettings.bMergeMeshSockets = false;
Options.MeshMergingSettings.bMergeMaterials = false;
Options.MeshMergingSettings.bCreateMergedMaterial = false;
Options.MeshMergingSettings.bBakeVertexDataToMesh = false;
Options.MeshMergingSettings.bUseVertexDataForBakingMaterial = true;
Options.MeshMergingSettings.bUseTextureBinning = false;
Options.MeshMergingSettings.bReuseMeshLightmapUVs = true;
Options.MeshMergingSettings.bMergeEquivalentMaterials = true;
Options.MeshMergingSettings.bUseLandscapeCulling = false;
Options.MeshMergingSettings.bIncludeImposters = true;
Options.MeshMergingSettings.bSupportRayTracing = true;
Options.MeshMergingSettings.bAllowDistanceField = false;
// Merge Actors
AStaticMeshActor* MergedActor = nullptr;
bOutSuccess = StaticMeshEditorSubsystem->MergeStaticMeshActors(Actors, Options, MergedActor);
if (!bOutSuccess)
{
OutInfoMsg = FString::Printf(TEXT("Merge Actors Failed - Merge Failed. '%s'"), *DestinationPath);
return nullptr;
}
// Load the merged static mesh and return it
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Merge Actors Succeeded - '%s'"), *DestinationPath);
return Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), nullptr, *DestinationPath));
}
#endif
if (Target.bBuildEditor == true)
{
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Required Modules
"UnrealEd",
"StaticMeshEditor",
}
);
}
Lock / unlock actor transform
#include "Kismet/BlueprintFunctionLibrary.h" // Engine
/**
* Lock or unlock an actor transform
*
* @param Actor Actor to lock or unlock
* @param bLockTransform If true, will lock the transform. Otherwise unlock
* @param bOutSuccess If the action was a success or not
* @param OutInfoMsg More information about the action's result
*/
UFUNCTION(BlueprintCallable, Category = "Alex Quevillon - Lock Actor And Level")
static void LockActorTransform(AActor* Actor, bool bLockTransform, bool& bOutSuccess, FString& OutInfoMsg);
#include "EngineUtils.h" // Engine
void UExampleClass::LockActorTransform(AActor* Actor, bool bLockTransform, bool& bOutSuccess, FString& OutInfoMsg)
{
// Validate the actor
if (Actor == nullptr)
{
bOutSuccess = false;
OutInfoMsg = FString::Printf(TEXT("Lock Actor Transform Failed - Actor is not valid"));
return;
}
// Already right state?
if (Actor->IsLockLocation() != bLockTransform)
{
// Set new lock state
Actor->SetLockLocation(bLockTransform);
// Mark package dirty so the user knows something changed
Actor->GetWorld()->MarkPackageDirty();
}
bOutSuccess = true;
OutInfoMsg = FString::Printf(TEXT("Lock Actor Transform Succeeded"));
}