本文档大致介绍了Actor的生命周期,其中包括:
如何在关卡中实例化或生成Actor,包括如何初始化Actor。
如何将Actor标识为PendingKill,然后通过垃圾回收移除或销毁。
下方流程图展示了如何实例化Actor的主要路径。无论Actor是如何创建的,它们的销毁路径均相同。
生命周期详解
从磁盘加载
从磁盘加载(Load From Disk)路径适用于已经在关卡中的Actor,如当 UEngine::LoadMap 发生时,或当关卡流送调用 UWorld::AddToWorld 时。
从磁盘加载包/关卡中的Actor。
序列化的Actor从磁盘加载完成后调用 PostLoad 。所有自定义版本化和修复操作应在此处执行。PostLoad与 AActor::PostActorCreated 互斥。
世界调用 UAISystemBase::InitializeActorsForPlay ,以准备Actor启动Gameplay。
关卡为未初始化的Actor和无缝行程结转调用 ULevel::RouteActorInitialize 。
在Actor的组件上调用InitializeComponent之前调用 AActor::PreInitializeComponents 。
UActorComponent::InitializeComponent 是Actor上定义的每个组件的创建辅助函数。
在Actor的组件初始化后,调用 AActor::PostInitializeComponents 。
关卡启动后,调用 AActor::BeginPlay 。
在编辑器中运行
在"在编辑器中运行(Play in Editor)"路径中,Actor是从编辑器中复制而来,并非从磁盘中加载。然后,复制的Actor按与"从磁盘加载(Load From Disk)"路径中所述的相同流程初始化。
将编辑器中的Actor复制到新世界中。
为未初始化的Actor调用 ULevel::RouteActorInitialize ,涵盖无缝行程结转。
在Actor的组件上调用InitializeComponent之前调用 AActor::PreInitializeComponents 。
UActorComponent::InitializeComponent 是Actor上定义的每个组件的创建辅助函数。
在Actor的组件初始化后,调用 AActor::PostInitializeComponents 。
关卡启动后,调用 AActor::BeginPlay 。
生成
当你生成Actorr的实例时,这是相关路径:
调用 UWorld::SpawnActor 。
在世界中生成Actor后,调用 AActor::PostSpawnInitialize 。
已生成Actor创建后为其调用 AActor::PostActorCreated ,所有构造函数实现行为应在此发生。PostActorCreated与PostLoad互斥。
AActor::OnConstruction - Actor的构建,蓝图Actor的组件在此处创建,蓝图变量在此处初始化。
AActor::PostActorConstruction :
在Actor的组件上调用InitializeComponent之前调用 AActor::PreInitializeComponents 。
UActorComponent::InitializeComponent 是Actor上定义的每个组件的创建辅助函数。
在Actor的组件初始化后,调用 AActor::PostInitializeComponents 。
UWorld::OnActorSpawned 在UWorld上播放。
调用 AActor::BeginPlay 。
延迟生成
将任意属性设为"生成时公开(Expose on Spawn)"即可延迟Actor生成。
UWorld::SpawnActorDeferred 旨在生成流程性Actor,允许在蓝图构建脚本之前进行额外设置。
SpawnActor中的所有操作发生,但在 AActor::PostActorCreated 之后发生以下操作:
通过一个有效但不完整的Actor实例设置并调用多个"初始化函数"。
调用 AActor::FinishSpawning 以最终确定Actor,在Spawn Actor行中的 AActor::ExecuteConstruction 选取。
Actor生命周期终点
你可以通过多种方式销毁Actor,但从世界中删除它们的方式始终如一。在Gameplay期间,你可以调用以下函数,但是,函数完全可选,因为许多Actor在运行中不会实际销毁(参阅垃圾回收):
当游戏在任何时候需要移除Actor时,手动调用 AActor::Destroy ,但Gameplay仍在继续。Actor被标记为等待销毁并从关卡的Actor数组中移除。
AActor::EndPlay 在数个地方调用,旨在保证Actor的生命走向终点。在游戏过程中,如果包含Actor的流送关卡被卸载,Destroy将调用此方法和关卡过渡(Level Transitions)。
调用EndPlay的全部情形:
对Destroy显式调用。
"在编辑器中运行(Play in Editor)"终结。
关卡过渡(无缝行程或加载地图)。
包含Actor的流送关卡被卸载。
Actor的生命周期已过。
应用程序关闭(全部Actor被销毁)。
无论这些情形出现的方式如何,Actor都将被标记为 RF_PendingKill
,因此在下个垃圾回收周期中,UE会将其从内存中解除分配。此外,可以考虑使用更清洁的 FWeakObjectPtr<AActor>
代替手动检查"等待销毁"。
Actor不一定在调用EndPlay时被销毁。例如,如果 s.ForceGCAfterLevelStreamedOut
为 false
,并快速重新加载子关卡,则将调用Actor的EndPlay,但Actor可能"复活",并与之前存在的Actor完全相同,其本地变量也一样,未重新初始化为其默认值
AActor::OnDestroyed - 这是对Destroy的旧有反应。我们推荐你将此处的逻辑移到EndPlay,因为会由关卡过渡和其他游戏清理函数调用。
垃圾回收
一个对象被标记待销毁的一段时间后,垃圾回收会将其从内存中移除,释放其使用的资源。
在销毁过程中,调用以下函数:
UObject::BeginDestroy - 对象可利用此机会释放内存并处理其他多线程资源(即:图像线程代理对象)。与销毁相关的大多数Gameplay功能理应在EndPlay中更早地处理。
UObject::IsReadyForFinishDestroy - 垃圾回收过程将调用此函数,以确定对象是否可以永久解除分配。返回false,此函数即可延迟对象的实际销毁,直到下一个垃圾回收过程。
UObject::FinishDestroy - 最后,对象将被销毁,这也是释放内部数据结构的机会。这是内存释放前的最后一次调用。
高级垃圾回收
虚幻引擎 中的垃圾回收过程将构建要一并销毁对象的群集。较之于单个删除对象, 群集处理 可减少垃圾回收相关的总时间和总内存抖动。 随着对象加载,可能创建子对象。将对象与其子对象组合到垃圾回收器的单个群集后,引擎可延迟释放群集使用的资源,直到整个对象可被释放时一次性释放全部资源。
多数项目中无需对垃圾回收进行配置或修改,但存在一些特定情况 - 可以如下方式对垃圾回收器的"集群"行为进行调整,以提高效率:
群集处理(Clustering) - 关闭集群。在 项目设置(Project Settings) 中的 垃圾回收(Garbage Collection) 部分下,可将 创建垃圾回收器UObject群集(Create Garbage Collector UObject Clusters) 选项设为 false。对多数项目而言,此操作将导致垃圾回收效率降低,因此只建议在性能测试证明其绝对有益的情况下使用。