entity / component / prefab 三者关系
课内把三个名词各自介绍了一遍,这一页把它们摆到一起:谁装谁、谁的生命周期先开始、以及你在 Outliner 里看到的那棵树是怎么长出来的。定义尽量引官方原文,API 成员表取自官方 Verse API 参考。
一、三张定义卡
先把官方定义摆正,后面所有推论都从这三句来:
| 名词 | 官方定义(大意) | 关键性质 |
|---|---|---|
entity |
component 或其它 entity 的容器;空 entity 没有可见效果也没有功能 | 可嵌套。默认自带 transform component(在 Verse 里构造的 entity 除外) |
component |
为 entity 提供数据与行为;装在一个 entity 上的 component 组合定义了它在场景里做什么 | 不可嵌套(component 里不装 component)。同一 entity 上同类型只能有一个 |
prefab |
用一套 entity 与 component 的层级,承载所有实例共享的基础信息的稳定对象 | 可实例化多次;改 prefab,所有实例自动跟着变;单个实例可覆写某个 component |
三句话读完,包含关系就出来了,而且是严格的一层套一层:
prefab ⊃ entity 树 ⊃ 每个 entity 上的 component 清单。
注意这里全是「包含」,没有一处是「继承」。蓝图作者最容易在这里翻车:看到「改 prefab、所有实例跟着变」就下意识把 prefab 当成父类。它不是。父类和子类之间是「你是一种我」,prefab 和实例之间是「你是我压出来的一份拷贝」——手感像,机制不同。
二、关卡里那棵树长什么样
整个项目的最顶上有一个 simulation entity,场景就是从它底下一层层嵌套出来的。父 entity 会管辖子 entity 的表现与行为。把课内那个宝箱画出来,大概是这个形状:
simulation entity ← 项目根,每个项目一个
└─ Level entity
└─ P_TreasureChest entity(prefab 根)
│ · mesh_component 箱体网格
│ · light_component 周围一圈微光
│ · interactable_component 玩家能按键互动
│ · chest_loot_component ← 你自己写的 Verse component
│
└─ Lid entity(子实体:盖子)
· mesh_component 盖子网格
· 关键帧动画 component 盖子单独翻开
读图三个要点:
- 前缀
·的是 component,不是子节点。它们挂在那个 entity 身上,不占层级的一格——层级里只有 entity。 - 盖子为什么单独一层?因为盖子要独立于箱体转动,而 transform 是每个 entity 一份。「需要单独运动的部件,给它自己的 entity」是这套框架里最高频的一条手感。同理,一个物件要两盏灯,也得拆成两个子 entity——同一 entity 上同类型 component 只能有一个。
P_TreasureChest这一整块可以整体保存成 prefab。保存之后,你在关卡里拖出的每一份都是它的实例;在 Prefab Editor 里改一次,所有实例跟着变。被单独覆写过的 component 卡片会在界面上打标记,提示这份实例已经偏离模板。
还有一件对写代码的人很重要的事:你做的 prefab 会以「类」的形式出现在项目的 Assets.digest.verse 里,于是 Verse 代码能引用它、生成它的实例并挂到场景上。这就是「关卡资产」和「代码」在新框架里的接缝。
三、生命周期:谁先醒,谁后睡
蓝图作者对生命周期的印象大概是三个词:BeginPlay、Tick、EndPlay。Scene Graph 的 component 把这条链拉得更长一点,官方 API 参考里 component 类上的生命周期函数是这几个:
| 函数 | 什么时候 | 蓝图里最近的亲戚 |
|---|---|---|
OnAddedToScene | 组件被加入场景时 | 大致对应「构造/注册」这一段,比 BeginPlay 更早 |
OnBeginSimulation | 组件开始参与模拟时 | 最接近 Event BeginPlay,但作用域是「这个组件」 |
OnSimulate | 组件的异步更新逻辑 | 类似把 Tick 换成一段标了 <suspends> 的长跑代码 |
OnEndSimulation | 组件结束模拟时 | Event EndPlay |
OnRemovingFromScene | 组件即将被移出场景前 | 比 EndPlay 更靠后的收尾钩子 |
顺序上的直觉是安全的:先进场景,再开始模拟;先结束模拟,再离开场景。课内步进器里那句 (super:)OnBeginSimulation() 就是这套体系的礼节——重写生命周期函数时先把父类那份跑掉,官方模板与文档示例都这么写。
component 上还有两个常用数据成员:Entity(这个组件所属的那个 entity,也就是你从组件里往外看世界的入口)和 TickEvents(物理前/物理后的逐帧回调)。另外有一组状态与操作函数:IsInScene、IsSimulating、RemoveFromEntity,以及场景事件相关的 SendDown 与 OnReceive。
对应地,entity 这一侧的函数按用途分成四组:
- 层级:
GetParent、GetEntities、AddEntities、RemoveFromParent - 组件:
GetComponent、GetComponents、AddComponents - 场景事件:
SendUp(沿层级向上发)、SendDown(沿层级向下发) - 标签:
AddTag、RemoveTag、ContainsTag、ContainsAnyTag等一组标签操作
两个类都住在 /Verse.org/SceneGraph 模块里——所以写自定义组件的第一行是 using { /Verse.org/SceneGraph }。这里只列函数名与用途,确切的签名、可失败性(方括号还是圆括号)与效果说明符,以官方 Verse API 参考为准;它们在 Beta 期间有变动的可能。真正动手写这些调用是第 27 课的事。
四、一句话对照
把三者和蓝图世界拉平了看,最省事的记法是:
prefab 是你双击打开的那个资产;entity 是 Outliner 里的一行;component 是 Details 面板上的一张卡。
而 Actor 时代之所以只有两个层级(资产 + Outliner 的一行),是因为「一张卡」那一层被继承吃掉了——能力藏在父类里,不出现在面板上。Scene Graph 把它挪回了面板上,代价是面板变长,好处是一个物件会干什么,全写在脸上。
五、小测验
一个宝箱需要「箱体不动、盖子单独翻开」。按 Scene Graph 的组织方式,盖子应该怎么放?
来源
本文整理自 Epic 官方文档与 Verse API 参考:Getting Started in Scene Graph in Fortnite(官方文档)↗ · Prefabs and Prefab Instances in UEFN(官方文档)↗ · Verse API:entity 类(官方文档)↗ · Verse API:component 类(官方文档)↗
层级示意图是本站按上述定义绘制的教学插图,不是官方素材;函数的确切签名以官方 API 参考为准。Scene Graph 目前在 UEFN 中标注为 Beta,细节可能随版本变化。