UObject 是构建 UE 世界的基础,该类提供了元数据、反射生成、GC、序列化、编辑器可见等等功能


AActor

AActor 在 UObject 的基础上,又增加了:Replication(网络复制)、Spawn、Tick 等,Actor 之间又可以相互嵌套,形成父子关系

思考:Actor 为何不像 GameObject 一样自带 Transform**

这里的 Actor 比起能够放置在世界中用来展示外观的物体不同,除了能表示这种物体之外,Actor 还能作为一些抽象类的基类。

而这些抽象数据类对于位置信息的依赖性不强,或者说根本没有依赖。这些在世界里不可见的对象,可以代表这个世界的某种信息、规则、状态,例如:ActorInfo 派生类(AWorldSetting, AGameMode, AGameSession, APlayerState, AGameState 等)、AHUD、APlayerCameraManager 等

既然不需要那就没有必要往其中增加不必要的成分了。

最终实现结果:将 Transform 封装到 SceneComponent 中,作为 RootComponent。像是 GetActorLocation 等,内部都是转发到 RootComponent


UActorComponent

UActorComponent 继承自 UObject

Actor 作为游戏世界(具象和抽象)信息的载体,Component 则将一些功能抽象化形成可复用组件,能够添加到 Actor 中,丰富 Actor 的功能。

具体的来说,Actor 中有一个TSet<UActorComponent*> OwnedComponents,保存着 Actor 所拥有的所有 Component,并且其中的一个 SceneComponent 会作为 RootComponent。另一个TArray<UActorComponent*> InstanceComponents保存着实例化的 Components

可以看到,从 UActorComponent 下分出了一个 USceneComponent,USceneComponent 又分出了另外的 Component

真正提供组件嵌套功能的,是 USceneComponent,参考下图:

USceneComponent 提供了两个能力:

  • Transform
  • 互相嵌套

思考:为什么 UActorComponent 不提供嵌套?非要在 USceneComponent 才提供?

UActorComponent 如同 AActor 一样,可能都表示一些抽象上的信息或操作。而嵌套在 UE 里,一般只有带着 Transform 的才会被允许嵌套

并且 UActorComponent 下其实不止有 USceneComponent,比如 UMovementComponent、AIComponent,或者是我们自己写的继承自 UActorComponent 的 Component

在编写我们自己的 Component 的时候,如果只是希望这个 Component 提供一些特定数据处理的功能,那么 Transform 功能和嵌套功能是否就没那个必要了

另外从设计上,直接从 UActorComponent 提供嵌套,势必会增大组件的灵活性,但同时带来组件的相互依赖、相互通信、嵌套过深等问题同样不可忽略。如果允许随意嵌套,必然会在使用上增大出错的概率

思考:如何确定 Actor 之间的父子关系?

在 UML 图中可以看到,Actor 中存在一个TArray<AActor*> Children,不过 Actor 之间实现父子关系是通过Component确定的,利用Child::AttachToActorChild::AttachToComponent

由于一个 Actor 中可以存在多个 SceneComponent,所以要将 Child 连到哪一个 SceneComponent 上要思考清楚。更进一步,还能通过传入 SocketName 来表明要 Attach 到 Mesh 的哪个位置,并提供变换规则(EAttachmentRules)

参考链接