用 Asyncflow 写玩法:一种更适合内容策划的工作方式

海量内容,需要策划自己写逻辑

2022 年,我在《逆水寒》项目实习时,第一次长期使用 Asyncflow。它至今仍是我用过最好用的策划可视化工具之一:直观、容易上手,也保留了对逻辑细节的控制。

对于《逆水寒》这样的运营期 MMO,每个小版本都会加入大量玩法、活动和小游戏。单个内容看起来未必复杂,但都要处理触发条件、状态切换、NPC 行为、胜负判定和各种异常。随着版本持续迭代,项目需要生产和维护的是海量逻辑。

面对这种规模,策划不能只写需求,再等开发逐一实现。在内容型游戏的生产管线里,策划既是设计者,也是逻辑的直接生产者。只有自己动手实现,才能快速验证设计、反复调整细节,并在版本节奏内完成足够多的内容。

这并不意味着每个策划都必须成为程序员。策划可以直接写 Lua、C#,也可以通过蓝图、Bolt 等工具做可视化编排。前者控制力强,但学习和用人成本高;后者降低了上手门槛,却很依赖节点覆盖、调试能力和编辑器体验。真正的问题不是要不要让策划写逻辑,而是用什么工具支撑策划稳定地写出海量逻辑。

玩法导向型游戏与内容导向型游戏的生产管线对比:前者由策划写需求、开发制作、策划验收,后者由策划自己实现、开发按需支持
两种管线面对的是不同问题:玩法导向型由开发承担实现、策划负责验收;内容导向型由策划直接生产内容,开发按需补充能力。

Asyncflow 和蓝图、Bolt 一样,本质上都是供策划编写逻辑的工作台。在《逆水寒》里,它把 Lua 的条件、分支、事件和函数调用放进清晰的流程图中。策划可以直接编排玩法,缺少底层能力时再请开发补充接口。我进入项目一两个月后,就能用它制作简单的 MMO 副本,后来还完成了类似跑跑卡丁车和“兔羊”的副玩法,整个过程几乎不需要开发参与。

笔者也是蓝图等可视化逻辑编程工具的重度用户;相比之下,Asyncflow 的交互设计更直观,异步通信更适合游戏内容生产,经过雷火多年的项目实践后,功能覆盖也已经相当完整,同时兼容性好、迁移成本低,后文会逐一说明。

到了 AI 时代,这套标准又多了一条。工具不仅要方便人理解和编辑,还要让模型能够读取、修改和生成同一份逻辑。人负责设计、审阅和调试,AI 负责补全与修改;双方能否在同一份数据上协作,会变得越来越重要。这也是我今天重新推荐 Asyncflow 的原因。

Asyncflow 到底是什么

在《逆水寒》的使用场景里,Asyncflow 可以理解为一种 Lua 的可视化编排方式。它没有另外发明一套编程语言,而是把纯文本代码里的执行顺序和 if/else 判断抽出来,放到一张可以直接阅读和编辑的流程图上。

在图里,一个普通节点负责执行一段表达式或调用一个方法,然后根据返回结果决定接下来往哪里走。绿色连线对应 True,红色连线对应 False,蓝色连线则不判断结果,直接继续执行。原本需要沿着缩进逐层阅读的条件分支,被展开成了几条颜色明确的路径。

同一段角色距离判断与攻击逻辑在 Asyncflow 流程图和 Lua 代码中的对照,中间以双向箭头表示两种形式可以相互转换
同一段逻辑的两种表示:流程图把判断与回环展开,Lua 则用 whileif 表达相同的控制流。

这种做法没有降低玩法本身的逻辑复杂度,只是把复杂度变得更容易看懂。策划仍然要考虑条件、顺序、状态和异常情况,但不必先熟练掌握 Lua 的语法细节,也不用在多层嵌套的代码里反复寻找某个分支。完成编排后,Asyncflow 仍然可以生成纯文本脚本,最终运行的并不是一份只能由编辑器识别的二进制图资产。

Asyncflow 已经是一套非常成熟的生产工具,设计、开发、维护和测试中的大部分场景都有对应的功能支持。它覆盖了从逻辑编写、代码生成到热更和调试的完整流程。

迁移成本很低,是它的另一个优势

Asyncflow 的另一个优势,正是迁移成本很低。它可以跟随项目原有的技术架构变化,而不是要求项目反过来迁就工具。它的运行框架用 C++ 编写,再向不同语言提供接口;编辑器负责把流程图转换成目标语言的代码。项目接入对应的运行时后,仍然可以继续使用原来的引擎、脚本语言和游戏对象。

从这个角度看,Asyncflow 更像一套独立的第三方插件:它叠加在已有的逻辑体系之上,不负责替换引擎,也不要求团队重建整套玩法框架。《逆水寒》端游使用自研引擎,手游使用 Unity,但端游中编写的流程图仍然可以迁移到手游。流程图保留条件、事件和执行顺序,语言与引擎的差异则由代码生成器和运行框架处理。

Asyncflow、Blueprints、Behaviac 和 Behavior Designer 在支持引擎、编程语言与异步节点方面的对比
论文 Table 1 的中文版。Asyncflow 同时支持跨平台运行、多种脚本语言和异步节点。

Asyncflow 的定位:它同时支持跨平台运行、多种语言和异步节点。这样即使项目更换引擎或调整脚本架构,原有的内容生产方式也有机会延续。

Asyncflow 对游戏对象采用非侵入式设计:接入运行时后,游戏对象对应一个 Agent,流程图数据解析为 ChartData,再由运行时创建 Chart 并挂载到 Agent 上。流程图通过 Agent 调用对象已有方法、接收其事件,无需改写对象代码或改造所有对象。

这种分层也降低了接入成本。项目开发中途同样可以先接入运行时,再逐步开放现有方法;缺少能力时,只需补充接口或封装方法,无需推翻原有业务代码。

用流程图表达玩法逻辑

Asyncflow 能处理的不只是几段简单的条件判断。同一套流程图既可以描述单个角色的行为,也可以管理一整套玩法的生命周期,还可以把任务演出的时序完整串起来。下面三个案例,正好对应三种不同尺度的内容生产。

复杂 Boss 的行为树

一个复杂 Boss 很少只是按照顺序循环释放几个技能。它需要判断当前阶段、血量、目标距离和技能冷却,还要处理受击、打断、召唤物死亡、仇恨丢失等临时事件。进入下一阶段时,技能组合、移动方式甚至场地规则都可能一起变化。

这些逻辑直接写成脚本,很快就会形成多层条件和回调。放进流程图后,可以先把不同阶段拆成几条主路径,再用条件线决定 Boss 此刻应该近战、追击、释放远程技能还是进入特殊机制。技能结束、动画完成或 Boss 被打断时,再通过事件回到对应的决策位置。

复杂 Boss 行为树示例,主决策根据目标状态和血量进入三个战斗阶段,并并行监听技能打断、召唤物死亡和目标丢失事件
复杂 Boss 行为树示例。流程图数据按 Asyncflow 的项目格式编写;图中的前端界面是为本文临时制作的渲染版本,并非公开编辑器的正式界面。点击图片可查看原图。

图上的调整也更直接。比如把某个血量判断改成二阶段入口,把冲锋的距离阈值调近,或者让技能被打断后接一次普攻,顺着执行路径找到节点改掉就可以。开发提供移动、选目标、放技能这些基础方法,Boss 什么时候切阶段、先做什么再做什么,则由策划自己在图里安排。

一整套玩法机制

流程图也可以继续向上扩展,用来管理完整的 Gameplay。我当时制作过类似跑跑卡丁车的竞速副玩法,它要处理的就不只是车辆移动,而是从玩家入场、出生点分配和倒计时开始,持续管理检查点、圈数、名次、完成条件、超时结算和最终退出。

用 Asyncflow 实现的简化版植物大战僵尸玩法机制流程图,包含波次、种植、僵尸、路线、子弹和失败结算流程
用 Asyncflow 实现的简化版《植物大战僵尸》玩法机制:同一张图里同时管理波次、种植、僵尸行为、子弹命中和失败结算。

在这个尺度上,主流程负责推进玩法阶段,事件节点接收玩家通过检查点、到达终点或中途退出等消息,计时器节点控制开场倒计时和整局时限,不同玩家的状态则可以并行更新。任何一条异常路径——例如玩家没有按时完成、比赛中途掉线,或者结算时人数发生变化——也能作为明确的分支留在图里。

这时,策划不是在配置一份由开发预先决定好的玩法模板,而是在真正编写玩法规则。胜负怎样判断、什么时候给反馈、某种意外情况如何收尾,都可以自己实现和反复调试。只有底层缺少某项能力时,才需要开发补充一个接口。

一段任务演出

任务演出关注的又是另一种复杂度。NPC 走到指定位置之后说话,镜头切换完成后播放动作,玩家交互后再进入下一段;如果玩家提前离开、跳过对白或者触发了另一条分支,当前演出还要能够正确结束。

Asyncflow 任务流程图与《逆水寒》任务演出实机画面对照
流程图与任务演出实机画面对照。《逆水寒》端游和手游中的任务内容,均由这套工具实现。

这类内容看起来是线性的,实际由大量“等待某件事完成后再继续”组成。流程图可以把对白、移动、镜头、动画和玩家交互排成清楚的时间顺序,同时为不同任务条件保留分支。策划可以直接看到一段演出为什么停住、当前在等待哪个事件,也可以亲手调整一句对白与一个动作之间的间隔。

这三个案例使用的仍然是同一套基本表达:普通节点执行方法,条件线选择路径,事件节点等待外部反馈,计时器节点控制时间,控制节点负责流程的开始、暂停和结束。变化的只是逻辑规模。也正因为事件和等待可以直接成为流程的一部分,Asyncflow 才特别适合游戏里这些充满异步行为的内容。

异步通信,是它很有特点的一项设计

异步通信在任务演出里非常重要。一段演出通常不会只由一个 NPC 从头演到尾:NPC A 走到门口之后,NPC B 才开始转身;B 的动作播放完成,镜头才能切换;玩家进入指定区域后,另一段对白才会开始。每一步都依赖其他对象发来的信号,而且动作究竟在哪一帧完成,事先并不能写死。

Asyncflow 用事件来处理这种关系。项目版里有 Send Event ToReceive Event 这样的节点:一边向指定对象发送事件,另一边等待并接收事件。比如 NPC A 到达目标点后发出“已就位”,NPC B 收到信号后继续播放动画;动画结束后,B 再发出事件,让镜头流程继续。

片场中导演协调 NPC A、NPC B 和镜头,镜头前站着玩家,三组橙色连线分别标注移动指令、场景变化指令和运镜指令
导演不必直接接管其他对象,只需要分别发出移动、场景变化和运镜指令,NPC、布景与镜头再按各自的流程响应。

每个agent只需要管好自己负责的对象。A 不需要直接控制 B 的动画,B 也不需要每帧检查 A 是否已经走到位置。它们只约定“什么时候发出什么事件”,然后各自继续运行。图与图之间没有绑死,却可以组合出一段时序非常精确的演出。而且这更符合大部分人的逻辑习惯。

当接收节点等待事件时,停下来的也只是当前这条流程,其他 NPC、其他图和游戏本身仍然可以继续运行。事件发生后,EventManager 再唤醒对应对象上正在等待的节点。这正是异步节点适合游戏内容的原因:移动、动画、对白、镜头和玩家交互都可以用“等待信号,再继续”的方式自然地串在一起。

AI 时代重新看 Asyncflow

AI 让图形化编辑的前提发生了变化。过去,我们把代码变成节点,是为了降低策划写脚本的门槛;现在,如果逻辑已经想清楚,AI 直接写 Lua 或 C# 往往更快。

所以,蓝图、Asyncflow 这类工具都会遇到同一个问题:图形数据除了玩法逻辑,还包含节点、连线以及编辑器信息。区别首先在于噪声有多少。蓝图还要处理输入输出引脚、类型、默认值、GUID 和布局;Asyncflow 的结构更薄,YAML 主要记录节点和控制流关系。

蓝图节点文本节选与 Asyncflow 生成 Lua 的并排对比
同一段逻辑在两种 AI 可读文本中的结构示意。Asyncflow 的 YAML 同样包含 UID 和连线;这里比较的是 AI 读取逻辑时最直接可用的文本入口,而不是完整编辑成本。点击图片可查看原图。

第二个区别在于读取路径。实际协作中,大部分工作不是让 AI 拖节点,而是让它理解逻辑、检查分支、定位问题和补充方法。Asyncflow 可以把流程图生成成一份干净的 Lua,AI 直接阅读这份文本就能完成这些工作,不必先解析图资产。

AI 时代到来后,我重新开始寻找更适合人与模型协作的工具和工作流。没想到兜兜转转,最后找到的,竟然还是几年前我用得最多、也最熟悉的 Asyncflow。

它当然不是为 AI 设计的,但当年那些关于图、代码和协作的取舍,却意外地适应了今天。技术向前走了一大圈,我也绕了一圈,最后又回到这个工具面前。这大概就是我重新写下这篇文章时,最唏嘘的地方。

P.S.:据我了解,《逆水寒》手游现在采用的方式是让模型直接编写 Lua,再把代码转成流程图。AI 操作代码,策划阅读和调整流程图,双方使用的是同一份逻辑数据。这样,人和 AI 可以在各自熟悉的界面里协作,实际用起来非常方便。