旧版编辑器 · 节点类型
这里的「旧版编辑器」指当前线上在用的整套宇宙编辑器体系—— 策划在 UE 里摆的行为树、挂在节点上的行为与条件、以及客户端对应的执行器。 新一代编辑器另开一页(新版编辑器),等资料齐了再同步。
本页是第一批内容:节点类型说明。先把「策划能摆出来的东西一共有几类、各自什么语义、 谁来执行」讲清楚,其余章节(保存格式、导表、调试)后续补。
1. 先分清三层:树 / 节点 / 挂载物
策划打开编辑器看到的东西,代码里其实是三层,三层各有一套枚举,谁也不是谁。 排查「策划说找不到这个」的时候,第一步就是判断他说的是哪一层。
ILevelPlay.Tree)、
任务树(IQuest)、野外事件树(IWildEvent)。
数据结构都是 IBehaviorTree = { Vars, Root },
Root 恒为 Start 节点。三种树共用同一套节点类型,但各自有独立的下拉过滤—— 同一个行为在主控树能摆、在任务树可能摆不出来。
EAction);
程序口中的「节点」通常指第 2 层(EBtNode)。接需求时先对齐这一点,能省掉一轮返工。
2. 13 种行为树节点(EBtNode)
定义在 UniverseEditor/Interface/IQuest.ts,
同文件里的 nodeTips 就是策划在编辑器里悬停看到的原文。
按「能挂几个子节点」分三组,这个分组直接决定了树能怎么搭:
// IQuest.ts:1374 —— 结构约束就写在类型里
export type TOneChildBtNode = IAlwaysFalseBtNode | IAlwaysTrueBtNode | IRepeaterBtNode | IStartBtNode;
export type TManyChildrenBtNode = IParallelSelectBtNode | ISelectBtNode | ISequenceBtNode;
export type TLeafBtNode = IActionBtNode | IChildQuestBtNode | IConditionBtNode
| IQuestFailedBtNode | IQuestSucceedBtNode;
2.1 单子节点(4 种)
| 类型 | 作用 | 编辑器里的原文提示 |
|---|---|---|
| Start | 树的根,恒定唯一,IBehaviorTree.Root 就是它 | (无提示) |
| Repeater | 循环执行下属节点 | 循环节点,可以重复执行下属节点,每次单个循环结束后判断是否退出循环(返回 true)。循环节点内部不支持中断,可以通过使用并行节点 ParallelSelect 新增条件中途打断循环节点 |
| AlwaysTrue | 吞掉子树结果,强制返回 True | 子节点执行完成后,强制返回值为 True(不熟悉行为树不推荐使用) |
| AlwaysFalse | 强制返回 False,用来把分支标成「可选」 | ParallelSelect 并行步骤中需要设置可选步骤时使用,AlwaysFalse 下属分支为可选 |
ParallelSelect 配了「完成 2 个」,下面挂 4 个分支,其中 1 个包在 AlwaysFalse 里——
那个分支就是可做可不做:玩家做了有表现,不做也不耽误并行节点判定通过。这是策划最常用的一个组合技,看到
AlwaysFalse 先按这个思路理解。
2.2 Repeater 的六个字段
循环节点是单子节点里字段最多、也最容易配错的一个:
| 字段 | 含义 | 注意 |
|---|---|---|
| MaxRepeatTimes | 最大循环次数 | 勾了无限循环后直接忽略 |
| TickIntervalMs | 循环 tick 间隔(毫秒) | 0 视为默认 1000,最小 500 |
| IsInfiniteLoop | 无限循环 | 勾上后忽略 MaxRepeatTimes |
| InfiniteLoopTimeoutMs | 无限循环超时(毫秒) | ≤0 表示不超时,仅无限循环生效 |
| IsTimeoutFail | 超时后节点算失败还是成功 | 默认成功 |
| ExitCondition | 退出循环条件 | 每单次循环流程结束后才判断,不是随时判断 |
ExitCondition 只在单次循环跑完之后求值——循环体内部卡住(等一个永远不来的事件),
条件再对也判断不到;② 想中途打断,必须在外面套 ParallelSelect 再挂打断条件, 在 Repeater 内部加节点是没用的(官方提示原文就是这么写的)。
2.3 多子节点(3 种)+ 分支选择
| 类型 | 作用 | 编辑器里的原文提示 |
|---|---|---|
| Sequence | 顺序执行 | 顺序执行下属节点,下面通常挂接多个 ChildQuest |
| Select | 依次尝试,一个成功就结束 | 依次执行子节点,子节点返回 True 则终止并返回 True,否则执行下个子节点(不熟悉行为树不推荐使用) |
| ParallelSelect | 并行 N 选 M | 并行步骤:并行执行多个步骤,要求完成其中若干个。节点中配置完成数目。可通过 AlwaysFalse 节点将下属分支设为可选 |
| ConditionSelector | 互斥分支 | 分支选择:根据之前的对话、战斗结果选择分支,分支之间互斥。节点中配置分支条件 |
ParallelSelect 的 Cnt 字段:0 表示要求全部完成,>0 表示完成对应个数即可。
ConditionSelector 结构上不是 Children[] 而是 Slots[],
每个 slot 是 { Node, Condition } 一对——分支和它的进入条件绑在一起:
export interface IConditionSelectorBtNodeSlot {
Node: TBtNode;
Condition: IConditionGroup;
}
2.4 四个逻辑节点共享的一批字段(ILogicBtNode)
Sequence / Select / ParallelSelect / ConditionSelector 都继承 ILogicBtNode,
所以它们身上挂着一批和流程无关、但很关键的字段。
找不到某个功能配在哪,先来这里翻一遍:
| 字段 | 作用 |
|---|---|
| SaveConfig | 存档 & 回退点配置——存档点不是独立节点,是挂在逻辑节点上的 |
| LockWalkMode | 节点激活期间只保留基础移动与视角,禁用技能/跳跃/冲刺/钩锁/交互 |
| PostAkMusicList | 节点激活时发的音频事件列表,节点退出时不做任何处理 |
| PostAkEvent | 已废弃被 PostAkMusicList 取代,仅运行时兼容读取存量单条配置 |
| HotFixActionConfigs | 热更修复行为列表(Condition 节点上也有这个字段) |
| RelatedChildQuests | 【关卡行为树专用】关联的任务子节点 |
| TidQuestAliasName / Desc | 【任务专用】在这个节点期间临时改任务名 / 描述 |
| LevelId / RewardConfig | 【任务专用】关卡 Id 与奖励配置 |
2.5 叶子节点(5 种)
| 类型 | 作用 | 关键字段 |
|---|---|---|
| Action | 执行一串行为 | Actions: IActionInfo[],就这一个字段 |
| ChildQuest | 一个任务步骤:等条件达成 | Condition + EnterActions / FinishActions + 追踪相关 |
| Condition | 当场判真假(叶节点,不是装饰器) | Condition、WaitUntilTrue(持续检测直到为真) |
| QuestSucceed | 成功并终止整棵树 | FinishActions、IsHalfFinish(是否中途结束) |
| QuestFailed | 失败并终止整棵树 | AutoFailed(直接失败)、NotReturnToSavePoint(失败后不回存档点)、FinishActions |
Condition 是瞬时判断:此刻为真就往下走,为假就返回 False。
勾了 WaitUntilTrue 才会持续检测。ChildQuest 是一个会显示在任务 UI 上的步骤:有追踪文本、有寻路箭头、有进入/退出行为。
「玩家要做点什么」用 ChildQuest,「程序判断一下状态」用 Condition。
2.6 ChildQuest 节点的字段(最厚的一个)
| 字段 | 作用 |
|---|---|
| Condition | 完成条件,类型是 TChildQuestCondition(50 选 1) |
| TidTip / EdTidTip | 追踪文本。Tid 走多语言,EdTid 只在编辑期可见 |
| TidTipPriority | 多条追踪文本时,优先级大的显示在上方 |
| ShowNavigation | 是否显示寻路 |
| TrackTarget | 追踪目标(位置 / 实体 / 刷怪器 / 分步 / 搬运物 五类) |
| EnterActions / FinishActions | 进入节点、退出节点时执行的行为列表 |
| HideUi | 隐藏任务 UI(跑纯逻辑步骤时用) |
3. 行为节点内容物:EAction(105 个)
Action 节点和 ChildQuest 的进入/退出钩子里挂的,都是 IActionInfo[]。
枚举定义在 Interface/IAction.ts,配置表在 Common/Operation/Action.ts 的 actionConfig。
actionConfig 里 83 条标 ImplementType: 'Server',
其余标 'Client'。「这个行为不生效」第一步就是看它归谁——服务器实现的行为,客户端根本没有对应代码可以断点。
LevelEventCenter(Game/LevelGamePlay/LevelEvents/)里
setup(EAction.X, LevelEventX) 一共 24 行。客户端侧能改的行为就这些,其余全在服务器。
3.1 actionConfig 的字段,决定节点「摆得出来吗」
// Common/Operation/Action.ts
interface IActionItem {
ImplementType: 'Client' | 'Server';
Context?: TActionContextType[]; // entity / levelPlay / quest / wildEvent / gm
RestrictedMapContext?: 'DungeonOnly' | 'WpOnly'; // 限副本 / 限大世界
IsTest?: boolean; // 非测试模式下会被过滤掉
ContextForTest?: TActionContextType[]; // 测试阶段额外开放的上下文
IsAbandoned?: boolean; // 废弃标记
Tags?: string[]; // 分类标签,如 ['HotFix']
}
Context 的分布(一个行为可以同时属于多个上下文):
| Context | 出现次数 | 含义 |
|---|---|---|
| levelPlay | 84 | 关卡主控树上可摆 |
| entity | 64 | 实体的交互 / 触发组件里可摆 |
| wildEvent | 54 | 野外事件树上可摆 |
| quest | 24 | 任务树上可摆 |
Context 里没列他所在的那种树;
② RestrictedMapContext 限了副本 / 大世界;
③ IsTest: true 而当前不是测试模式;
④ Scheme 加了但编辑器没重启——下拉在进程内只建一次。
4. 子任务节点内容物:EChildQuest(50 种)
ChildQuest 节点的 Condition.Type,决定这个步骤「等的是什么」。
枚举在 IQuest.ts,一共 50 项:击杀、到达区域、交互、等待时间、比较变量、检查刷怪器剩余数、爬塔星级……
4.1 客户端只实现了 17 种,其余 33 种兜底给服务器
// Game/Module/GeneralLogicTree/Define/NodeTypeDefine.ts
public static readonly ChildQuestNodeType: Record<EChildQuest, NodeTypeData> = {
[EChildQuest.DoInteractNew]: new NodeTypeData(InteractNewBehaviorNode), // 客户端专属实现
[EChildQuest.ReachArea]: new NodeTypeData(ReachAreaBehaviorNode),
[EChildQuest.TowerClimb]: new NodeTypeData(TowerClimbNode),
// …… 共 17 个有专属节点类
[EChildQuest.Kill]: new NodeTypeData(ServerAchieveChildQuestNode), // 服务器判定,客户端只收结果
[EChildQuest.CompareVar]: new NodeTypeData(ServerAchieveChildQuestNode),
// …… 共 33 个走这个兜底类
};
落在
ServerAchieveChildQuestNode → 客户端只是在等服务器发达成通知,
本地断点毫无意义,去看协议有没有到;有专属节点类 → 客户端自己在判,直接去那个
XxxNode.ts 里断点。
4.2 有客户端专属实现的那 17 种(按用途分)
| 用途 | 节点类 |
|---|---|
| 交互 / 移动 | InteractNewBehaviorNode、ReachAreaBehaviorNode |
| 等待 | WaitTimeNode、WaitEntityLoadedNode、WaitFlowAssetEndNode |
| 刷怪器 | CheckAiSpawnerRemainCountNode、CheckAiSpawnerKilledCountNode |
| 教学 / 引导 | CheckPlayerInputNode、CheckGuideCompleteNode、CheckGuideHaveCurrentTutorialNode、FinishComboTeachingNode、CheckQteCompleteNode |
| 状态检查 | CheckEntityStateNode、CheckItemNumBehaviorNode、CheckPlayerAttackTargetNode |
| UI 交互 | ClickQuestFinishTipNode |
| 玩法 | TowerClimbNode(→ 爬塔模块) |
5. 判断条件:ECondition(76 种)
Condition 节点、ConditionSelector 的分支条件、
Repeater 的退出条件,用的都是 IConditionGroup,里面是 ECondition。
| 项 | 数量 / 位置 |
|---|---|
| 枚举定义 | Interface/ICondition.ts,76 项 |
| 客户端求值器 | LevelConditionCenter 里 SetupCondition(...) 62 处 |
| 执行器文件 | Game/LevelGamePlay/LevelConditions/,62 个 LevelConditionCheckXxx.ts |
| 纯客户端条件 | ClientConditions/ 下 2 个(任务状态、子任务节点状态) |
ConditionChild.ts 穷举默认表 → scheme + 注册。第 3 层漏了是编译报错(还算好查), 第 4 层漏了是编译通过但下拉里没有(最难查)。
而且下拉三种树各一套:Quest 用
condition2Scheme、
关卡玩法用 …ForLevelPlay、野外事件用 …ForWildEvent——
只注册一处的结果就是「策划在 A 树看得到、B 树看不到」。完整实例见 W36-R01 的四层链路。
6. 客户端这边谁在执行
| 编辑器概念 | 客户端落点 | 规模 |
|---|---|---|
| EAction(行为) | LevelGamePlay/LevelEvents/LevelEventXxx.ts | 27 个文件 / 24 处注册 |
| ECondition(条件) | LevelGamePlay/LevelConditions/LevelConditionCheckXxx.ts | 62 个文件 / 62 处注册 |
| EChildQuest(子任务) | Module/GeneralLogicTree/BehaviorNode/ChildQuestNode/ | 17 个专属类 + 1 个服务器兜底类 |
| EBtNode(流程节点) | Module/GeneralLogicTree/BehaviorNode/LogicNode/ | NodeTypeDefine.NewNodeObj 只造 5 类 |
ChildQuest / Condition / QuestFailed / ParallelSelect / Sequence,
其余全部 default: break 返回 undefined。也就是说
Select、Repeater、ConditionSelector、AlwaysTrue/False
在客户端没有对应的运行时对象——它们的流程语义由服务器负责,
客户端只负责把服务器点亮的节点显示出来。这解释了为什么很多「树没往下走」的问题在客户端断不到点。
7. 加一个节点要动几处(速查)
Interface/IAction.ts 枚举 + 参数接口②
Common/Operation/Action.ts 的 actionConfig(Context / ImplementType)③
EditorCommon/Scheme/Action/*.ts 参数 Scheme④ 客户端实现的话,再加
LevelEventXxx.ts + LevelEventCenter 注册→ 详见 Scheme 页
ConditionChild.ts 穷举表 → scheme 注册(三种树各一处)。
客户端判定的还要加 LevelConditionCheckXxx.ts + SetupCondition。→ 详见 W36-R01
childQuestConfigs + scheme,
客户端要判定的话在 NodeTypeDefine.ChildQuestNodeType 里挂专属节点类,
否则挂 ServerAchieveChildQuestNode 由服务器判。Optional;
② 改完 grep 有没有第二个注册点;
③ 编辑器 Scheme 下拉进程内只建一次,必须重启编辑器才看得到新项。8. 待补清单
本页目前只写了节点类型。后面按这个顺序补(顺序可调):
| 待补章节 | 要讲清楚的问题 |
|---|---|
| 三种树的差异 | 主控树 / 任务树 / 野外事件树,各自能摆什么、不能摆什么,为什么 |
| 树变量与黑板 | IBehaviorTree.Vars 怎么定义、谁能读写、和服务器怎么同步 |
| 保存格式与导表 | 编辑器存的 json 长什么样、dbChildId 这类「仅 DB 数据有效」字段是干嘛的 |
| 节点状态与调试 | 树跑到哪一步怎么看、日志前缀、GM 指令 |
| 存档点(SaveConfig) | 挂在逻辑节点上的存档 / 回退点,回退时哪些状态会重置 |
| 热更节点(HotFixActionConfigs) | Tags: ['HotFix'] 的行为是怎么被筛出来的 |
| 与新版编辑器的对应关系 | 哪些概念保留、哪些被取代 —— 等新版那页有内容后一起写 |
d:\zhaozibo_071\Source\Client\TypeScript 分支上实测统计的。
分支不同数字会有出入,引用前先自己 grep 一遍。注:Scheme 页里「171 个 EAction / 82 个 Server」是更早分支的口径,两处不一致以本页为准。