这是一篇关于UE中蓝图Construction Script和C++中Constructor、OnConstruction
官方文档参考:Actor Lifecycle Construction Script
Constructor
C++的构造函数是要比OnConstruction、Construction Script、C++Beginplay、蓝图Beginplay要早的
Construction Script & OnConstruction
在蓝图编辑器中访问构造脚本

在 C++ 中,它由 UserConstructionScript 方法表示:
/**
* Construction script, the place to spawn components and do other setup.
* @note Name used in CreateBlueprint function
*/
UFUNCTION(BlueprintImplementableEvent, meta=(BlueprintInternalUseOnly = "true", DisplayName = "Construction Script"))
ENGINE_API void UserConstructionScript();
这个方便的工具对于定义如何构建 Actor 非常有用。一个典型的用例是当您希望能够设置 Actor 的属性并看到它自动地根据编辑器中的这些参数进行更新时。例如,我们可以使用它来构建一个以边数作为属性的多边形Actor,每次编辑边数时,我们都可以看到多边形在编辑器中重建和更新(而不是在运行时,但是在运行前,会运行一次构造脚本)。
但是,虽然它非常有用,但如果使用不当,也非常危险,甚至导致编辑器崩溃。
为了避免任何潜在的问题,我们需要:
- 知道何时调用构造脚本
- 了解它如何导致编辑器崩溃,使项目无法编辑,甚至一打开就崩溃
- 学习如何正确地使用它(构造脚本并不总是唯一的办法)
OnConstruction和UserConstructionScript统一由ExecuteConstruction函数调用并且UserConstructionScript调用要早于OnConstruction
编辑器何时调用构造脚本
这是构造脚本最有趣的一点:它是在编辑器中调用的,而不是在运行时调用的。
并且在编辑器中,可以多次调用它:
- 当Actor产生(Spawn)时
UWorld::SpawnActor -> Actor->PostSpawnInitialize -> AActor::FinishSpawning -> ExecuteConstruction - 当Actor移动时(PostEditMove事件):在这种情况下,当此Object移动时它将被调用多次
PostEditMove -> RerunConstructionScripts -> ExecuteConstruction - 当Actor的属性发生更改时(PostEditChangeProperty事件)
PostEditChangeProperty -> RerunConstructionScripts -> ExecuteConstruction
正如我们所看到的,它可以被频繁调用。虽然它确实很有用(如果我更改任何属性,我可以直接看到构造脚本正在重建的对象),但也出现了一些缺点。
另外
/** Called after an actor has been moved in the editor */
virtual void PostEditMove(bool bFinished);
/**
* Called when a property on this object has been modified externally
*
* @param PropertyThatChanged the property that was modified
*/
virtual void PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent);
重载时(参考PaperGroupedSpriteComponent和VPRootActor)一般添加#if WITH_EDITOR与#endif
// UObject interface
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
// End of UObject interface
#if WITH_EDITOR
virtual void PostEditMove(bool bFinished) override;
#endif
滥用的主要威胁
使用构造脚本时存在两个主要威胁。第一个很明显,第二个则比较棘手。
第一个是对项目性能的影响:即使只在编辑器中调用Construction Script,如果Construction Script太复杂,将无法再在编辑器中移动Actor。假设运行 ConstructionScript 需要一秒钟,如果尝试移动 Actor,则在移动 Actor 时,Construction Script 将被调用数十次,因此编辑器可能卡数十秒。那么对于美术同学来说,记住不要移动这个特定对象将是一件非常痛苦的事情,因为总是会对这个Actor摆放的位置不满意。
第二个威胁更加棘手。它涉及构造脚本的循环调用:如果属性的更改导致Construction Script的循环调用,编辑器将崩溃。原理如下:假设我们在一个 Actor 上有一个属性 A 和一个属性 B,并且Construction Script声明如果 A 改变,则 B 等于 A+1,如果 B 改变,则 A 等于等于B+1。理论上,这可能会由于无限循环而造成的崩溃。幸运的是,这种特定情况是由编辑器管理的:Construction Script无法调用自己(即使它更改了A/B属性)。但是,当您的演员具有ChildActorComponent并且这个Actor和这个ChildActorComponent之间存在循环关系时,编辑器将无能为力,它只会崩溃(根据Isara的经验,不是每次都会崩溃,这使得诊断变得更加困难)。因此,如果父级的构造脚本更改了子级的属性,并且子级的构造脚本更改了父级的属性,我们将进行循环调用(在两个不同的构造脚本之间)。
建议
构建脚本的使用建议如下:
- 如果没有必要,请避免使用它们:它是一个自动化工具,仅当您有重复性任务需要自动化时才使用它
- 保持构建脚本简单快速,它只是作为一个方便的工具而不是当成制作一个完全程序化的游戏
- 避免在 Construction Script 中更改另一个 actor(甚至是子 actor)的属性,如果必须这样做,请记住,对
Construction Script的循环调用可能会使编辑器崩溃