반응형

액터의 생명 주기

https://docs.unrealengine.com/latest/KOR/Programming/UnrealArchitecture/Actors/ActorLifecycle/index.html

  • SHARE:

언리얼 엔진

이 문서는 액터의 생명 주기에 대한 하이 레벨 개요서로, 액터가 레벨에 어떻게 인스턴스가 생성 (Spawn) 되고 소멸 (Destroy) 되는지에 대해서입니다.

아래 흐름도는 액터 인스턴스가 생성되는 세 가지 주요 경로에 대해 보여줍니다. 액터 생성이 어떻게 되었든지 관계없이, 소멸까지 모두 같은 경로를 따릅니다.

생명 주기 분해도


디스크에서 로드

이 경로는 이미 레벨에 있는 액터에 대해서, LoadMap 이 발생했을 때나 (스트리밍 또는 서브 레벨에서) AddToWorld 가 호출되었을 때 발생합니다.

  1. 패키지/레벨에 있는 액터가 디스크에서 로드됩니다.

  2. PostLoad 로드 이후 - 디스크에서의 로드 완료 후 serialize 된 액터에 의해 호출됩니다. 커스텀 버전이나 픽스 작업은 이 곳에 와야 합니다. PostLoad 는 PostActorCreated 와 상호 배제됩니다.

  3. InitializeActorsForPlay 플레이용 액터 초기화

  4. RouteActorInitialize 액터 초기화 경로변경 - 초기화되지 않은 액터에 대해 호출됩니다 (심리스 트래블 전환 처리)

    1. PreInitializeComponents 컴포넌트 초기화 이전 - 액터의 컴포넌트에 InitializeComponent 를 호출하기 전 호출합니다.

    2. InitializeComponent 컴포넌트 초기화 - 액터에 정의된 각 컴포넌트 생성용 헬퍼 함수입니다.

    3. PostInitializeComponents 컴포넌트 초기화 이후 - 액터의 컴포넌트 초기화 완료 후 호출됩니다.

  5. BeginPlay 플레이 시작 - 레벨이 시작되면 호출됩니다.

에디터에서 플레이

에디터에서 플레이 경로는 디스크에서 로드와 거의 똑같지만, 액터를 디스크에서 로드하기 보다는 에디터에서 복사해 옵니다.

  1. 에디터에 있는 액터를 새 월드로 복제합니다.

  2. PostDuplicate (복제 이후)를 호출합니다.

  3. InitializeActorsForPlay (플레이용 액터 초기화)

  4. 초기화되지 않은 액터는 RouteActorInitialize (액터 초기화 전달), (심리스 트래블 이전 처리)

    1. PreInitializeComponents (컴포넌트 초기화 이전) - 액터의 컴포넌트에서 InitializeComponent (컴포넌트 초기화)가 호출되기 이전에 호출됩니다.

    2. InitializeComponent 컴포넌트 초기화 - 액터에 정의된 컴포넌트 각각에 대한 생성을 돕는 함수입니다.

    3. PostInitializeComponents 컴포넌트 초기화 이후 - 액터의 컴포넌트 초기화가 완료된 이후 호출됩니다.

  5. BeginPlay 플레이 시작 - 레벨이 시작되면 호출됩니다.

스폰

액터를 스폰(인스턴싱)할 때 따르게 되는 경로입니다.

  1. SpawnActor (액터 스폰)이 호출됩니다.

  2. PostSpawnInitialize (스폰 초기화 이후)가 호출됩니다.

  3. PostActorCreated 액터 생성 이후 - 스폰된 액터의 생성 이후에 호출됩니다. 생성자같은 것이 여기에 와야 합니다.PostActorCreated (액터 생성 이후)는 PostLoad (로드 이후)와 상호 배제적입니다.

  4. ExecuteConstruction (생성 실행):

    • OnConstruction 생성시 - 액터의 생성 지점으로, 블루프린트 액터가 컴포넌트를 만들고 블루프린트 변수를 초기화시키는 곳입니다.

  5. PostActorConstruction (액터 생성 이후):

    1. PreInitializeComponents 컴포넌트 초기화 이전 - 액터의 컴포넌트에서 InitializeComponent (컴포넌트 초기화)가 호출되기 전에 호출됩니다.

    2. InitializeComponent (컴포넌트 초기화) - 액터에 정의된 컴포넌트 각각의 생성을 돕는 함수입니다.

    3. PostInitializeComponents 컴포넌트 초기화 이후 - 액터의 컴포넌트 초기화가 완료된 이후 호출됩니다.

  6. OnActorSpawned (액터가 스폰되었을 때)가 UWorld 에 방송됩니다.

  7. BeginPlay (플레이 시작)이 호출됩니다.

디퍼드 스폰

"Expose on Spawn" (스폰시 노출) 설정된 프로퍼티가 있으면 액터는 디퍼드(유예시켜 나중에 한 번에 몰아서) 스폰이 가능합니다.

  1. SpawnActorDeferred 액터 디퍼드 스폰 - 순차적 액터 스폰을 뜻하는 것으로, 블루프린트 컨스트럭션 스크립트 이전 추가적인 구성이 가능합니다.

  2. SpawnActor (액터 스폰) 안의 모든 것이 일어나지만, PostActorCreated (액터 생성 이후) 뒤에는 다음과 같은 일이 일어납니다:

    1. 유효하지만 완료되지는 않은 액터 인스턴스로 다양한 "초기화 함수" 구성 / 호출을 합니다.

    2. FinishSpawningActor 액터 스폰 마무리 - 액터를 마무리시키기 위해 호출되며, SpawnActor (액터 스폰) 줄의 ExecuteConstruction (생성 실행)을 집어듭니다.

수명의 막바지에 달해서

액터 소멸(destroy)은 여러가지 방식으로 가능하나, 존재의 종말은 항상 같습니다.

게임플레이 도중

이는 완벽히 옵션인데, 많은 액터가 플레이 도중에는 실제로 죽지 않을 것이기 때문입니다.

Destroy 소멸 - 액터를 제거해야겠다 싶지만 게임플레이가 계속 진행중일 때 게임에서 수동으로 호출됩니다. 액터는 킬 대기 상태로 마킹되며, 레벨의 액터 배열에서 제거됩니다.

EndPlay 플레이 종료 - 액터의 수명이 막바지에 다다랐음을 보증하기 위해 여러 곳에서 호출됩니다. 플레이 도중에는 Destroy (소멸)이나, Level Transition (레벨 전환)이나, 액터가 들어있는 스트리밍 레벨이 언로드되면 이 함수가 발동됩니다. EndPlay 가 호출되는 경우는 다음과 같습니다:

  • Destroy 명시적 호출

  • 에디터에서 플레이 종료

  • Level Transition (심리스 트래블 또는 맵 로드)

  • 액터가 들어있는 스트리밍 레벨 언로드

  • 액터의 수명 만료

  • 어플리케이션 종료 (모든 액터 소멸)

어떻게 발생했는지와는 상관없이, 액터는 RF_PendingKill 마킹되어 다음 가비지 콜렉션 주기 동안 deallocate (할당 해제)됩니다. 또한 pending kill (킬 대기상태)를 수동 검사하기 보다는, 보다 깔끔한 FWeakObjectPtr 사용을 추천합니다.

OnDestroy 소멸시 - Destory (소멸)에 대한 구식(legacy) 반응입니다. 여기 있는 것들은 Level Transition (레벨 전환)이나 기타 게임 정리용 함수에 호출되는 EndPlay (플레이 종료)에 옮기는 것이 좋을 것입니다.

가비지 콜렉션

오브젝트가 소멸 마킹된 이후에는, 가비지 콜렉션이 일어날 때 메모리에서 실제로 제거되어 사용중이던 리소스가 해제됩니다.

오브젝트의 소멸 도중 호출되는 함수는 다음과 같습니다:

  1. BeginDestroy 소멸 시작 - 여기서 오브젝트의 메모리를 해제하고 기타 (그래픽 스레드 프록시 오브젝트 등의) 멀티스레드 리소스 처리를 할 수 있습니다. 소멸 예정 관련 대부분의 게임플레이 함수성은 더욱 일찍 EndPlay 에서 처리되었을 것입니다.

  2. IsReadyForFinishDestroy 소멸 마무리 준비 여부 - 가비지 콜렉션 프로세스는 이 함수를 호출하여 오브젝트 할당을 영구히 해제할 준비가 되었는지 여부를 결정합니다. false 를 반환하면, 이 함수는 다음 가비지 콜렉션 패스까지 실제 오브젝트 소멸 작업을 유예시킵니다.

  3. FinishDestroy 소멸 마무리 - 오브젝트가 곧 소멸되므로, 내부 데이터 구조체를 해제시킬 마지막 기회입니다. 메모리 해제 이전 마지막 호출입니다.

고급 가비지 콜렉션

언리얼 엔진 4 의 가비지 콜렉션 프로세스에서는 모두 같이 소멸되는 오브젝트 클러스터를 만듭니다. 클러스터를 만들면 오브젝트를 개별적으로 삭제할 때에 비할 때 가비지 콜렉션 관련 전체적인 메모리 교란(churn) 정도나 총 시간이 감소됩니다. 오브젝트가 로드되면서 서브오브젝트가 생성될 수 있습니다. 오브젝트와 그 서브오브젝트를 가비지 콜렉터의 단일 클러스터로 합쳐 넣는 경우, 엔진은 전체 오브젝트가 해제 준비될 때까지 클러스터에 사용되는 리소스의 해제를 지연시켰다가, 모든 리소스를 한 번에 해제시킬 수 있습니다.

가비지 콜렉션은 대부분의 프로젝트의 경우 환경설정 또는 변경할 필요가 전혀 없으나, 가비지 콜렉터의 클러스터 작동방식을 바꿔서 효율을 향상시킬 수 있는 경우가 있을 수 있는데, 그 방법은 다음과 같습니다:

  1. Clustering 클러스터링 - 이 옵션은 끕니다. 프로젝트 세팅 에서 Garbage Collection (가비지 콜렉션) 섹션 아래 Create Garbage Collector UObject Clusters (가비지 콜렉터 오브젝트 클러스터 생성) 옵션을 false 로 설정하면 됩니다. 대부분의 프로젝트의 경우 이 옵션을 끄면 가비지 콜렉션 효율이 떨어지므로, 퍼포먼스 테스트를 통해 득이 되는 것이 확실한 경우에만 끄는 것이 좋습니다.

  2. Cluster Merging 클러스터 병합 - 클러스터링이 true 로 설정된 경우, (프로젝트 세팅 에서 Garbage Collection (가비지 콜렉션) 섹션 아래) Merge GC Clusters (GC 클러스터 병합) 옵션을 true 로 설정하면 클러스터 병합 기능을 켤 수 있습니다. 이 옵션은 기본적으로 꺼져있으며, 모든 프로젝트에 적합하지도 않습니다. 오브젝트의 클러스터를 만드는 프로세스 도중, 오브젝트를 검사하여 그 안에 다른 오브젝트로의 레퍼런스를 찾을 수 있습니다. 클러스터 병합이 없으면 (기본 동작) 그 레페런스가 기록은 되나, 로드되는 오브젝트와 그 서브오브젝트는 원래 클러스터에 남아있게 됩니다. 클러스터 병합이 있으면, 로드되는 오브젝트의 클러스터와 레퍼런스 대상 오브젝트가 합쳐집니다. 예를 들어 파티클 시스템 애셋이 머티리얼 애셋을 레퍼런스하나 클러스터 병합 옵션은 꺼진 경우, 머티리얼과 파티클 시스템은 가비지 콜렉션용 별도의 클러스터에 남아있습니다. 클러스터 병합 옵션이 켜진 경우, 파티클 시스템이 머티리얼을 레퍼런스하고 있기 때문에 파티클 애셋 클러스터가 머티리얼 클러스터와 병합됩니다. 이러한 방식은 오픈 월드 게임처럼 콘텐츠 스트리밍이 일어나는 경우엔 바람직하지 않을 수 있는데, 다수의 클러스터가 병합되면서 보다 거대하고 다양한 오브젝트 그룹을 형성하기 때문입니다. 클러스터의 오브젝트들은 개별적으로 소멸되지 않고, 그 그룹의 모든 오브젝트가 소멸 설정될 때까지 기다리게 됩니다. 그렇게 되면 그 오브젝트 중 소수만 사용되어도 거대한 오브젝트 클러스터가 메모리에 남아있게 되는 경우가 생길 수 있습니다. 하지만 클러스터 병합 기능을 켜고, 코드에서 다른 오브젝트에 공유되지 않는 레퍼런스된 오브젝트가 다수 있는 애셋과 같은 특별한 경우를 수동으로 추가해 주면, 서브오브젝트들을 한꺼번에 청소하고, 가비지 콜렉터가 게임 도중 유지하면서 검사해야 하는 개별 항목 및 서브오브젝트 수가 줄어들기에 퍼포먼스가 향상될 수 있습니다. AdvancedGC.png


반응형

+ Recent posts