Verse Wiki — 写给蓝图作者的 Verse 手册
第八章 · 第 26 课

从 Actor 到 entity:Scene Graph 的世界观

前面 25 课,你学的是一门语言。从这一课开始,你要学的是 UE6 用这门语言重新搭的那套框架:Scene Graph。它只做了一件事,但这件事足够颠覆——把「这个东西什么」换成了「这个东西什么」。你熟悉的 Actor 继承树,在这里被拆成了一堆可插拔的零件。

一、语言学完了,该学框架了

盘一下战利品:变量与类型、if 与失败上下文、for 与 loop、array 与 map、函数与类、suspends 与并发、事件与订阅——这 25 课教的全部是 Verse 这门语言本身。语言是中性的,它不关心你拿它写门、写敌人还是写一个记分板。

但语言总要落在某个框架上才能变成游戏。你过去二十年用的框架叫 Actor:世界里的每样东西都是一个 Actor,Actor 之间靠继承组织,行为写在蓝图类里。UE6 端上来的新框架叫 Scene Graph,官方的描述是「把世界里所有对象连起来的一套统一结构」,而且它从零基于 Verse 构建——不是给 Actor 加一层 Verse 皮,是重新划了一张地基。

那么老实交代利害关系。Actor 与 Blueprint 在 UE6 Early Access(目标 2027 年底)及早期版本中完整支持,你现在打开的那些蓝图资产不会在某个版本号跳变的早晨消失。弃用要等 Scene Graph 足够成熟,时间未定;Epic 已承诺在弃用前提供转换工具,但尚未发布。所以本章不是「逃命指南」,是「先去新家看看户型」。

而且这个新家今天就能进去转:Scene Graph 在 UEFN 里已经可用,官方文档给它标的状态是 Beta——原话大意是「可以学着用,但拿它上线要谨慎」。也就是说,你能亲手把 entity 拖进关卡、给它装 component、写自定义 Verse 组件,只是别指望它的每个边角都已经打磨完。

二、继承树:蓝图作者都懂的那个坑

先复习你现在的世界观。你有一个 BP_Door,能开能关。今天策划过来说:大厅那扇门要会发光。你有三条路:

  1. 改父类。把发光逻辑塞进 BP_Door——于是全项目 47 扇门都长出了一个用不上的发光开关。
  2. 建子类。新建 BP_GlowingDoor 继承 BP_Door,加发光。看起来干净。直到下周策划要「会发光的宝箱」,而宝箱继承自 BP_Chest——发光逻辑得再写一遍,或者复制粘贴。
  3. 加 Actor Component。做一个 BPC_Glow,谁要发光谁挂上。这条路是对的——它正是 Scene Graph 那套思路的雏形,只不过在 Actor 世界里它是「补救手段」,不是默认姿势。

再往下走两步,坑就成型了。策划继续加需求:会发光的门、会发光的宝箱、会发光还会沿轨道移动的门、会移动但不发光的平台、能被击碎的会发光的门……继承树是一棵树,而需求是一张网。用树去铺网,只有两种结局:

结局一:上帝类。为了不重复,你造一个 BP_InteractableBase,把发光、移动、可击碎、可拾取全塞进去,靠一堆布尔开关控制。三个月后它有 2000 个节点、31 个变量,没人敢动。改它一行,47 扇门、12 个宝箱、8 个平台一起进入测试范围。

结局二:菱形。你想让某个东西「既是可移动物又是可交互物」——而这两条能力线各自有自己的父类。蓝图不支持多继承,于是你只能选一边当父类,另一边靠 Actor Component 或者复制粘贴补上。选哪边都别扭,这就是俗称的「菱形问题」。

这两个坑不是蓝图独有的,是把「是什么」当作组织原则的必然结果。只要你的分类轴不止一根(能不能动 × 能不能交互 × 会不会掉血),单根继承线就一定不够用。

三、组合:空容器 + 一叠能力卡

Scene Graph 换了个组织原则:不分类,只装配。

按官方文档的定义,entity(实体)是「component 或其它 entity 的容器」,而且「一个空 entity 没有任何可见效果或功能」。请把这句话读两遍。entity 不是「Actor 的新名字」——Actor 哪怕什么都不加,也自带 Tick、自带 BeginPlay、自带一长串继承来的成员;而 entity 出厂时什么都不会,它是一个纯粹的挂载点。

能力从哪来?component(组件)。官方定义是「component 为 entity 提供数据与行为;装在一个 entity 上的 component 组合,定义了这个 entity 在场景里在做什么」。component 有可编辑属性,可以是物理性的(静态网格、粒子系统),也可以是逻辑性的(玩法标签、你自己写的 Verse 代码)。

于是「会开门又会发光的东西」这个需求,答案朴素到有点扫兴:装两个 component。会发光的宝箱?把同一个发光 component 装到宝箱那个 entity 上。会移动的平台?装移动 component。要哪几样能力,就装哪几张卡——没有父类要选,没有继承树要重排,也没有 47 扇门陪着一起进测试范围。

有两条规则现在就要记住,后面会反复撞上:

顺手看一眼 component 写出来长什么样。下面这段用的是官方 Verse 组件模板的骨架,细节(<final_super> 是什么、生命周期函数各自何时触发)留到第 27 课,这里只感受一下形状:

greeter_component.verse
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# 一张「能力卡」:装到任何 entity 上都能用
greeter_component := class<final_super>(component):

    @editable
    Message:string = "宝箱就位"

    OnBeginSimulation<override>():void =
        (super:)OnBeginSimulation()
        Print("{Message}")
输出日志

点「运行下一步」,看这张能力卡怎么被装上、怎么被触发。

请留意最后一句:这张卡不知道自己装在什么东西上。这是组合式架构的全部魔力,也是它的全部代价——好处是这张卡能装在任何东西上,坏处是你没法一眼看出「这个物件到底会干什么」,得去 Details 面板上把卡片一张张看过来。这笔账怎么算,见文末拓展。

四、三个名词:entity / component / prefab

新框架的名词不多,核心就三个。先用一句话各自定位,再看它们怎么套在一起:

名词 一句话 蓝图作者的类比
entity 空容器,装 component、也装别的 entity 占了 Actor 的位置,但不是同一个东西——它出厂时什么都不会
component 挂在 entity 上的能力单元,提供数据与行为 最接近 Actor Component,但在这里它是唯一的能力来源,不是补救手段
prefab 装配好的 entity 层级模板,可反复实例化 你做好的那个蓝图资产(BP_Door)——拖一份进关卡就是一个实例

三者的关系是包含,不是继承:prefab 里装着一棵 entity 树,树上每个 entity 装着若干 component。官方文档说得很直白:prefab 是「用一套 entity 与 component 的层级,承载所有 prefab 实例共享的基础信息」的稳定对象;在 Prefab Editor 里改动 prefab 并保存,所有实例自动跟着变——这一点和蓝图类改父级、实例跟着变的手感几乎一样。你也可以在某个实例上单独覆写某个 component,被覆写的卡片会打上标记。

再补两条坐标,免得你在 Outliner 里迷路:每个项目的最顶上有一个 simulation entity,整个场景就是从它底下一层层嵌套出来的;而你自己做的 prefab,会以的形式出现在项目的 Assets.digest.verse 里,让 Verse 代码能引用和生成它。层级到底长什么样、生命周期怎么走,见拓展页 entity / component / prefab 三者关系

五、把「是什么」改写成「有什么」

名词记住不难,难的是改口。蓝图作者的默认句式是:「这个东西一个可交互物,它继承自可交互基类。」Scene Graph 的句式是:「这个东西网格、互动、掉落。」

练一下。拿你项目里一定有的 BP_TreasureChest——会亮、玩家按 E 能开、开了掉三件战利品、盖子还有个翻开动画。用旧句式,它「是一个可交互 Actor 的子类」。用新句式,它这些能力:

这个箱子有什么 装什么
看得见的箱体网格类 component(mesh_component)
周围一圈微光灯光类 component(light_component)
玩家能按键互动互动类 component(interactable_component)
盖子会翻开关键帧动画类 component(装在「盖子」这个子 entity 上)
开箱掉三件战利品你自己写的 Verse component

注意第四行:盖子的动画装在子 entity 上,不是装在箱子本体上。为什么?因为盖子要单独转,而 transform 是每个 entity 一份——「需要单独动的部件,给它自己的 entity」是这套框架里最常用的一条手感。

再来两个,自己先想 10 秒再看答案:

练习 A:BP_HealingFountain——一座喷泉,有水花特效,站进去持续回血,回血时有音效。
拆成:网格(泉体)+ 粒子系统 component(水花)+ 声音 component(回血音效)+ 一个自定义 Verse component(检测谁站在范围里、按秒回血)。注意「回血」和「水花」被拆开了:换个美术资产不影响回血逻辑,反过来调数值也不碰特效。

练习 B:BP_MovingPlatform_Glowing——一块沿轨道来回移动的会发光的平台。
拆成:网格 + 灯光 + 移动 component。三张卡各管一件事。现在策划要「会发光但不动的平台」——把移动那张卡拆掉就完事,不需要新建任何类。这就是本课开头那个「会发光的门」问题的最终答案。

改写时有一条自查:如果你写下的 component 名字里出现了「门」「宝箱」「敌人」这种名词,多半拆错了——能力卡的名字应该是动词或形容词(会发光、能互动、会掉落)。名词属于 entity 和 prefab,动词属于 component。

六、蓝图对照

这一课的每个概念,在蓝图里都有对应物。差异那一列才是重点——很多名词看着眼熟,地位却完全不同。

蓝图里的做法Scene Graph 里的对应物差异
Actor——放进关卡的那个东西 entity 占同一个生态位,但 Actor 出厂自带一长串继承来的功能,entity 出厂什么都不会。能力必须靠 component 装上去
Actor Component——挂在 Actor 上的功能块 component 名字几乎一样,地位不同:Actor 不挂 Component 也能干活,entity 不装 component 就什么都不是。而且同一 entity 上同类型 component 只能有一个
Blueprint Class 资产(BP_Door) prefab 手感像(改模板、所有实例跟着变),本质不同:蓝图类之间是继承,prefab 之间是包含。prefab 里放的是一棵 entity 树,不是一条父子类链
World Outliner——关卡里的对象列表 Outliner 里的 entity 层级 层级从「附加(attach)」升格为真正的父子实体:父 entity 直接管辖子 entity 的表现与行为,整个项目的根上有一个 simulation entity
Details 面板——选中对象后的属性栏 还是 Details 面板,但内容是一张张 component 卡片 「Add Component」从可选操作变成主要操作;自定义 Verse 组件也从这里加(Add Component → New Verse Component)
Reparent——给蓝图类换父类 没有对应物 要加能力就装一张卡,要减能力就拆一张卡,不存在「换父类」这个动作,也就不会有牵一发动全身的重排

差异从哪来?从组织原则的更换。Actor 体系用「分类」组织世界:每个东西属于某个类,类之间靠父子关系共享代码。Scene Graph 用「装配」组织世界:每个东西是一个空壳,能力靠拼。前者回答「它是什么」,后者回答「它有什么」——前面五节讲的所有差异,都是这一个换轴动作的后果。

还有一条现实差异值得写明:Actor 体系有二十年的教程、插件和肌肉记忆,Scene Graph 目前在 UEFN 里标着 Beta。这一章教你看懂新户型,不是让你今晚就搬家。

七、关卡挑战

新世界观装配完毕,三道小关卡验收一下。答对拿星星 ★,答错可以一直重试,零惩罚。

在 Scene Graph 里,一个刚创建、还没装任何 component 的 entity,会做什么?

策划要一扇「会开关、会发光、还能沿轨道移动」的门。在 Scene Graph 里,标准做法是?

你做好的蓝图资产 BP_Door,在 Scene Graph 里最接近哪个概念?两者的本质差异是什么?

拓展阅读

拔高 · EXTRA

组合优于继承:为什么 Epic 要换掉 Actor 继承树

菱形问题、上帝类、改父类波及全世界——继承在大型项目里是怎么崩的。以及诚实地写清楚组合的代价:间接层变多、调试更难。

进入拓展 →

拓展 · EXTRA

entity / component / prefab 三者关系

一张表 + 一段层级示意,讲清三者的包含关系与生命周期,以及「关卡里那棵树」到底长什么样。

进入拓展 →

技巧 · EXTRA

把一个 Actor 蓝图拆成 components 的思维练习

拿「会巡逻、能被击杀、掉落战利品的敌人」开刀,一步步拆成清单;拆的三条原则,以及拆过头和拆不够长什么样。

进入拓展 →