拓展 · EXTRA
没有对应物的那些:Construction Script、Timeline、Animation Blueprint
三个查不到译名的词,逐个说清它们在做什么、为什么 Verse 里没有直接对应,以及 UE6 里大概会怎么处理。
进入拓展 →学一门新语言,最贵的成本不是语法,是「我知道我要什么,但不知道它在那边叫什么」。这一课是一本可以反复查的词典:六张分领域的对照表,把蓝图里的每个东西翻译成 Verse 的说法,再指出哪些是同名不同物、哪些根本没有对应物。查完这一页,你后面每一课都会轻松一截。
你已经知道怎么做一扇会开的门:变量面板里加一个 Boolean 叫 IsOpen,Event BeginPlay 里 Bind 一下按钮的事件,回调里接一个 Branch,走通的那条线上挂一个 Set 节点。这套流程你闭着眼睛都能连出来。
换到 Verse,你要做的还是这件事。真正卡住你的是另一个问题:Boolean 那个类型,在下拉菜单里选什么?翻遍文档也找不到 bool —— 因为它在 Verse 里叫 logic。你不是不会,你只是不知道它改了名。
这种「我知道我要什么,但不知道它在那边叫什么」的卡顿,会在你写 Verse 的头两周里反复发生几十次。每一次都不难,但每一次都要停下来查十分钟。这一课就是把这几十次一口气解决掉:六张对照表,五十多个词,一次查清。
每张表都是三列:蓝图里的叫法 / Verse 里的叫法 / 差异。前两列是查询用的,第三列才是真正值钱的部分 —— 很多词看着是一一对应,行为却不完全一样,踩过一次才知道疼。收藏这一页,以后写代码时开着它。
先从最常查的一组开始:你在变量面板的 Variable Type 下拉里能选到的那些东西。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
| Blueprint Class(蓝图类资产) | class |
不是资产,是 .verse 文本文件里的一段声明。同样只能有一个父类,同样能实现多个接口 |
| Structure(结构体资产) | struct |
同样是值语义(整包复制)。但 Verse 的 struct 里不能放可变字段、不能写方法,比蓝图结构体更「纯」 |
| Enumeration(枚举资产) | enum |
背后不藏整数,不能转成数字或文字。分头处理交给 case |
| Blueprint Interface(BPI) | interface |
概念完全一致。实现方的每个方法必须挂 <override>;没有蓝图那种「Message 调用 vs 直接调用」的区分 |
| Integer | int |
只是改了名。一个冷知识:整数 ÷ 整数得到的不是整数,是精确分数 rational |
| Float | float |
写小数必须带小数点(1.0 而不是 1)。int 与 float 不会像蓝图那样自动互转,混着算会报错 |
| Boolean | logic |
改了名,而且不能直接当 if 的条件 —— 要写 if (IsOpen?),那个问号把「真 / 假」翻译成「走得通 / 走不通」 |
| String / Text / Name(三种文字类型) | string(只有一种) |
Verse 没有 Text 与 Name 的三分。UE 里 Text 管本地化、Name 管高速比对,这两件事在 Verse 里各有别的机制,不体现为类型 |
| Array(数组容器) | array |
Verse 的数组是不可变的值,「改数组」其实是整包换成一个新的。按序号取是「可能走不通」的,越界不会崩游戏 |
| Map(映射容器) | map |
查和写都可能走不通,连写入都得包在 if 里。能当键的类型必须是「可比较」的那一类 |
| Set(集合容器) | 没有内建对应物 | Verse 不提供集合类型。惯用替代是 map(t, logic),只用键、把值当占位;「去重」这件事得你自己维持 |
| Object Reference(对象引用引脚) | 直接写类型名,如 my_door |
没有单独的「引用类型」写法 —— 类的实例天生就是引用。「可能为空」不靠运行时判断,而是写进类型里:?my_door |
这张表里最值得记住的三行是 Boolean、String 和 Set。前两个是「改了名 + 行为不同」,第三个是「干脆没有」。剩下的基本可以直译过去。
第二组是变量面板本身:新建、读、写、暴露、权限。这一组的对应关系最整齐,几乎每一行都能直译。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
| 变量面板(My Blueprint › Variables) | 类里缩进一级的字段声明 | 没有面板,声明写在哪儿就属于哪儿。「变量属于这个类」这件事由缩进直接表达 |
| Get 节点 | 直接写名字 | 读一个值不需要任何节点,写出名字就是读它 |
| Set 节点 | set 名字 = 值 |
set 是必须的关键字。漏掉它,单独一个 = 会被当成「比较」,报出一句新手看不懂的错 |
| Default Value(默认值那一栏) | 声明处的 = 值 |
函数里、模块里的声明必须当场给初始值;类的字段可以不给,由使用者在 Details 面板补 |
| Instance Editable / Expose on Spawn(那只小眼睛) | @editable |
一个属性同时管两件事:出现在 Details 面板、每个实例可以各填各的 |
| Private / Protected / Public(访问权限下拉) | <private> / <protected> / <public> |
默认档位不是 public,而是「同模块可见」。另外 Verse 能把「谁能读」和「谁能改」分开标 |
| Local Variable(函数局部变量) | 函数体里的 var 名字:int = 0 |
局部的也默认不可变 —— 不写 var 就是局部常量。作用域跟着缩进走 |
| Const(蓝图变量做不到) | 什么都不写 | Verse 没有 const 关键字,因为「不可变」是默认待遇。反过来是「可变」才要申请 var |
最后一行值得单独盯一眼:蓝图里没有「不可变变量」这个概念,所有变量都能接 Set。Verse 把默认值反了过来 —— 这不是刁难,是让别人打开你的代码时一眼就能看出哪些值是死的、哪些是活的。
第三组是执行线上的节点。这一组差异最大,因为 Verse 把「失败」当成一等公民,很多蓝图里靠 Boolean 和 Is Valid 解决的问题,在 Verse 里换了一套机制。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
| Event BeginPlay | OnBegin<override>()<suspends>:void = |
它自带 <suspends>,可以在里面等待若干秒、等事件,是一根能挂起的长线。BeginPlay 做不到这一点 |
| Branch | if (条件): |
Verse 的 if 收的不是 Boolean,是「这一步走不走得通」。所以 Cast 失败、数组越界、查不到键,全都走到同一个 else |
| Sequence | 顺序写下去 | 没有对应节点 —— 缩进在同一级的几行,本来就是从上到下依次执行 |
| ForEach Loop | for (X : Arr): |
整个 for 跑完会攒出一个新数组,天生能「边跑边收集结果」。要序号写 for (I -> X : Arr) |
| For Loop(带索引) | for (I := 0..9): |
范围写法 0..9 含两端,和 ForEach 是同一个关键字。Verse 把两种循环合并成了一个 |
| While Loop | loop + break |
Verse 没有 while,也没有 continue。条件搬进循环体,用 if (…) { break } 跳出 |
| Delay(带小钟表的 latent 节点) | Sleep(1.0) |
调用它的函数必须挂 <suspends>。它是「异步」的,因此不能塞进 if 的条件里 |
| Is Valid / Accessed None 报错 | option(?t)与失败上下文 |
「可能为空」写进类型里,取值用 X? 并放在 if 中。运行时的 Accessed None 崩溃在 Verse 里不存在 —— 编译期就逼你处理 |
| Switch on Enum | case (值): |
分支必须覆盖全部取值,或写一条 _ => 兜底,否则编译不过 |
| Return Node | 隐式返回(函数体最后算出的值)/ return |
大多数时候不用写任何东西 —— 最后一个表达式的值自动就是返回值。想提前退出才写 return |
还有一类蓝图里根本没有的东西:同时跑几条线。蓝图要做「一边倒计时一边等玩家踩板」得靠 Timeline 或 Tick 手工拼;Verse 把 sync(全员到齐)、race(谁先谁算)、rush(先到先得但都跑完)、branch(岔一条线出去)直接做进了语言里。这一组在第七章展开。
第四组是「代码怎么被组织、怎么被触发」。Event Dispatcher 那一行是很多人迁移时最先要用到的。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
| Function(蓝图函数) | 类里的函数 DoThing():void = … |
多一种蓝图没有的花样:挂上 <decides> 的函数「可能走不通」,调用时用方括号 DoThing[] |
| Macro(蓝图宏) | 没有直接对应物 | 蓝图宏是「会被展开的图」,存在的理由之一是节点图不方便复用执行线。Verse 里写个普通函数就够了 |
| Pure 节点(绿色、没有执行引脚) | 没有副作用的函数(<computes> / <converges>) |
蓝图靠勾一个 Pure 复选框,Verse 靠在函数上申报「效果」。区别是 Verse 会检查你申报得对不对 |
| Event Dispatcher(事件分发器) | event(t) |
事件在 Verse 里是一等值:可以当字段、当参数传、放进数组。蓝图的分发器只能挂在类上 |
| Call(调用分发器) | Signal(数据) |
一样是「广播一声」,可以捎带一份数据 |
| Bind Event(绑定) | Subscribe(回调函数) |
回调的参数必须和事件递来的数据对得上。Subscribe 会回一个把手,调 Cancel() 解绑;重复绑会叠加触发 |
| Custom Event(自定义事件) | 类的一个普通方法 | Verse 里「事件」和「函数」是两个概念。蓝图的自定义事件,九成场合翻译过来就是一个方法 |
| Blueprint Function Library | 模块里的 <public> 函数 |
不需要专门建一个「库」资产。项目里每个文件夹自动是一个模块,函数挂上 <public> 别人就能用 |
| Implements Interface + Interface Call | my_class := class(my_interface): + 方法挂 <override> |
没有「Message 调用 vs 直接调用」的分裂 —— 只有一种调法,编译期就保证对方实现了 |
这一组必须先说清状态,否则查表会查出误会。
今天(2026 年 8 月):Verse 唯一能真正跑起来的地方是 UEFN,那里的「关卡里的东西」叫 creative_device。UE6 的方向:Epic 为 UE6 做了一套全新的玩法框架 Scene Graph,它从零基于 Verse 构建,用 entity(实体)装 component(组件),在 UEFN 中已经可以使用。同时 Epic 明确表示:Actor 与 Blueprint 在 UE6 Early Access(目标 2027 年底)及早期版本中完整支持,弃用要等 Scene Graph 足够成熟,时间未定;Epic 已承诺在弃用前提供转换工具,但尚未发布。
所以下表的 Verse 一列会给两个答案:今天怎么写、UE6 的方向是什么。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
| Actor | 今天:creative_device;方向:Scene Graph 的 entity |
Actor 是一棵继承树的根,你靠「派生一个子类」加功能;entity 是一个几乎空的容器,你靠「装组件」加功能。这是两种模型,不只是改名 |
| Actor Component / Scene Component | Scene Graph 的 component |
名字一样,分工不一样:在 Scene Graph 里,组件是唯一放行为的地方,不再有「Actor 自己也写一堆逻辑」这回事 |
| Level Blueprint(关卡蓝图) | 一个 .verse 文件里的类,把它拖进关卡 |
没有「关卡蓝图」这个特殊容器。想要关卡级的逻辑,就做一个设备放进去 —— 好处是它可以复制、可以复用 |
| Details 面板 | 用 @editable 暴露出来的字段 |
面板是代码的镜子,不是另一份数据。代码里没标 @editable,面板上就没有那一栏 |
| World Outliner(世界大纲) | 关卡里的 entity 树(Scene Graph) | Scene Graph 里的父子层级不只是「摆放关系」,它就是数据结构本身:组件靠它找到彼此 |
| Spawn Actor from Class | 今天:设备预先摆进关卡,用 @editable 连引用;方向:生成 entity |
UEFN 今天不能凭空生成任意设备,惯用法是「先放好、再启用」。这是当前工具的限制,不是语言的限制 |
| Get All Actors of Class | 今天没有等价写法 | 拿到别的设备只能靠 @editable 在面板上把引用连进来。代码全对、面板忘了连线,是新手第一大坑 |
这一组是全站最容易被过期文章带偏的地方。判断一篇教程新不新,看它讲不讲 entity 与 component 就知道了。Scene Graph 的完整世界观在第八章,这里只需要记住:Actor 那一行不是简单改名,是换了一种组织方式。
词典查到最后一定会遇到查不到的词。查不到有两种:一种是「Verse 还没做」,另一种是「这压根不是 Verse 要管的事」。下面这五个属于第二种 —— 它们是另外的系统,不在 Verse 要替代的范围内,以后也不会有一条「Verse 里怎么写 Timeline」的答案。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
| Construction Script(构造脚本) | 没有对应物 | 它跑在编辑期:你在关卡里拖一下,它就重算一遍。Verse 今天不参与编辑期构建,它的代码只在游戏运行时跑 |
| Timeline(时间轴) | 没有对应物 | Timeline 是一个「曲线资产 + 播放器」的组合。Verse 有 Sleep 和循环,能手写插值,但没有曲线编辑器 —— 曲线属于动画/序列系统 |
| Animation Blueprint(动画蓝图) | 没有对应物 | 状态机、混合空间、骨骼控制是一整套独立的动画系统。Verse 可以「告诉它切换状态」,但不会去替代它 |
| Material Graph(材质图) | 没有对应物 | 材质节点最终编译成着色器,跑在 GPU 上,和游戏逻辑不是同一种东西 |
| Niagara(粒子特效) | 没有对应物 | 同上:特效系统有自己的编辑器和自己的运行模型。Verse 的角色是「在合适的时刻把它点着」 |
看清这条边界,能省掉大量焦虑。「蓝图作者的技能会不会作废」这个问题,在这张表面前有一半答案是现成的:你在材质、动画、特效、序列上积累的一切,和 Verse 完全无关,也就完全不受影响。Verse 要替代的只是「游戏逻辑」那一块 —— 而那正是这本词典前六张表覆盖的范围。
光看表记不住。下面这台设备就是开头那扇门,十九行,把上面查到的词几乎全用上了。点「运行下一步」,每一步都会告诉你「这一行在蓝图里叫什么」。
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
door_device := class(creative_device):
@editable
OpenButton:button_device = button_device{}
var IsOpen:logic = false
OnBegin<override>()<suspends>:void =
OpenButton.InteractedWithEvent.Subscribe(OnPressed)
OnPressed(Agent:agent):void =
if (IsOpen?):
Print("门已经是开的")
else:
set IsOpen = true
Print("门开了")
点「运行下一步」,看代码怎么一行行执行。
回头数一数:class、@editable、对象引用、var、logic、OnBegin、Subscribe、回调方法、if、?、set、Print —— 十二个词,全部能在前面的表里查到。这就是这本词典的用法:遇到不认识的,回去查那一行的第三列。
查表最危险的不是查不到,是查到了一个长得一样的词,但它不是那个意思。三个必须提前知道的陷阱:
陷阱一:Verse 的布尔不叫 bool,而且不能直接当条件。它叫 logic,只有 true 和 false 两个值 —— 到这里都和 Boolean 一样。但 if 不收它:Verse 的 if 问的是「这一步走不走得通」,不是「这个值是不是真」。所以要在后面加一个问号:if (IsOpen?)。少写这个问号是新手第一天最常见的报错,而报错信息不会告诉你「你少了个问号」。
陷阱二:Verse 的 = 是比较,不是赋值。蓝图作者不容易踩这个坑(蓝图里没有等号可写),但只要你看过任何一门其它语言的代码,手就会自己敲出 Score = 100。在 Verse 里这行的意思是「问一句:Score 等于 100 吗?」—— 而且 Verse 根本没有 ==。三个符号必须分清::= 是第一次声明,set … = 是改值(对应 Set 节点),单独的 = 是提问。以后编译报错里蹦出「failure context」,先回头看是不是漏了 set。
陷阱三:Scene Graph 的 component 与蓝图的 Actor Component 名字一样,模型不一样。两边都叫「组件」,都是「挂在某个东西上的一块功能」。但蓝图的世界里,主角是 Actor:它自己是一个可以被派生的类,自己也能写一堆逻辑,组件只是它的配件。Scene Graph 的世界里,entity 几乎是个空壳,所有数据和行为都住在组件里,「这个东西是什么」由「它装了哪些组件」决定,而不是由「它继承自哪个类」决定。同一个词,一边是配件,一边是全部 —— 这个差别大到值得单开一页,见文末拓展。
还有一层对照容易被忽略:名字本身的写法。UE 有一套前缀体系(BP_、S_、E_),Verse 没有前缀,但它对大小写有明确分工。
| 蓝图里的叫法 | Verse 里的叫法 | 差异 |
|---|---|---|
BP_MyDoor(蓝图类资产) |
my_door |
类型名一律 snake_case,全小写加下划线,不带任何前缀 |
S_PlayerStats(结构体资产) |
player_stats |
同上。类型是类、struct 还是 enum,由声明本身说明,不靠前缀区分 |
E_DoorState(枚举资产) |
door_state |
同上 |
IsOpen(变量,PascalCase) |
IsOpen |
这一条不用改:成员与局部变量在 Verse 里也是 PascalCase |
OpenDoor(函数,PascalCase) |
OpenDoor() |
同样不用改。函数名也是 PascalCase |
全大写常量(如 MAX_HEALTH) |
MaxHealth |
Verse 没有全大写常量的惯例 —— 常量和别的成员长得一模一样,因为不可变本来就是默认 |
/Game/Doors(资产路径) |
/MyProject/Doors(模块路径) |
项目里每个文件夹自动成为一个同名模块,路径就是模块路径 |
一句话记法:类型小写带下划线,其余首字母大写。看到 my_door 就知道那是一个类型,看到 MyDoor 就知道那是一个值或函数 —— 这条规则让 Verse 代码不用看上下文就能读懂一半。完整的改名对照与练习在拓展页 x3。
整整一课都是对照表,这一节讲的是「怎么查」。一个词查下去,只会落到三种结果之一:直译(换个名字照抄)、近似(概念在,写法和边界都不同)、无对应(它属于另一个系统)。判断落在哪一类,比记住译名更重要。
| 蓝图里的做法 | Verse 里的写法 | 差异 |
|---|---|---|
| Set 节点改一个 Integer 变量 | set Score = 100 |
直译:只多一个关键字,行为完全一致 |
| Branch 判断一个 Boolean | if (IsOpen?): |
近似:条件从「真 / 假」变成「走得通 / 走不通」,要补一个 ? |
| Is Valid 挡住一个空引用 | if (D := MaybeDoor?): |
近似:检查从运行时挪到了类型上,写法完全不同,但目的一致 |
| 用 Set 容器给一串东西去重 | map(t, logic) 手工顶替 |
近似:Verse 没有集合类型,「去重」这个保证得由你自己维持 |
| Construction Script 在编辑器里摆放物件 | 没有 | 无对应:它跑在编辑期,不属于 Verse 管的范围 —— 别浪费时间找译名 |
为什么会有「近似」这一类?因为 Verse 不是把蓝图节点一个个翻译成文字,它是另一套设计:失败被做成了控制流、不可变被做成了默认、并发被写进了语言。凡是碰到这三件事的词,对照关系就一定不是一对一的。
查表的正确姿势因此是三步:一,先看第一列找到你要的东西;二,看第二列拿到写法;三,一定要读第三列。跳过第三列,你会写出「能编译但行为不对」的代码 —— 那比编译不过难查得多。
词典翻完了,来三道查表题。答对拿星星 ★,答错可以一直重试,零惩罚。
蓝图变量面板里的 Boolean,在 Verse 的类型表里叫什么?
你在蓝图里用了一个 Set 容器(集合)来去重。翻到 Verse,应该怎么办?
Scene Graph 里的 component,和蓝图里的 Actor Component 是同一个东西吗?
拓展 · EXTRA
三个查不到译名的词,逐个说清它们在做什么、为什么 Verse 里没有直接对应,以及 UE6 里大概会怎么处理。
进入拓展 →拔高 · EXTRA
继承树模型与组合模型的根本差别,以及为什么同一个「组件」在两边指的不是一回事。第八章的伏笔。
进入拓展 →技巧 · EXTRA
Verse 的命名惯例与 UE 的前缀体系怎么对应,附一张改名对照表和一个动手练习。
进入拓展 →