Post

UE5 Actor 生命周期

一图流

ActorLifeCycle1 Actor Lifecycle

从磁盘加载

从磁盘加载(Load from disk) 路径适用于已经在关卡中的Actor,如当UEngine::LoadMap发生时,或当关卡流送调用UWorld::AddToWorld

个人理解:对于打包发布后的游戏,每张地图上的默认物品,例如放置的树,建筑,道路等都以这种方式加载

整体流程:

  1. 从磁盘加载包/关卡中的Actor
  2. 序列化的Actor从磁盘加载完成后调用PostLoad。所有自定义版本化和修复操作应在此处进行。PostLoadAActor::PostActorCreated互斥
  3. 世界调用UAISystemBase::InitializeActorsForPlay,以准备Actor启动GamePlay
  4. 关卡为任何没有初始化的Actor和seamless travel carry-over 调用Ulevel::RouteActorIntialize,以下为此过程调用的函数
    1. AActor::PreInitializeComponentInitializeComponent调用前被调用
    2. UActorComponent::InitiializeComponent是在Actor上的每一个Component创建时的帮助函数
    3. AActor::PostInitializeComponent在Actor的所有Component初始化完毕后调用
  5. 当关卡开始运行后,调用AActor::BeginPlay函数

在编辑器中运行

从编辑器中运行时,Actor是从编辑器中直接复制到世界场景中,而不是从磁盘进行加载,整体初始化流程与从磁盘加载类似

  1. Actor被从编辑器复制到新的世界场景中
  2. Actor调用UObject::PostDumplicate
  3. 世界场景调用UAISystemBase::InitializeActorsForPlay
  4. 关卡为任何没有初始化的Actor和seamless travel carry-over 调用Ulevel::RouteActorIntialize,以下为此过程调用的函数
    1. AActor::PreInitializeComponentInitializeComponent调用前被调用
    2. UActorComponent::InitiializeComponent是在Actor上的每一个Component创建时的帮助函数
    3. AActor::PostInitializeComponent在Actor的所有Component初始化完毕后调用
  5. 当关卡开始运行后,调用AActor::BeginPlay函数

生成

生成Actor时遵循以下流程

  1. UWorld::SpawnActor被调用
  2. AActor::PostSpawnInitialize在Actor生成到世界场景中后被调用
  3. AActor::PostActorCreated被调用,任何构造函数的实现应该在这里进行,与PostLoad冲突
  4. AActor::ExecuteConstructor
  5. AActor::OnConstructor 蓝图Actor的Component和变量在这里被创建和初始化
  6. AActor::PostActorConstructor:
    1. AActor::PreInitializeComponent
    2. UActorComponent::InitializeComponent
    3. AActor::PostInitializeComponent 在Component初始化结束后被调用
  7. UWorld::OnActorSpawned 广播到UWorld
  8. 调用AActor::BeginPlay

补充:第一步UWorld::SpawnActor源码,其中SpawnActor在LevelActor.cpp中实现

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
template< class T >  
T* SpawnActorDeferred(  
    UClass* Class,  
    FTransform const& Transform,  
    AActor* Owner = nullptr,  
    APawn* Instigator = nullptr,  
    ESpawnActorCollisionHandlingMethod CollisionHandlingOverride 
	    = ESpawnActorCollisionHandlingMethod::Undefined,  
    ESpawnActorScaleMethod TransformScaleMethod 
	    = ESpawnActorScaleMethod::MultiplyWithRoot  
    )  
{  
    if( Owner )  
    {  
       check(this==Owner->GetWorld());  
    }  
    FActorSpawnParameters SpawnInfo;  
    SpawnInfo.SpawnCollisionHandlingOverride = CollisionHandlingOverride;  
    SpawnInfo.TransformScaleMethod = TransformScaleMethod;  
    SpawnInfo.Owner = Owner;  
    SpawnInfo.Instigator = Instigator;  
    SpawnInfo.bDeferConstruction = true;  
    return (Class != nullptr) 
	    ? Cast<T>(SpawnActor(Class, &Transform, SpawnInfo)) : nullptr;  
}

LevelActor.cpp中,经过了大量的检查和配置,最终生成Actor的是这一行代码

1
2
3
4
5
6
7
8
9
// actually make the actor object  
AActor* const Actor = NewObject<AActor>(LevelToSpawnIn, 
					Class, 
					NewActorName, 
					ActorFlags, 
					Template, 
					false/*bCopyTransientsFromClassDefaults*/, 
					nullptr/*InInstanceGraph*/, 
					ExternalPackage);

接下来是上述流程中的事件

1
2
3
4
5
6
7
8
9
10
// Broadcast delegate before the actor and its contained components are initialized  
OnActorPreSpawnInitialization.Broadcast(Actor);

Actor->PostSpawnInitialize(UserTransform, 
						   SpawnParameters.Owner, 
						   SpawnParameters.Instigator, 
						   SpawnParameters.IsRemoteOwned(), 
						   SpawnParameters.bNoFail, 
						   SpawnParameters.bDeferConstruction, 
						   SpawnParameters.TransformScaleMethod);

延迟生成

一个具有生成时公开属性的Actor可以被延迟生成

注意:此处特指蓝图对象,在C++中,应直接使用UWorld::SpawnActorDeffered

蓝图

假设有一个名为BP_LandBuilding的蓝图Actor,并且这个蓝图Actor有一个名为BaseScal的变量,这个变量设置为生成时公开可编辑实例,则BaseScale的细节面板应如图所示

Base Scale Base Scale

在调用SpawnActor蓝图节点生成此蓝图Actor时,将可以设置BaseScale的值

Spawn Land Building Spawn Land Building

C++

在C++中使用延迟生成,应使用下方代码

1
2
3
const auto YourActor = GetWorld()->SpawnActorDeferred<YourClass>(YourClass, FTransform::Identity);  
// Do Something 在此处进行任何需要的设置操作
YourActor->FinishSpawning(FTransform::Identity);

Actor 生命周期的结束

在游戏中有几种销毁(Destroy)Actor的方式,但是Actor最终被从世界场景中移除(Removed)的方法是相同的,在游戏过程中,可以使用以下几个函数,但实际上许多Actor实际并不会在游戏过程中被销毁

  • AActor::Destroy 当游戏在任何时候需要移除Actor时,手动调用 AActor::Destroy ,但Gameplay仍在继续。Actor被标记为等待销毁并从关卡的Actor数组中移除
  • AActor::EndPlay 在数个地方调用,旨在保证Actor的生命走向终点。在游戏过程中,如果包含Actor的流送关卡被卸载,Destroy将调用此方法和关卡过渡(Level Transitions)
  • 调用EndPlay的全部情形
    • 显式调用Destroy
    • 手动终止在编辑器中运行
    • 关卡过渡(Seamless Travel 或 加载地图)
    • 包含Actor的流送关卡被卸载
    • Actor的生命周期已过
    • 应用程序被关闭
    • 无论这些情形出现的方式如何,Actor都将被标记为 RF_PendingKill ,因此在下个垃圾回收周期中,UE会将其从内存中解除分配。此外,可以考虑使用更清洁的 FWeakObjectPtr<AActor> 代替手动检查”等待销毁”
  • AActor::OnDestroyed - 这是对Destroy的旧有反应。我们推荐你将此处的逻辑移到EndPlay,因为会由关卡过渡和其他游戏清理函数调用

垃圾回收

一个对象被标记待销毁的一段时间后,垃圾回收会将其从内存中移除,释放其使用的资源。

在销毁过程中,调用以下函数:

  1. UObject::BeginDestroy - 对象可利用此机会释放内存并处理其他多线程资源(即:图像线程代理对象)。与销毁相关的大多数Gameplay功能理应在EndPlay中更早地处理。
  2. UObject::IsReadyForFinishDestroy - 垃圾回收过程将调用此函数,以确定对象是否可以永久解除分配。返回false,此函数即可延迟对象的实际销毁,直到下一个垃圾回收过程。
  3. UObject::FinishDestroy - 最后,对象将被销毁,这也是释放内部数据结构的机会。这是内存释放前的最后一次调用。

高级垃圾回收

用处不大,在项目设置(Project Settings) 中的 垃圾回收(Garbage Collection) 部分下,可将 创建垃圾回收器UObject群集(Create Garbage Collector UObject Clusters) 选项设为 false,如果经测试性能有提高可以保持关闭

参考链接

虚幻引擎Actor生命周期

This post is licensed under CC BY 4.0 by the author.