UE_C++ —— UE反射系统(三)
目录一UObject Instance CreationObject Flags二Unreal Object Handling一UObject Instance Creation创建对象新实例的方法// NewObject() 是最简单的UObject工厂方法接受一个可选的外部对象和类并用自动生成的名称创建一个新实例 template class T T* NewObject ( UObject* Outer (UObject*)GetTransientPackage(), // 可选的对象外部 UObject UClass* Class T::StaticClass() // 可选的对象类的 UClass )// NewNamedObject() 是在 NewObject() 上扩展通过为新实例指定名称、对象标记、模板对象 template class TClass TClass* NewNamedObject ( UObject* Outer, FName Name, EObjectFlags Flags RF_NoFlags, UObject const* Template NULL // 创建新对象时用作模板的 UObject )// 此函数调用 StaticConstructObject() 来分配内存执行 ClassConstructor 并初始化如加载配置属性加载本地化属性和实例化组件 template class T T* ConstructObject ( UClass* Class, UObject* Outer (UObject*)GetTransientPackage(), FName Name NAME_None, EObjectFlags SetFlags RF_NoFlags, UObject const* Template NULL, bool bCopyTransientsFromClassDefaults false, struct FObjectInstancingGraph* InstanceGraph NULL )Object FlagsEObjectFlags枚举用于快速而简洁地描述对象有多种描述对象的标志、垃圾收集如何处理、对象在其生命周期中所处的阶段等等还有特殊的全掩码或无掩码和预定义的标记组Object TypeFlagValueDescriptionRF_Public0x00000001对象在包含它的包外部是可见的RF_Standalone0x00000002即使没有被任何东西引用对象也会被保留以供编辑RF_Native0x00000004对象是本地对象仅用于UClass对象RF_Transactional0x00000008对象是事务对象RF_ClassDefaultObject0x00000010对象是其类的默认对象即该类的新实例在创建时使用的默认模板RF_ArchetypeObject0x00000020对象是另一个对象的模板。它被视为一个类默认对象RF_Transient0x00000040对象不保存到磁盘Garbage CollectionFlagValueDescriptionRF_RootSet0x00000080即使对象没有被任何东西引用也不被垃圾回收RF_IsLazyReferenced0x00000100对象由一个延迟指针引用删除时需要额外的清理RF_Unreachable0x00000400对象图表上不能访问该对象RF_TagGarbageTemp0x00000400对象被标记为供使用垃圾回收的各种实用程序所使用此标记不由垃圾回收器本身解释Object LifetimeFlagValueDescriptionRF_NeedLoad0x00000800对象需要加载RF_AsyncLoading0x00001000对象是异步加载的RF_NeedPostLoad0x00002000对象需要后加载RF_NeedPostLoadSubobjects0x00004000对象仍然需要实例化子对象并修复序列化的组件引用RF_PendingKill0x00008000对象正在等待销毁基于游戏目的将对象标记为无效但仍然是有效的对象RF_BeginDestroyed0x00010000对象已经调用了BeginDestroy()RF_FinishDestroyed0x00020000对象已经调用了FinishDestroy()Special MasksFlagValueDescriptionRF_AllFlags0x0003ffff对象具有所有标记主要用于错误检查RF_NoFlags0x00000000对象不具有标记用于避免类型转换Predefined GroupsFlagValueDescriptionRF_LoadRF_Public | RF_Standalone | RF_Native | RF_Transactional | RF_ClassDefaultObject | RF_ArchetypeObject从虚幻文件加载的标记RF_PropagateToSubobjectsRF_Public | RF_ArchetypeObject | RF_Transactional子对象从其超对象继承的标记二Unreal Object Handling使用适当的宏标记类、属性和函数可以将其转变为UClasses、UProperties和UFunctions以让引擎能够访问它们从而实现一些后台处理功能Automatic Property Initialization在调用构造函数之前UObject在初始化时自动归零会作用于整个类如UProperties和原生成员成员随后可在类构造函数中使用自定义值进行初始化Automatic Updating of References当AActor或UActorComponent被销毁或从运行中删除时对反射系统可见的对它的所有引用UProperty指针和引擎容器类中存储的指针如TArray都将自动清空以防止悬空指针持久存在并导致后续问题如果其他某段代码将AActor和UActorComponent指针销毁时这些指针也会变为空这种功能仅适用于标记了UPROPERTY或存储在引擎容器类中的UActorComponent或AActor引用存储在原始指针中的Object引用对于引擎未知不会自动清空也不会妨碍垃圾回收注意这不意味着所有UObject*变量都必须是UProperties如需要的Object指针不是UProperty考虑使用TWeakObjectPtr不妨碍垃圾回收但可查询有效性再访问且所指向的Object被销毁时将被置空另一种被引用UObject UProperty自动清空的情况是对编辑器中的资源使用强制删除Force Delete因此作用于属于资源的UObject的所有代码都必须处理这些指针变为空的情况Serialization当UObject被序列化时所有UProperty值都将被自动写入或读取除非显式标记为transient或无法从post-constructor默认值更改如可在关卡中放入AEnemy实例将其Health设置为500保存并成功地重新加载而不必在UClass定义之外编写一行代码当添加或移除UProperties时系统会无缝处理预先加载存在的内容新属性从新的CDO复制默认值移除的属性将会被静默忽略如需自定义行为可以重写UObject::Serialize函数这对于检测数据错误检查版本号或执行自动转换或更新如数据格式有所更改十分有用Updating of Property Values当UClass的类默认对象CDO更改引擎将尝试在加载类的所有实例时对这些实例应用这些更改对于给定Object实例如更新的变量值与旧CDO中的值相匹配则将更新为它在新CDO中保存的值如假设在一个关卡中放置了多个AEnemyObject 并保存然后将AEnemy构造函数中的默认Health值设置为100再假设将Enemy_3的Health值设置为500现在假设改变主意将Health的默认值增加到150下次加载关卡时使用旧默认Health值100的所有AEnemy实例将更新为150Enemy_3的Health将保持在500因为它不使用旧的默认值Editor Integration编辑器理解UObject和UProperties编辑器可以自动公开这些值以供编辑而不必编写特殊代码可选择在蓝图视觉脚本系统中融入集成也有许多选项可以控制变量和函数的可访问性和公开Run-Time Type Information and Casting由于UObject是引擎反射系统的一部分始终知道UClass并可在运行时做出有关类型的决定和类型转换在原生代码中每个UObject类都将自定义Super类型定义设置为其父类从而可以轻松控制覆盖行为class AEnemy : public ACharacter { virtual void Speak() { Say(Time to fight!); } }; class AMegaBoss : public AEnemy { virtual void Speak() { Say(Powering up! ); Super::Speak(); } }; //调用Speak将会让MegaBoss说Powering up!Time to fight!此外可以使用模板化Cast函数安全地将Object从基类转换为更派生类或使用IsA查询如Object是特定类class ALegendaryWeapon : public AWeapon { void SlayMegaBoss() { TArrayAEnemy EnemyList GetEnemyListFromSomewhere(); // The legendary weapon is only effective against the MegaBoss for (AEnemy Enemy : EnemyList) { AMegaBoss* MegaBoss CastAMegaBoss(Enemy); if (MegaBoss) { Incinerate(MegaBoss); } } } };Garbage Collection虚幻实现了一套垃圾回收机制不再被引用或已被显式标记为销毁的UObject将定期清理引擎构建一个引用图表以确定哪些UObject仍在使用哪些是孤立的该引用图表的根节点是一组被指定为“root set”的UObject任何UObject都可以添加到root set当进行垃圾回收时引擎将从root set开始搜索已知UObject引用树来跟踪所有引用的UObject任何未被引用的UObject意味着未在树搜索中找到这些对象将被假设为不再需要被删除一个实际的影响是通常需要对保持存活的任何Object维护一个UPROPERTY引用无论是一个简单的对象指针或存储在TArray或其他引擎容器类中对象指针Actor及其组件通常属于例外情况因为Actor通常会被可追溯到根集的Object引用如它们所属的关卡而Actor的组件被Actor自身引用可通过调用Destroy函数来显式标记Actor为销毁这是从运行中游戏移除Actor的标准方法组件可以使用DestroyComponent函数显式销毁通常在拥有它们的Actor从游戏中移除时自动销毁引擎中的垃圾回收速度快效率高内置大量的优化功能能够尽量降低开销如用于识别孤立对象的多线程可达性分析及经过优化可尽快将Actor从容器中移除的unhashing代码还有一些其他功能以调节更精准地控制如何以及何时执行垃圾回收大部分都可以在Project Settings/Engine / Garbage Collection下找到Setting(s)Feature DescriptionCreate Garbage Collector UObject Clusters如打开默认打开相关Object将被分组到一起归入垃圾回收集群这样只需要检查集群自身即可而不必检查每个Object将整个集群视为一个对象可更快速地执行可达性分析但也意味着该集群中的单个项目在同一帧中删除如集群足够大可能会导致卡顿一般而言集群创建会提高垃圾回收性能缩短可达性分析耗费的时间Merge GC Clusters当一个簇的对象引用另一个簇中的对象时可以启用簇合并功能使簇能够连接在一起注意清除导致合并的引用并不会使新合并的簇解散或分裂要使此功能生效还需启用Create Garbage Collector UObject Clusters这将使垃圾回收器更少地unhashing和销毁对象但会一次性unhashing和销毁更多对象但在某些情况下合并后的簇可能不会像未合并时那样进行垃圾回收因为对簇中任何对象的任何引用都会阻止整个簇被垃圾回收Actor Clustering Enabled在Project Settings中开启并将bCanBeInCluster变量设置为true或重写代码中的CanBeInCluster函数以使其返回true可以将Actor放入集群中默认Actor和组件会将该选项关闭但静态网格体Actor和反射捕获组件除外此功能对于将预期会一次性全部销毁的Actor通常是指放置在关卡中的静态网格进行打组非常有用这些静态网格只能通过卸载包含它们的子关卡才能销毁Blueprint Clustering Enabled蓝图的UBlueprintGeneratedClass和相关数据如共享UPROPERTY和UFUNCTION数据可以通过打开该设置来建立集群必须要认识到的是该集群引用蓝图生成的类自身而不是蓝图的单个实例Time Between Purging Pending Kill Objects垃圾回收活动的频率可以在项目设置中调整该高级控制对于防止卡顿尤其有用通过缩短回收间隔可以减少将在下一次可达性分析阶段发现的无法访问的对象的可能数量并避免同时清除大量Actor时可能会发生的卡顿Network ReplicationUObject系统包含一组可靠的功能能够促进网络通信和多人游戏UProperties可以标记为告知引擎在网络游戏期间复制数据常见模型是一个变量在服务器上发生更改引擎检测到这个更改并将其可靠地发送到所有客户端当变量通过复制发生更改时客户端可以选择性接收回调函数UFunctions也可以标记为在远程机器上执行如server函数在客户端上调用时将会在服务器上执行这个函数以获取服务器版本的Actor而另一方面client函数可以从服务器调用并在拥有这个函数的客户端版本的对应Actor上运行