기본 콘텐츠로 건너뛰기

[Unreal Engine] Actor

    목차

Spawn

BP로 정의된 Actor를 C++에서 Spawn

C++ 변수 선언시 TSubclass<C++로 정의된 BP의 부모 클래스> 형태로 선언
Gist code here
Gist code end

주의사항!

  C++ UObject에서 BlueprintImplementableEvent, BlueprintNativeEvent를 이용하여 파라미터로 WorldContext를 넘겨도 Blueprint에서 SpawnActor 노드 호출이 안될 수도 있다. 때문에 C++ 내부에서 Spawn을 하거나 다른 WorldContext를 가져올 수 있는 곳에서 해야된다.

Actor 스폰 때 파라미터를 넘기기...


  원칙적으로 Actor 스폰시 사용자 정의 파라미터를 넘기는 것이 불가. 따라서 별도의 초기화함수를 만들어서 생성자 내/외부에서 호출하는 방법을 이용한다. 다만, 일반적으로 SpawnActor를 호출할 경우, 변수 초기화가 진행되기도 전에 BeginPlay 함수가 실행되는 문제가 있다.
  C++ 코드에서 SpawnActorDeferred 함수 또는 BeginDeferredActorSpawnFromClass 함수를 이용하면, 초기값을 먼저 설정한 후, BeginPlay를 호출하도록 로직 순서를 조정할 수 있다. (끝나고 난후, FinishSpawningActor 또는 FinishSpawning 함수를 호출해야된다.)


1
2
3
4
5
UWorld* World = GetWorld();
const FTransform SpawnLocAndRotation;
AMyActor* MyActor = World->SpawnActorDeferred<AMyActor>(AMyActor::StaticClass(), SpawnLocAndRotation);
MyActor->MyInitFunction( ... ); // 파라미터를 전달할 함수를 여기서 자유롭게
MyActor->FinishSpawning(SpawnLocAndRotation); // 끝난 후, 반드시 호출해야됨
cs


알아둬야할 사항

  • "UWorld::SpawnActorDeferred" Doc에 Remark 부분에 명시된 내용에 따르면, (상속받은) Blueprint의 생성자는 바로 호출되지 않는다. 
    • 근데 아마 C++의 자체 생성자는 무조건 먼저 호출될 것이다.
  • :UGameplayStatics::FinishSpawningActor" 함수 내부에서 "AActor::FinishSpawning"를 호출한다.

코드



참고



Destroy

  Actor 파괴 (또는 하위 Component 정리)시 EndPlay 함수에서 처리하면 안정적 (예 : NewObject로 동적 생성된 객체).
  보통의 경우 Actor 파괴 시 하위 Component도 같이 파괴된다고 함.


LifeCycle







주의사항

SpawnActor 직후에 Spawn된 Actor 내부의 변수 값을 할당할 때, 생애주기를 잘 생각하고 할당할 것. 아래와 같은 상황에서 결과 값이 "YYYY"가 될 수도 있음.

1
2
3
4
5
6
7
8
9
10
11
//외부 코드
{
    AMyActor A* = SpawnActor(AMyActor::StaticClass//틀린 코드지만 무시하자..
    A->CustomName = TEXT("XXXX");
}
 
//Actor 코드 내부
void AMyActor::PostInitializeComponent()
{
    CustomName = TEXT("YYYY");
}
cs



GetActor 함수

References

Module Engine
Header /Engine/Source/Runtime/Engine/Classes/Kismet/GameplayStatics.h
Include #include "Kismet/GameplayStatics.h"
Source /Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp


Description

월드에 배치되어 있는 모든 액터 목록을 가져온다. 그리고나서 클래스, 인터페이스, 태그 정보를 이용해서 원하는 액터가 나오도록 필터링한다. 느린 작업(Iterator를 이용)이기 때문에 사용에 유의 (매 프레임마다 호출하는 작업은 피할 것)


Declaration

1
2
3
4
5
6
7
8
9
GetActorOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass)
 
GetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors)
 
GetAllActorsWithInterface(const UObject* WorldContextObject, TSubclassOf<UInterface> Interface, TArray<AActor*>& OutActors)
 
GetAllActorsWithTag(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors)
 
GetAllActorsOfClassWithTag(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, FName Tag, TArray<AActor*>& OutActors)
cs


Parameters

  • WorldContextObject :보통은 GetWorld() 함수를 이용하여 가져온다.
  • OutActors : 검출된 액터들.
    함수 외부에서 TArray<AActor*> 변수 선언 후, 파라미터에 넣으면 된다.
    함수 시작시 "Array::Reset(SizeType NewSize = 0)"를 호출하기 때문에 찾으려는 타입을 지정하지 않은 경우 비어있게 된다.(Length = 0)
  • ActorClass : 찾으려는 액터 타입 파라미터로 "찾으려는함수클래스명::StaticClass()" 형식으로 넣으면 됨
  • Interface : 찾으려는 인터페이스
  • Tag : 찾으려는 액터의 태그 정보

Code


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/** 
 *    Find the first Actor in the world of the specified class. 
 *    This is a slow operation, use with caution e.g. do not use every frame.
 *    @param    ActorClass    Class of Actor to find. Must be specified or result will be empty.
 *    @return                Actor of the specified class.
 */
UFUNCTION(BlueprintCallable, Category="Actor", meta=(WorldContext="WorldContextObject", DeterminesOutputType="ActorClass"))
static class AActor* GetActorOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass);
 
AActor* UGameplayStatics::GetActorOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass)
{
    QUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetActorOfClass);
 
    // We do nothing if no is class provided
    if (ActorClass)
    {
        if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
        {
            for (TActorIterator<AActor> It(World, ActorClass); It; ++It)
            {
                AActor* Actor = *It;
                return Actor;
            }
        }
    }
 
    return nullptr;
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** 
 *    Find all Actors in the world of the specified class. 
 *    This is a slow operation, use with caution e.g. do not use every frame.
 *  @param    ActorClass    Class of Actor to find. Must be specified or result array will be empty.
 *    @param    OutActors    Output array of Actors of the specified class.
 */
UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject", DeterminesOutputType="ActorClass", DynamicOutputParam="OutActors"))
static void GetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);
 
void UGameplayStatics::GetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors)
{
    QUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsOfClass);
    OutActors.Reset();
 
    // We do nothing if no is class provided, rather than giving ALL actors!
    if (ActorClass)
    {
        if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
        {
            for (TActorIterator<AActor> It(World, ActorClass); It; ++It)
            {
                AActor* Actor = *It;
                OutActors.Add(Actor);
            }
        }
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/** 
 *    Find all Actors in the world with the specified interface.
 *    This is a slow operation, use with caution e.g. do not use every frame.
 *    @param    Interface    Interface to find. Must be specified or result array will be empty.
 *    @param    OutActors    Output array of Actors of the specified interface.
 */
UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject", DeterminesOutputType="Interface", DynamicOutputParam="OutActors"))
static void GetAllActorsWithInterface(const UObject* WorldContextObject, TSubclassOf<UInterface> Interface, TArray<AActor*>& OutActors);
 
void UGameplayStatics::GetAllActorsWithInterface(const UObject* WorldContextObject, TSubclassOf<UInterface> Interface, TArray<AActor*>& OutActors)
{
    QUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsWithInterface);
    OutActors.Reset();
 
    // We do nothing if no interface provided, rather than giving ALL actors!
    if (!Interface)
    {
        return;
    }
 
    if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
    {
        for (FActorIterator It(World); It; ++It)
        {
            AActor* Actor = *It;
            if (Actor->GetClass()->ImplementsInterface(Interface))
            {
                OutActors.Add(Actor);
            }
        }
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 *    Find all Actors in the world with the specified tag.
 *    This is a slow operation, use with caution e.g. do not use every frame.
 *    @param    Tag            Tag to find. Must be specified or result array will be empty.
 *    @param    OutActors    Output array of Actors of the specified tag.
 */
UFUNCTION(BlueprintCallable, Category="Actor",  meta=(WorldContext="WorldContextObject"))
static void GetAllActorsWithTag(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors);
 
void UGameplayStatics::GetAllActorsWithTag(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors)
{
    QUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsWithTag);
    OutActors.Reset();
 
    // We do nothing if no tag is provided, rather than giving ALL actors!
    if (Tag.IsNone())
    {
        return;
    }
 
    if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
    {
        for (FActorIterator It(World); It; ++It)
        {
            AActor* Actor = *It;
            if (Actor->ActorHasTag(Tag))
            {
                OutActors.Add(Actor);
            }
        }
    }
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 *    Find all Actors in the world of the specified class with the specified tag.
 *    This is a slow operation, use with caution e.g. do not use every frame.
 *    @param    Tag            Tag to find. Must be specified or result array will be empty.
 *    @param    ActorClass    Class of Actor to find. Must be specified or result array will be empty.
 *    @param    OutActors    Output array of Actors of the specified tag.
 */
UFUNCTION(BlueprintCallable, Category = "Actor", meta = (WorldContext="WorldContextObject", DeterminesOutputType="ActorClass", DynamicOutputParam="OutActors"))
static void GetAllActorsOfClassWithTag(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, FName Tag, TArray<AActor*>& OutActors);
 
void UGameplayStatics::GetAllActorsOfClassWithTag(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, FName Tag, TArray<AActor*>& OutActors)
{
    QUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsOfClass);
    OutActors.Reset();
 
    UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
 
    // We do nothing if no is class provided, rather than giving ALL actors!
    if (ActorClass && World)
    {
        for (TActorIterator<AActor> It(World, ActorClass); It; ++It)
        {
            AActor* Actor = *It;
            if (IsValid(Actor) && Actor->ActorHasTag(Tag))
            {
                OutActors.Add(Actor);
            }
        }
    }
}
cs

추가사항

  • 현재 레벨에 배치되어 있는 액터를 대상으로만 검출
  • 공식적으로 저 함수들 말고는 효율이 좋은 함수는 없는듯.
    • 다른 함수를 원한다면 Map이나 Array 같은 자료유형을 이용하여 직접 설계해야됨.





댓글