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 (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.
/**
* 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);
#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;
}
PrivateDependencyModuleNames.AddRange
(
new string[]
{
// Default Modules
"Core",
"CoreUObject",
"Engine",
// 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:
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();
}