技巧 · EXTRA
把节点图「读」成代码的四步法
一套可复用的翻译流程:先找执行线起点,顺着白线写成从上到下的行,把数据线塞进括号,把分支画成缩进。配一个从头做到尾的练习。
进入拓展 →这一课不教写,只教读。我们拿一个你闭眼都能搭出来的机制——踩到触发盒、播一声音效、3 秒后开门——先把那张节点图在脑子里摆好,再看同一件事写成 15 行 Verse 长什么样。学完你会拿到 5 条读代码规则,和一个结论:这玩意儿你看得懂。
在看任何代码之前,先做一件你已经做过一百遍的事:在脑子里搭一张节点图。机制很土,但每个人都做过——玩家踩上触发盒,响一声机关音效,过 3 秒,门开了。
你在事件图表(Event Graph)里会怎么连?大概是这四个节点串成一条白线:
| 顺序 | 节点 | 它在做什么 |
|---|---|---|
| 1 | Event ActorBeginOverlap |
触发盒(Box Collision)上的重叠事件。有东西碰到它,这个红色事件节点就发车,白线从这里往右流。 |
| 2 | Play Sound at Location |
播一声「咔哒」。Sound 引脚上接着那个音效资产,Location 引脚接着门的位置。 |
| 3 | Delay(Duration = 3.0) |
那个带时钟图标的 latent 节点。执行线在这里停 3 秒,然后从 Completed 引脚继续往右。 |
| 4 | Open Door(自定义函数调用) |
调用门蓝图上那个开门函数,一根线连过去,门就开了。 |
这张图有几样东西你已经熟到不用想:一根白色执行线把四个节点串成先后顺序;几根彩色数据线把资产和数值喂进节点的引脚;左边的变量面板里躺着 Trigger Box、Sound、Door 这几个引用;可能还有一个黄色的 Comment 框圈住整段,上面写着「踩板开门」。
记住这五样东西:执行线、数据线、节点、变量面板、注释框。接下来那 15 行代码里的每一个符号,都能在这五样里找到对应物——真的,一个不落。
下面是同一个机制在 Verse 里的样子。别急着理解每个字符,先整体扫一眼形状:上面几行是「有哪些引用」,下面几行是「发生了什么」,而且是从上往下一条道走到黑。
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
gate_device := class(creative_device):
@editable Pad:trigger_device = trigger_device{}
@editable Chime:audio_player_device = audio_player_device{}
@editable Gate:door_device = door_device{}
OnBegin<override>()<suspends>:void =
# 有人踩上触发盒之前,就停在下一行等着
Pad.TriggeredEvent.Await()
Chime.Play()
Sleep(3.0)
Gate.Open()
现在逐行对照。左边是代码,右边是「这一行对应你那张图里的哪个东西」:
| 行 | 代码 | 在你那张图里,这是什么 |
|---|---|---|
| 1–2 | using { … } |
把要用的资产库搬到手边。图上没有对应节点——它更像「项目里启用了哪些插件」。/Fortnite.com/Devices 这个抽屉里装的,就是你能拖进关卡的那些设备。 |
| 4 | gate_device := class(creative_device): |
新建一个蓝图类,父类选 creative_device——就是右键 → Child Blueprint Class 那一步。这个类就是「装着这张图的那个蓝图」。 |
| 6–8 | @editable Pad:trigger_device = … |
变量面板里的三个变量:类型分别是触发盒、音频播放器、门的引用。@editable 就是变量右边那个眼睛图标(Instance Editable)——勾上之后,你能在关卡里的 Details 面板上把实际的触发盒拖进来。 |
| 10 | OnBegin<override>()<suspends>:void = |
事件图表的入口,角色等同 Event BeginPlay。这一行以下、往里缩进的所有行,都是从这个事件节点拉出去的那条白线。 |
| 11 | # 有人踩上触发盒之前…… |
那个黄色的 Comment 框。写给人看的,机器直接跳过。 |
| 12 | Pad.TriggeredEvent.Await() |
Event ActorBeginOverlap。执行线走到这里就停住,直到有人踩上 Pad 这个触发盒才继续。 |
| 13 | Chime.Play() |
Play Sound at Location。区别只在于:蓝图直接把 Sound 资产喂给节点,Verse 这里是叫一台已经摆在关卡里的音频播放器设备干活。 |
| 14 | Sleep(3.0) |
Delay,Duration = 3.0。括号里的 3.0 就是那个引脚上填的数。 |
| 15 | Gate.Open() |
调用门上的开门函数。等于那根从 Delay 的 Completed 引脚连到 Open Door 节点的线。 |
看出来了吗?节点没少,线没了。四个节点原样还在,只是从「摆在画布上、用线串起来」变成了「摞成一列、从上往下读」。执行线消失的原因很朴素:文字天生就有先后顺序,不需要再画一根线来说明「先做这个再做那个」。这件事值得单独说一页,见文末的拔高拓展。
把上面那张对照表压缩一下,就是五条规则。学会这五条,你能读的就不止这一段代码了——本站后面所有的 Verse 片段,骨架都是它们。
规则 ① 缩进 = 连线。往里缩一格(4 个空格),意思是「我接在上一个东西后面」或者「我在它内部」。第 12–15 行都缩在 OnBegin 里面,所以它们是从 BeginPlay 拉出去的那条白线上的四个节点;而第 6–8 行只缩了一格,和 OnBegin 平级,所以它们不是节点,是这个蓝图类的变量。看缩进就等于看连线:同一列的是同一条线上的兄弟,更靠右的是被包在里面的。
规则 ② := = 新建并赋值。读作「定义为」。第 4 行 gate_device := class(…) 是「新建一个叫 gate_device 的东西,内容是一个类」;写数据也一样,MaxPlayers := 12 就是在变量面板里新建一个整数变量、默认值填 12。注意它只用于新建——给已有变量改值在 Verse 里另有写法(set,就是那个 Set 节点),第 8 课细讲。也别把它和单独的 = 搞混:那个是「比较相不相等」。
规则 ③ 名字(参数) = 调用节点。只要看到名字后面紧跟一对圆括号,那就是一个节点在干活。Sleep(3.0) 是 Delay 节点,3.0 是填在引脚上的值;Chime.Play() 前面那个点读作「的」——「Chime 的 Play」,相当于把 Chime 这个引用拖出来、从它身上拉出一个 Play 节点。括号里空着,就是这个节点没有输入引脚要填。
规则 ④ <> 尖括号里的东西 = 节点的勾选项。第 10 行的 <override> 和 <suspends> 叫「说明符」(specifier),它们不是参数,是贴在这个函数上的标签,作用很像蓝图函数 Details 面板里那一排勾选框。<override> ≈「我在覆盖父类的同名函数」(蓝图里的 Override 下拉);<suspends> ≈「这个函数里可以有 latent 节点」——正因为标了它,第 14 行才允许出现 Sleep,就像蓝图里 Delay 只能放在事件图表、不能放在纯函数里。顺带一提,第 6–8 行开头的 @editable 是同类东西的另一种写法(叫「属性」),对应变量的 Instance Editable 勾选框。读代码时的策略:先跳过尖括号,把主干读通,再回头看标签。
规则 ⑤ # 开头 = 注释气泡。从 # 到行尾全是写给人看的,编译器视而不见。它就是蓝图里那个黄色 Comment 框,或者节点上挂的那条小便签。好消息是:代码里的注释不会像蓝图注释框那样被拖歪、被压在节点底下。
五条读完,回头再扫一遍第二节那段代码。这次你应该能一眼把它切成三块:搬工具箱(1–2)、变量面板(4–8)、事件图表(10–15)。
规则是死的,执行顺序是活的。下面的步进器把这 15 行按运行顺序点亮一遍,每一步的说明都只回答一个问题:这一步在蓝图里是哪个节点。点「运行下一步」,像看回放一样跟一遍。
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
gate_device := class(creative_device):
@editable Pad:trigger_device = trigger_device{}
@editable Chime:audio_player_device = audio_player_device{}
@editable Gate:door_device = door_device{}
OnBegin<override>()<suspends>:void =
# 有人踩上触发盒之前,就停在下一行等着
Pad.TriggeredEvent.Await()
Chime.Play()
Sleep(3.0)
Gate.Open()
点「运行下一步」,看代码怎么一行行执行。
注意第 12 步:执行线在那里停住等人,一停可能就是十分钟。蓝图里 Delay 和事件等待也是这样——只不过在图上,「等待」是一个长得像时钟的节点;在代码里,「等待」就是普普通通的一行,后面那些行乖乖排队。这就是缩进能顶替连线的底气:顺序是白送的。
换一张图试试:踩上触发盒 → 等 1.5 秒 → 把灯点亮。下面这段代码被挖掉了两个位置,一个考规则 ②,一个考规则 ③。填回去:
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# 新建一个蓝图类,父类是 creative_device——「定义为」的符号是?
lamp_device ____ class(creative_device):
@editable Pad:trigger_device = trigger_device{}
@editable Lamp:light_device = light_device{}
OnBegin<override>()<suspends>:void =
Pad.TriggeredEvent.Await()
# 这一行是那个 Delay 节点——它叫什么名字?
____(1.5)
Lamp.TurnOn()
填对了的话,请留意一件事:你刚才是照着那张节点图的形状把代码填出来的,不是背语法背出来的。这就是本课想给你的那个结论。
把这一课压成一张速查表:左边是节点图上的元素,右边是它在代码里的形态。差异那一列才是重点。
| 蓝图里的做法 | Verse 里的写法 | 差异 |
|---|---|---|
| 白色执行线 | 从上到下的行序 + 缩进 | 没有线可以连歪、连错、连成一团;代价是你不能再靠拖动节点来「重排」逻辑,只能剪切粘贴行 |
| 彩色数据线接进引脚 | 圆括号里的参数,如 Sleep(3.0) |
数据流被写进了调用里;好处是「值从哪来」一眼可见,坏处是没法像蓝图那样把一根数据线拉给好几个节点共用 |
| 右键新建 Child Blueprint Class | gate_device := class(creative_device): |
一行文本代替一次资产创建;类名用小写加下划线,是 Verse 的类型命名习惯 |
| 变量面板新建变量 + 勾 Instance Editable | @editable Pad:trigger_device = trigger_device{} |
勾选框变成写在名字前面的 @editable;不勾就是纯内部变量,Details 面板上看不见 |
Event BeginPlay |
OnBegin<override>()<suspends>:void = |
角色相同,但 Verse 要求你显式写 <override>,说明你知道自己在覆盖父类的函数 |
Event ActorBeginOverlap |
Pad.TriggeredEvent.Await() |
蓝图的事件节点是「被调用」,这一行是「停下来等」。两种口味 Verse 都有,另一种是订阅一个回调函数,第 25 课细讲 |
Delay(Duration 引脚) |
Sleep(3.0) |
Delay 只能放在事件图表里;Sleep 也只能出现在标了 <suspends> 的函数里——同一条约束,换了个写法 |
| Call Function 节点 | Gate.Open() |
点号读作「的」,等于把引用拖出来再拉一个节点。Verse 只开放 Epic 显式暴露的函数,不要照搬蓝图节点名去猜 |
| 黄色 Comment 框 / 节点便签 | # 开头的行 |
不会被拖歪,也不会被压在节点底下;但也画不出「把这一坨框起来」的视觉分组 |
| Details 面板上的勾选框 | <> 里的说明符 / @ 开头的属性 |
蓝图的勾选项散在面板各处,Verse 全部贴在名字旁边——读代码时能顺手读到,不用切窗口 |
为什么会有这些差异?根子上只有一句话:节点图把「逻辑」和「布局」绑在了一起,文本代码把两者拆开了。在蓝图里,一个节点在画布上的位置既是美观问题也是可读性问题,而连线既表达顺序又表达数据流;线一多,画布就成了毛线球。文本代码里,顺序由行序表达、层次由缩进表达、数据流由括号表达,三件事各归各管,画布这个维度整个消失了。
这不是说文本一定更好。蓝图的图形化有一个代码给不了的优势:你能一眼看见整个流程的形状,分支多的时候尤其明显。Epic 显然也知道这点——官方调研问卷里提到过一个面向 Verse 的可视化脚本层(社区称之为 "Visual Verse"),但形态尚未公布。在那之前,读代码这项技能是必修的。也顺便说清楚:Actor 与 Blueprint 在 UE6 Early Access(目标 2027 年底)及早期版本中完整支持,弃用要等 Scene Graph 足够成熟、时间未定,Epic 已承诺在弃用前提供转换工具、但尚未发布。你现在学的不是「替代品」,是「另一种写法」。
三道小关卡,全是「读」的题,没有一道要你写代码。答对拿星星 ★,答错可以无限重试。
在 Verse 里,「这一行接在上一行后面执行」是靠什么表达的?
代码里的 Sleep(3.0),对应你那张节点图上的哪个节点?
OnBegin<override>()<suspends>:void = 这一行里,尖括号里的东西是什么?
技巧 · EXTRA
一套可复用的翻译流程:先找执行线起点,顺着白线写成从上到下的行,把数据线塞进括号,把分支画成缩进。配一个从头做到尾的练习。
进入拓展 →拓展 · EXTRA
同一段极简逻辑用三种语言并排写一遍。Verse 哪里像 Python、哪里像 C#、哪四样东西只属于它自己——专治「代码恐惧」。
进入拓展 →拔高 · EXTRA
白线其实只在说一句话:「先做这个,再做那个」。写成文本之后顺序是白送的,连线反而冗余——顺便讲讲 goto 是怎么被淘汰的。
进入拓展 →