Verse Wiki — 写给蓝图作者的 Verse 手册
拓展 · EXTRA

纯节点(Pure)与 Verse 的效果说明符

主课说「节点 → 函数调用」,但漏了一件事:蓝图的节点分两种长相,有白色执行引脚的和没有的。这个区别在 Verse 里没有消失,它被搬到了一个更严格的地方——函数签名。

一、先把 Pure 节点看清楚

蓝图里有些节点接不上白线:Add、Get、Make Vector、Greater Than、Get Player Count。它们只有数据引脚,没有执行引脚,这类节点叫 Pure(纯)节点,图标是绿色的圆角小方块。它们有三个特征:

关键在第三条的那两个字:约定。在蓝图里勾选一个函数的 Pure 选项,引擎不会去验证你在函数体里到底有没有偷偷 Set 一个变量。你勾了 Pure,它就长成 Pure 的样子,仅此而已。C++ 侧的 BlueprintPure 标记同理:它是节点作者写下的一句承诺,不是一份被检查的合同。所以蓝图里「这个节点纯不纯」这个信息,准确度取决于写它的人有多自觉。

二、Verse 把它写进了函数签名

Verse 里对应的机制叫 效果说明符(effect specifier),写在函数名之后、返回类型之前的尖括号里。你其实一直在看它——每节课都出现的 OnBegin<override>()<suspends>:void,末尾那个 <suspends> 就是。

score_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

score_device := class(creative_device):

    var Score:int = 100

    # 纯计算:只吃参数、只吐结果,不碰任何会变的状态
    Doubled(X:int)<computes>:int = X * 2

    # 可能失败:买得起才走通,买不起就算「失败」
    Afford(Price:int)<decides><transacts>:void =
        Score >= Price

    # 不写说明符 = <transacts>:能读能写,和挂在白线上的普通节点一样
    AddScore(N:int):void =
        set Score += N

    OnBegin<override>()<suspends>:void =
        Print("{Doubled(21)}")
        if (Afford(30)):
            AddScore(-30)
            Print("买了,剩 {Score}")

三个函数,三种身份。Doubled 是彻头彻尾的 Pure 节点;AddScore 是挂在白线上的普通节点(它 set 了变量);Afford 则是蓝图里没有的第三种东西——一个「可能走不通」的函数,调用它必须放在 if 这类允许失败的位置。

三、蓝图作者最先撞上的五个

说明符不止五个,但下面这五个覆盖了你迁移时 95% 的场景。这里只写「大致管什么」,精确语义以官方文档为准:

说明符 大致管什么 蓝图里的类比
<computes> 纯计算:同样的输入永远给同样的输出,不读也不写会变的状态 最纯粹的那种 Pure 节点(Add、Max 这类数学节点)
<converges> 「最小」的效果集:保证在有限时间内算出一个值,不会陷进去出不来 没有对应物——蓝图不关心你的函数会不会死循环
<transacts> 默认档:可以读、写、分配;这些动作被放进一个事务,配合失败时可以整体回滚 挂在白线上的普通(Impure)节点
<decides> 这个函数可能失败;调用它就是一个「可能走不通」的表达式,必须写在允许失败的地方 多加一个 bool 输出引脚,再手动接一个 Branch 去判断
<suspends> 这个函数可以中途暂停、把控制权让给别的并发任务,跨多次模拟更新才跑完 Latent 节点(Delay、Timeline 那种右下角带时钟图标的)

还有一条组合规则值得记:说明符分成独占叠加两类。<converges> / <computes> / <transacts> 是独占的,一个函数最多挑一个;<decides> / <suspends> / <reads> / <writes> / <allocates> 是叠加的,可以和独占的写在一起,比如上面的 <decides><transacts>。有一条硬限制:<decides><suspends> 不能同时用——「可以回滚」和「跨帧等待」这两件事凑不到一起。

什么都不写会怎样?按官方文档,不写说明符的函数默认就是 <transacts>——相当于蓝图里默认给你一个 Impure 节点:能读能写,老老实实排在白线上。你需要主动申请的,是比默认更纯(<computes>)或者更特别(<decides><suspends>)的那些。

顺手澄清一个容易误会的地方:<computes> 不代表结果会被缓存。主课第五节讲过,Verse 是写几遍算几遍;说明符描述的是「这个函数能做什么」,不是「引擎会怎么优化它」。同一个值要用两次,该起名字还是得起名字。

四、最大的差异:这是合同,不是注释

说明符的价值不在于「多写几个词」,而在于编译器会替你检查。三个具体后果:

换个说法:蓝图把「这个节点会干什么」画在节点长相上,靠人自觉、靠人眼看;Verse 把它写进签名,靠编译器强制。你打开一个陌生的 .verse 文件,不用读函数体,光看一行签名就知道这个函数会不会改状态、会不会失败、会不会卡住等几帧——这份信息在蓝图里要么得点开函数图翻一遍,要么根本无从得知。文档变成了合同。

代价当然也有:多了一套要记的词,而且刚开始写的时候,你会被一堆「这里不允许有这个效果」的报错拦住。好消息是这些报错几乎都指向真实的设计问题——它在问你「你到底想让这个函数干什么」,而你在蓝图里从来不用回答这个问题。

五、小测验

蓝图里的 Pure 节点(没有执行引脚、每拉一根线就重算一次、只算不改),在 Verse 里最接近哪个说明符?

来源

本文整理自 Epic 官方文档与官方语言手册:Verse Glossary(官方文档)↗ · Specifiers and Attributes in Verse(官方文档)↗ · Book of Verse — Effects(官方语言手册)↗。说明符的完整清单与精确语义以官方文档为准。