2026-W36 周报
覆盖 2026/08/31(周一)– 2026/09/06(周日)。 四大类固定顺序:1 需求 · 2 Bug · 3 经验总结 · 4 问题复盘,大类下面直接 1. 2. 3. 排。 本周两条线:① 坠落重生(软重生)落点异常定位——QA 上周(W35)报的,本周定位完成; ② 关卡工具需求「为 Condition 增加副本集合功能」,同日交付。 两者按归周规则都属小耗时任务,只记在完成周。
周期08/31 – 09/06
需求4 个(已交付)
Bug3 条(含 3 个顺带发现)
经验总结0 条
问题复盘0 条
分支zhaozibo_071
要直接交周报?跳到 §5 飞书周报(一键复制)——三段成品,点一下就能贴。
0. 本周速览
| 大类 | # | 一句话 |
|---|---|---|
| 需求 | 1 | 【关卡工具】Repeater(循环)节点增加 tick 间隔、无限循环、超时时长与超时结果四个配置项 |
| 2 | 【关卡工具】为 Condition(条件)节点增加副本集合功能(等待副本集合完成) | |
| 3 | 【关卡工具】行为树 RemoveBuffAction 增加「移除全部」选项,一次清掉目标身上该 BuffID 的全部实例 | |
| 4 | 【主线】副本集合区域:进入区域后显示集合人数与自动传送倒计时,全员到齐或超时由服务器传送 | |
| Bug | 1 | 【主线】软边界坠落重生回来的地方又有问题,卡流程 |
| 2 | 【主线】野外事件倒计时过程中退出事件或完成事件,倒计时 UI 仍留在屏幕上 | |
| 3 | 【主线】显示倒计时时走到事件范围外退出事件,再次加入同一事件不显示倒计时 |
1需求4 个
本周做的新功能 / 改造 / 配置支持。 需求卡字段单元:模块 / 需求 / 实现细节 / 总结,没有的单元可以不写;跨周的加进度块,写法见 留档模板。
1.
【关卡工具】
Repeater(循环)节点增加 tick 间隔、无限循环、超时时长与超时结果四个配置项
模块:编辑器 Scheme · 行为树 Repeater 节点
需求
Repeater 节点原来只有
最大循环次数和循环退出条件两项,循环节奏和终止方式都不可配。策划要求增加四项,字段名与服务器 proto 对齐:TickIntervalMs 循环 tick 间隔(默认 1000ms,最小 500ms)、IsInfiniteLoop 无限循环(勾选后忽略最大循环次数)、InfiniteLoopTimeoutMs 无限循环超时时长(默认 -1 即永久)、IsTimeoutFail 超时后节点判定成功还是失败(默认成功)。
实现细节
编辑器侧
类型定义同步三处:
四项最终都定为纯可选(只留
repeaterBtNodeScheme 加四个字段,TickIntervalMs 挂 OnSchemeCheck 卡最小值 500(填 0 视为用默认值)。类型定义同步三处:
IRepeaterBtNode、导表脚本侧的同名镜像接口、以及 AI 建树用的 IBtPlanNode;两个 Executor 的 Repeater 分支补齐字段透传。四项最终都定为纯可选(只留
Optional,不给 IsDefaultCreate)——不配置就不写进 JSON,由服务器走 proto 默认值。
总结
编辑器字段的「可选」有三档,选错直接影响导出的 JSON 体积和默认值归属:
① 只写
② 加
③ 再加
渲染判断在
另外
① 只写
Optional → 真可选,不勾就不进 JSON,默认值归服务器;② 加
IsDefaultCreate → 新建节点时就写死进 JSON,老节点仍然没有;③ 再加
IsAlwaysShow + DefaultValue → 老节点也直接显示。渲染判断在
Obj.tsx:Optional && value === undefined && !IsAlwaysShow 就不渲染,所以②对老数据是看不见的,得去节点的「可选▼」里手动勾。IsAlwaysShow 必须配 DefaultValue,否则 OnBaseCheck 抛异常。另外
CreateDefault 是「从可选菜单勾选时填入的初始值」,不是「不配置时的值」——布尔项勾了却填 false 会让人以为没生效。
起止
提出并完成 2026/09/01(W36)
2.
【关卡工具】
为 Condition(条件)节点增加副本集合功能(等待副本集合完成)
模块:编辑器 Scheme · Condition / ChildQuest
需求
「副本集合」(等所有玩家进入集合区域)原来只有 ChildQuest 节点有
(
EChildQuest.WaitDungeonGather)。策划在 Condition 节点里配不出来,
只能绕着改任务结构。需求是 Condition 节点也支持,中文名定为「等待副本集合完成」。
实现细节
照抄 ChildQuest 侧的
waitDungeonGatherScheme,在 Condition 侧新建一份对等实现,
走完枚举 → 接口 → 穷举默认表 → scheme + 注册四层(见「总结」)。
实体选择器过滤 DungeonGatherComponent,只能选挂了集合组件的实体。
服务器判定,客户端不需要写求值器。
总结
新增一个 Condition 类型固定要过四层:枚举(
坑在第四层:
另外两条:
ICondition.ts)→
接口(同文件)→ 穷举默认表(ConditionChild.ts 的
CommonConditionAllDefault)→ scheme + 塞进聚合 ChildConfig(Condition.ts)。坑在第四层:
createDynamicChildScheme 建出来只是「定义了」,不塞进
ChildConfig 就永远不显示,而且编译完全通过——第三层刚把它设成
undefined,DynamicScheme.Init() 只收非 undefined 项。另外两条:
--noEmit 不产出文件,改完必须跑真编译并 grep 产物;
Scheme 下拉进程内只建一次,要重启编辑器才看得到新项。
影响面
只影响行为树 Condition 节点的条件类型下拉,三种树各一套:
Quest(
存量配置零影响——新增枚举值,不改任何已有分支。
condition2Scheme)、关卡玩法(…ForLevelPlay)、
野外事件(…ForWildEvent)。存量配置零影响——新增枚举值,不改任何已有分支。
验收
npm run compile-base -- tsconfig.json if-log EXIT=0;
产物 Condition.js 三个注册点齐;
重启编辑器后策划在 Condition 节点下拉里看到「等待副本集合完成」——已确认可见。
起止
提出 2026/08/31(W36)· 交付 2026/08/31(W36)· 当日完成
→ 小耗时,不用进度块
3.
【关卡工具】
行为树 RemoveBuffAction 增加「移除全部」选项,一次清掉目标身上该 BuffID 的全部实例
模块:编辑器 Scheme · 行为树 RemoveBuffAction
需求
RemoveBuffAction 原来只能按
BuffCount 扣层数,策划要一个「全部移除」开关。IsRemoveAll 这个字段 proto 和服务器早就有了(RemoveBuffContent 第 4 个字段,两个 Action 实现都已经走 removeAllBuffByConfigId),只有编辑器侧没暴露出来,所以这次只改 TS 编辑器,字段名沿用 IsRemoveAll,不动协议和 C++。
实现细节
定成纯可选可叉掉:只给
IRemoveBuffAction 加 IsRemoveAll?: boolean,removeBuffActionScheme 加一个 createBooleanScheme,排在 Target 之后(UI 顺序 = Fields 声明顺序)。定成纯可选可叉掉:只给
Optional,不给 IsAlwaysShow——× 删除按钮的渲染条件是 Optional && !IsAlwaysShow,加了 IsAlwaysShow 就没有叉。CreateDefault 给 true:从「可选▼」里勾出这个字段本身就是「我要移除全部」的意思,勾出来还是 false 会让人以为没生效。
起止
提出并完成 2026/09/01(W36)
顺带发现
①「全部层数」这一半做不了,需求原文要「全部层数 + 全部实例」两个子选项,现在只有后者。底层
②
removeBuffByConfigId(remove_stack_cnt = 0) 本来就是「删一个实例的全部层数」,但服务器两个 Action 里 BuffCount == 0 ? 1 : BuffCount 把 0 吃成了 1,通路是断的。要做得让服务器把 IsRemoveAll 拆成三态,编辑器再跟着补下拉。②
removeBuffActionScheme 的 Name 写的是 EAction.AddBuffAction,跟「移除 Buff」对不上,疑似复制添加 Buff 时漏改,本次没动。
4.
【主线】
副本集合区域:进入区域后显示集合人数与自动传送倒计时,全员到齐或超时由服务器传送
模块:关卡 · 副本集合区域 / 集合提示 UI
需求
副本里摆一块集合区域,玩家进入后开始集合:屏幕上提示当前已集合人数 / 总人数,以及多少秒后自动传送;倒计时结束由服务器把仍在区域外的玩家强制传送到集合点。
人数统计、集合状态机、强制传送全部在服务器(
人数统计、集合状态机、强制传送全部在服务器(
gather_comp.cpp),客户端本期只做三件事:范围实体进出检测、提示 UI 显示刷新、边界特效反馈。
实现细节
UI 复用匹配模块的现成预设
提示由关卡级
倒计时完全以服务器
编辑器侧文案只出一行:
UiItem_MatchingMeetingTips,两行拆开:上行 TxtNum 出「(2/5)」,与预设自带静态文案同行排列;下行 TxtCD 出配置文案(含倒计时)。提示由关卡级
DungeonGatherController 驱动:监听 EntityGatherUpdateSCNotify(CmdId 14050)刷新,文案按广播里的 entityConfigId 直接查静态配置表(关卡实例ID高位为 0,与 PrefabDataId 同域),不依赖集合区实体是否在本地视野内;进关卡 / 重连靠 EndClientLoading 用全量 gatherInfo 补一次。倒计时完全以服务器
forceTeleportTimeMs 时间戳为准,本地只起一个 1s Timer 逐秒重算剩余秒数;算出 ≤ 0 就停 Timer 并隐藏 UI。编辑器侧文案只出一行:
TidTeleportText 挂 HideFlag: ETabFlag.AlwaysHide 隐掉正式文本行,Key 由 ObjectScheme.OnBaseModify 从 EdTid 自动同步,多语言不受影响。
总结
这次踩到的五条模块知识,除第①条外都跟集合区本身无关,做任何新 UI 都会撞上:
① 关卡级广播驱动的 UI,不能挂在 AOI 实体组件上。最初把提示做成实体组件
② 客户端运行时的 UI 资源配置读的是 SQLite,不是 json / csv。真正被读的文件是
③ 一个新 UI 界面要登记四处:
④ LGUI 控件是按预设
⑤ 集合区功能单人测不出来。服务器
① 关卡级广播驱动的 UI,不能挂在 AOI 实体组件上。最初把提示做成实体组件
EtDungeonGatherComponent(照 EtBoundaryRespawnComponent 的样子写),结果离集合区远的玩家完全看不到提示:服务器 EntityGatherComp::bcUpdateNotify() 走 LevelInst::bc() 遍历 player_map_ 全员下发(不是 bcAoi),而客户端场景物件实体是 AOI 增删的(ServerLevel.UpdatePartial 按 enterEntityList / leaveEntityList),远处玩家的组件压根不存在,广播只写进 Model 没人消费;跑远时 AOI 销毁实体触发 OnEnd() → Hide(),倒计时恰好在最需要它的那个人脸上被关掉;进关卡就在远处时组件不存在,全量兜底也不执行。改成关卡级 Controller 后组件整体删除。判据:这个 UI 该由关卡状态决定可见性,还是由某个实体在不在视野里决定?前者一律挂 Controller;实体组件只做「近处才该看见」的本地表现(特效 / 音效 / 交互提示)。同理适用于任何 LevelInst::bc() 全员下发的协议。② 客户端运行时的 UI 资源配置读的是 SQLite,不是 json / csv。真正被读的文件是
Client/Content/Aki/ConfigDB/db_ui_resource.db,表里存的是 FlatBuffers blob(SELECT BinData FROM UiResource WHERE Id = ?)。改完 u.UI资源.csv 必须导表,只改 csv 或 UiResource.json 运行时完全看不到。单表导出命令:dotnet ConfigTool.Cmd.dll config -j <proj> -r <configdir> -e -d -a -l -t 1 -f <filter>(-t 1 只导客户端,-f 指定单表过滤文件,几秒钟完事);导表工具是删掉旧 db 重新建,所以编辑器开着占用文件也不影响。另外查不到的 Id 会被 ConfigCommon.SaveConfig(key, NotFound) 缓存,改完 db 必须重启客户端,热重载 TS 没用。③ 一个新 UI 界面要登记四处:
EUiViewName 枚举 → UiViewStorage.RegisterUiTsInfo → UI资源表(即上面那个 db) → UiShow 表显示配置。少任何一处报的都是 [UiConfig.TryGetViewInfo] 找不到界面配置。④ LGUI 控件是按预设
LGUIComponentsRegistry.Components 的数组下标取的,不是按名字。代码里 ComponentRegisterInfos 填的那个数字就是 registry 里的位置,UI 那边调整登记顺序,代码必须跟着改;没有「按路径取嵌套控件」的 API。数量或类型对不上时报 [FindInitedComponentRegistryActor]。⑤ 集合区功能单人测不出来。服务器
total_num = getPlayerNum() 不可覆盖,startGather() 里紧接着就判 in_range_uid_set_.size() ≥ total_num,单人时 1≥1 直接进 FINISHED 并把 force_teleport_time_ms 清 0,客户端连倒计时都收不到。必须 2 人以上联机验证——也正因为如此,上面第①条那个「远处看不到」的问题单人自测根本暴露不出来。
起止
提出 2026/08(W35)· 客户端提示 UI 完成 2026/09/02 · 远处玩家不显示修复 2026/09/03(W36)
环境
测试关卡副本 · 集合区域关卡实例
遗留
① 协议缺状态字段:
② 文案重复:配置
③ 边界进出特效没落点:原计划挂在已删除的实体组件上。特效本来就该由实体组件做(本地表现,也本该只有近处的人看得见),要做时新建一个只管特效的组件,别把 UI 驱动搬回实体上。
④ 尚未联机实测:远处玩家能否看到提示需 2 人以上验证,改完 db 记得重启客户端(
force_teleport_time_ms == 0 同时表示 IDLE(未开始)和 FINISHED(已完成),客户端区分不了。本期两种都按隐藏处理没问题,但要做「集合完成」的成功表现(音效 / 特效 / 文案切换)就必须让服务器加 state 字段。② 文案重复:配置
TidTeleportText 目前是 {0}/{1} 玩家已集合 | {2}秒后自动传送,拆成两行后人数在上下两行各出一次。需要策划把它改成只剩 {2}秒后自动传送,客户端不用动代码。③ 边界进出特效没落点:原计划挂在已删除的实体组件上。特效本来就该由实体组件做(本地表现,也本该只有近处的人看得见),要做时新建一个只管特效的组件,别把 UI 驱动搬回实体上。
④ 尚未联机实测:远处玩家能否看到提示需 2 人以上验证,改完 db 记得重启客户端(
NotFound 缓存不吃热重载)。
2Bug3 条
本周处理的线上 / QA 问题。一个 bug 一条——同一次排查顺带挖出来的附带问题进「顺带发现」单元,不单独立条。
1.
【主线】
任务513000281
软边界坠落重生回来的地方又有问题,卡流程
模块:软边界重生 / 坠落重生
问题描述详细版本
关卡 40065010,掉进坠落区域触发软重生后落点在门外侧,门是单向的,回不去,主线卡死。
同时报「14 秒才重生」。
原因
复活点配置位置错误,坐标落在模型内部。
个人复活点是在管道传输期间被刷新掉的:玩家被管道带着飞过 Entity 513002412「存档04_通道复活点」的范围盒,这段路玩家不可控、也没有停留, 但盒子上挂的
「14 秒」里代码只占 3.2 s(2.5 s Tip + 0.6 s 黑屏流送),其余 11.6 s 是玩家在触发盒边缘反复起落。
个人复活点是在管道传输期间被刷新掉的:玩家被管道带着飞过 Entity 513002412「存档04_通道复活点」的范围盒,这段路玩家不可控、也没有停留, 但盒子上挂的
UpdatePersonalRevivePoint 照常触发,把个人复活点覆盖成这个 Entity;坠落后走
FallZoneRebornRuleId = 1(服务器复活点),三级查找命中第二级,返回它的
Transform.Pos (-18754.02, -121482.02, -7155)——
这个点埋在 StaticMesh 里,FixBornLocation 检到
bStartPenetrating 后抬高射线贴地到 Z = -7081.28,正好门外。「14 秒」里代码只占 3.2 s(2.5 s Tip + 0.6 s 黑屏流送),其余 11.6 s 是玩家在触发盒边缘反复起落。
解决方案
策划把 513002412 的
Transform.Pos 移到门内侧可站立地面。
待配置修改后进游戏复测落点。
总结
落点类问题先做数值比对——最终落点和 Entity 的
耗时投诉要先拆「代码耗时」和「玩家耗时」,从真正的触发事件开始算,别从玩家开始下坠算。
Transform.Pos 完全一致,
既 ≠ 关卡出生点也 ≠ 客户端安全点,不读一行逻辑就锁定来源。耗时投诉要先拆「代码耗时」和「玩家耗时」,从真正的触发事件开始算,别从玩家开始下坠算。
顺带发现
三个待改项,跟这条一起处理:
①
② FallingRebornController.ts:511 覆盖成功那条路径没日志,只在被拒绝时打 Warn。
③ EtBoundaryRespawnComponent.ts:585 注释写「不使用个人复活点」,下一行却传
①
Params.Location 填了不生效——服务器 findRevivePointOpt() 只读 Entity 的
Transform.Pos,本次两者差 1400+ 单位,策划在编辑器界面无从察觉。要么让它真生效、要么在 Scheme 层面藏掉。② FallingRebornController.ts:511 覆盖成功那条路径没日志,只在被拒绝时打 Warn。
③ EtBoundaryRespawnComponent.ts:585 注释写「不使用个人复活点」,下一行却传
FallZoneRebornRuleId = 1。1 是对的,注释是错的,②③ 一个 CL 提。
遗留风险
挪坐标只治好这一处。管道传输途中不屏蔽复活点记录这件事没解——
被动位移(管道 / 传送 / 剧情移动)穿过任意挂了
UpdatePersonalRevivePoint 的范围盒,
个人复活点都会被覆盖成一个玩家其实从没落地站过的点。
只要还有关卡把复活点盒子摆在管道路径上,同一个现象会换个关卡再来一次。
要定的是:被动位移期间是否应该整体屏蔽记录点触发。
起止
提出 2026/08/27(W35)· 定位完成 2026/08/31(W36)· 净耗时不到 1 天
关卡 40065010 · UID 27111981068 · 日志 Client-backup-2026.08.27-19.28.10.logcompr
罪魁配置 …/40065010/曾卓枫/513002412_存档04_通道复活点.json
关卡 40065010 · UID 27111981068 · 日志 Client-backup-2026.08.27-19.28.10.logcompr
罪魁配置 …/40065010/曾卓枫/513002412_存档04_通道复活点.json
2.
【主线】
野外事件倒计时过程中退出事件或完成事件,倒计时 UI 仍留在屏幕上
模块:行为树计时器(TimerAction)· 野外事件
问题描述详细版本
进 30100112 关卡 →
服务器侧原来是「玩家离开副本时下发一条定时器协议通知关闭」修的,组长要求改成客户端自主控制(少一次往返,也不会因丢包导致 UI 与玩法状态不一致),服务器会删掉那段下发逻辑,并在增量协议 15016 里补了归属字段。
#pre_start_wild_event 514000077 开启事件 → 传送进事件范围 → 秒杀怪进入下一节点显示倒计时,此时走出事件范围或 #finish_wild_event 514000077,事件已经结束了但倒计时 UI 还在跑,一直挂到倒计时自然归零才收起。服务器侧原来是「玩家离开副本时下发一条定时器协议通知关闭」修的,组长要求改成客户端自主控制(少一次往返,也不会因丢包导致 UI 与玩法状态不一致),服务器会删掉那段下发逻辑,并在增量协议 15016 里补了归属字段。
原因
于是退出 / 完成野外事件、行为树被销毁时,客户端手里没有任何线索能判断该清哪条计时器,只能等
BombTimerModel.Timers 以 timerName 为 key,存的计时器不知道自己属于哪棵行为树。15016 WorldBehaviorTreeTimerParameterSCNotify 已经带了 type / btTypeId,但客户端只取 btTimerMsg 就丢掉了。于是退出 / 完成野外事件、行为树被销毁时,客户端手里没有任何线索能判断该清哪条计时器,只能等
GetDisplayTime 把值推到 0、状态自动置 Stopped 后才被 GetDisplayTimerName 过滤掉——这就是「倒计时仍显示」。
解决方案
① 计时器落地时记住归属:
② 清理挂在既有事件
③
④ 顺手修了 UI 侧的残留:见「顺带发现 ①」。
SetInfo / SetList 增加 treeIncId(type_btTypeId)与 btType 必填参数,存进 TBombTimerWithExtra。② 清理挂在既有事件
EEventName.GeneralLogicTreeRemove 上,按 TreeIncId 精确删(只删归属相符的,避免不同树的同名计时器互删),删到了就 Emit BombTimerChanged 让 UI 自己收起。③
OnLeaveLevel 兜底:只保留任务树(跨关卡存活)的计时器,其余含归属缺失的一并清掉。④ 顺手修了 UI 侧的残留:见「顺带发现 ①」。
总结
行为树派生出来的 UI,清理时机统一挂
前提是这类数据落地时必须带上
GeneralLogicTreeRemove,一个监听点就够——它由 ApplyRemoveBehaviorTree 统一 Emit,天然覆盖退出野外事件(34504)、完成(34505)、事件结束(34507)、服务器删树(15004)、以及离开副本时批量删树五个场景,不用每条协议各接一次,也不需要服务器专门下发一条「关闭 UI」。前提是这类数据落地时必须带上
treeIncId 归属;只按业务名(这里是 timerName)做 key 的缓存,树一销毁就成了没人认领的孤儿数据。
起止
提出并完成 2026/09/02(W36)
环境
关卡 30100112 · 野外事件 514000077
顺带发现
① 倒计时走完只停刷新没收面板。
② 重进事件不显示倒计时是另一条。复现路径不一样(退出后再次加入),按「一个现象一条」单独立条:重进野外事件不显示倒计时 UI。
LevelUiTimerView.OnUpdate 里「正计时超目标值」「倒计时到负数」两处只调 ClearTimer() 就 return,定时器停了但面板还开着,数字冻结在屏幕上;而且 IsUiActive 停留 true,下次显示不播 Start 动画。抽 HideTimerUi()(停刷新 + 收面板 + 复位标志)统一调用。② 重进事件不显示倒计时是另一条。复现路径不一样(退出后再次加入),按「一个现象一条」单独立条:重进野外事件不显示倒计时 UI。
复测说明
本地起不了复现环境,改动只过了自定义编译并 grep 产物核对 emit,等 QA 复测。
3.
【主线】
显示倒计时时走到事件范围外退出事件,再次加入同一事件不显示倒计时
模块:行为树计时器(TimerAction)· 野外事件
问题描述详细版本
进 30100112 关卡 →
#pre_start_wild_event 514000077 → 进事件范围秒杀怪,倒计时正常出来 → 走出事件范围直到服务器判定退出事件 → 再走回去重新加入,事件本身还在跑(倒计时也还在走),但屏幕上不出倒计时 UI。与退出 / 完成事件倒计时仍显示是同一模块的两个方向,复现路径不同,分开两条。
原因
客户端手里的计时器只有增量 15016 一个来源,而增量只在计时器发生变化时下发——重新加入一个已经在跑的事件,服务器不会再补一条增量。
恢复用的数据其实一直都发了:34501
Model 里没数据 →
恢复用的数据其实一直都发了:34501
WorldJoinWildEventSCNotify 带 wildEventBt(BehaviorTreeMsg),里面就有 timerList。但 WildEventController.CreateWildEventBT 只把它整个交给 GeneralLogicTreeModel.CreateBehaviorTree 建树,timerList 全程没人读(客户端只在进关卡时消费过 levelBt.timerList)。Model 里没数据 →
GetDisplayTimerName() 返回 undefined → UI 判定「没有要显示的计时器」。
解决方案
①
②
③ 全量与增量两条落地路径写入前,都先把该树在分帧队列里积压的增删同步排空。
BombTimerController 加公开方法 ApplyTreeTimerList(treeInfo):从全量里取 type / btTypeId / timerList,走已有的 SetList 带归属落地,再 Emit BombTimerChanged 让 UI 自己刷出来。②
WildEventController.CreateWildEventBT 里加一行调用。它是野外事件建树的唯一入口,所以一处改动同时覆盖「重新 join」(34501)和「进关卡 / 重连时重建进行中的事件」两条路径。③ 全量与增量两条落地路径写入前,都先把该树在分帧队列里积压的增删同步排空。
CreateBehaviorTree / RemoveBehaviorTree 都是入队、下一帧 Tick 才执行,退出与重进间隔不到一帧时,队列里那条待执行的删树会 Emit GeneralLogicTreeRemove,把刚落地的计时器又清掉(不管数据来自全量还是 15016)。ApplyCreateBehaviorTree 开头自带的 FlushByTree 救不了——Process() 期间 IsDraining 为 true,flush 直接 no-op。
总结
一个 msgId 在 Net 层只能注册一个 handler——
另一条更通用:增量协议只在「变化时」下发,凡是「重进 / 重连要恢复显示」的数据,都必须另有一个全量来源。写消费代码时顺手确认全量里那个同名字段有没有人读——这次的
NetConnection.Register 发现 NotifyHandles 里已有就直接打 Error 并 return false,不是叠加也不是覆盖,后注册的那个静默失效。所以想在别的模块里消费一条已被占用的协议(这里 34501 归 WildEventController),只能从原注册点调过来,不能自己再 Register 一次。做法是把逻辑留在自己模块里暴露成公开方法,调用点只占一行,模块边界不糊。另一条更通用:增量协议只在「变化时」下发,凡是「重进 / 重连要恢复显示」的数据,都必须另有一个全量来源。写消费代码时顺手确认全量里那个同名字段有没有人读——这次的
timerList 在协议里躺了很久没人碰。
起止
提出并完成 2026/09/03(W36)
环境
关卡 30100112 · 野外事件 514000077
复测说明
根因尚未确认,不算已解决。服务器称重进时他有下发定时器通知:若下发的是 15016 增量,客户端本来就会落地,那问题在显示条件(
showUi / status,或 currentValue 与 sendTimeStamp 的组合被 GetDisplayTime 判成 Stopped 过滤掉)而不是漏读全量,本条的修法就没打中根因。已向服务器要日志,过滤 行为树计时器 看有没有 SetInfo 落地记录即可分流。本地也起不了环境复测。
3经验总结0 条
可复用的模块知识 / 工具用法。这一类只在明确要求时才写——不主动凑。
本周无
4问题复盘0 条
排查走了弯路、造成外部代价的事。这一类只在明确要求时才写——不主动凑。
本周无
5. 飞书周报(一键复制)
下面这块是按飞书周报模板排好的成品——三段固定:本周工作进展 / 下周工作计划 / 待讨论的问题。 点「复制」直接贴进飞书,不用再排版。上面 1–4 节是这三段的底稿,这里是压缩后的对外版。
飞书周报 · 2026-W36(08/31 – 09/06)
file:// 下若复制被拒,会自动帮你选中,按 Ctrl+C
1.本周工作进展:
a. 【关卡工具】Repeater(循环)节点增加 tick 间隔、无限循环、超时时长与超时结果四个配置项
模块:编辑器 Scheme · 行为树 Repeater 节点
需求:Repeater 节点原来只有最大循环次数和循环退出条件两项,循环节奏和终止方式都不可配。策划要求增加四项,字段名与服务器 proto 对齐:
TickIntervalMs 循环 tick 间隔(默认 1000ms,最小 500ms)、IsInfiniteLoop 无限循环(勾选后忽略最大循环次数)、InfiniteLoopTimeoutMs 无限循环超时时长(默认 -1 即永久)、IsTimeoutFail 超时后节点判定成功还是失败(默认成功)。
实现细节:编辑器侧 repeaterBtNodeScheme 加四个字段,TickIntervalMs 挂 OnSchemeCheck 卡最小值 500(填 0 视为用默认值)。
类型定义同步三处:IRepeaterBtNode、导表脚本侧的同名镜像接口、以及 AI 建树用的 IBtPlanNode;两个 Executor 的 Repeater 分支补齐字段透传。
四项最终都定为纯可选(只留 Optional,不给 IsDefaultCreate)——不配置就不写进 JSON,由服务器走 proto 默认值。
总结:编辑器字段的「可选」有三档,选错直接影响导出的 JSON 体积和默认值归属:
① 只写 Optional → 真可选,不勾就不进 JSON,默认值归服务器;
② 加 IsDefaultCreate → 新建节点时就写死进 JSON,老节点仍然没有;
③ 再加 IsAlwaysShow + DefaultValue → 老节点也直接显示。
渲染判断在 Obj.tsx:Optional && value === undefined && !IsAlwaysShow 就不渲染,所以②对老数据是看不见的,得去节点的「可选▼」里手动勾。IsAlwaysShow 必须配 DefaultValue,否则 OnBaseCheck 抛异常。
另外 CreateDefault 是「从可选菜单勾选时填入的初始值」,不是「不配置时的值」——布尔项勾了却填 false 会让人以为没生效。
b. 【关卡工具】为 Condition(条件)节点增加副本集合功能(等待副本集合完成)
模块:编辑器 Scheme · Condition / ChildQuest
需求:「副本集合」(等所有玩家进入集合区域)原来只有 ChildQuest 节点有(EChildQuest.WaitDungeonGather)。策划在 Condition 节点里配不出来,只能绕着改任务结构。需求是 Condition 节点也支持,中文名定为「等待副本集合完成」。
实现细节:照抄 ChildQuest 侧的 waitDungeonGatherScheme,在 Condition 侧新建一份对等实现,走完枚举 → 接口 → 穷举默认表 → scheme + 注册四层(见「总结」)。实体选择器过滤 DungeonGatherComponent,只能选挂了集合组件的实体。服务器判定,客户端不需要写求值器。
总结:新增一个 Condition 类型固定要过四层:枚举(ICondition.ts)→ 接口(同文件)→ 穷举默认表(ConditionChild.ts 的 CommonConditionAllDefault)→ scheme + 塞进聚合 ChildConfig(Condition.ts)。
坑在第四层:createDynamicChildScheme 建出来只是「定义了」,不塞进 ChildConfig 就永远不显示,而且编译完全通过——第三层刚把它设成 undefined,DynamicScheme.Init() 只收非 undefined 项。
另外两条:--noEmit 不产出文件,改完必须跑真编译并 grep 产物; Scheme 下拉进程内只建一次,要重启编辑器才看得到新项。
影响面:只影响行为树 Condition 节点的条件类型下拉,三种树各一套: Quest(condition2Scheme)、关卡玩法(…ForLevelPlay)、野外事件(…ForWildEvent)。
存量配置零影响——新增枚举值,不改任何已有分支。
验收:npm run compile-base -- tsconfig.json if-log EXIT=0;产物 Condition.js 三个注册点齐;重启编辑器后策划在 Condition 节点下拉里看到「等待副本集合完成」——已确认可见。
c. 【关卡工具】行为树 RemoveBuffAction 增加「移除全部」选项,一次清掉目标身上该 BuffID 的全部实例
模块:编辑器 Scheme · 行为树 RemoveBuffAction
需求:RemoveBuffAction 原来只能按 BuffCount 扣层数,策划要一个「全部移除」开关。
IsRemoveAll 这个字段 proto 和服务器早就有了(RemoveBuffContent 第 4 个字段,两个 Action 实现都已经走 removeAllBuffByConfigId),只有编辑器侧没暴露出来,所以这次只改 TS 编辑器,字段名沿用 IsRemoveAll,不动协议和 C++。
实现细节:IRemoveBuffAction 加 IsRemoveAll?: boolean,removeBuffActionScheme 加一个 createBooleanScheme,排在 Target 之后(UI 顺序 = Fields 声明顺序)。
定成纯可选可叉掉:只给 Optional,不给 IsAlwaysShow——× 删除按钮的渲染条件是 Optional && !IsAlwaysShow,加了 IsAlwaysShow 就没有叉。
CreateDefault 给 true:从「可选▼」里勾出这个字段本身就是「我要移除全部」的意思,勾出来还是 false 会让人以为没生效。
顺带发现:①「全部层数」这一半做不了,需求原文要「全部层数 + 全部实例」两个子选项,现在只有后者。底层 removeBuffByConfigId(remove_stack_cnt = 0) 本来就是「删一个实例的全部层数」,但服务器两个 Action 里 BuffCount == 0 ? 1 : BuffCount 把 0 吃成了 1,通路是断的。要做得让服务器把 IsRemoveAll 拆成三态,编辑器再跟着补下拉。
② removeBuffActionScheme 的 Name 写的是 EAction.AddBuffAction,跟「移除 Buff」对不上,疑似复制添加 Buff 时漏改,本次没动。
d. 【主线】副本集合区域:进入区域后显示集合人数与自动传送倒计时,全员到齐或超时由服务器传送
模块:关卡 · 副本集合区域 / 集合提示 UI
需求:副本里摆一块集合区域,玩家进入后开始集合:屏幕上提示当前已集合人数 / 总人数,以及多少秒后自动传送;倒计时结束由服务器把仍在区域外的玩家强制传送到集合点。
人数统计、集合状态机、强制传送全部在服务器(gather_comp.cpp),客户端本期只做三件事:范围实体进出检测、提示 UI 显示刷新、边界特效反馈。
实现细节:UI 复用匹配模块的现成预设 UiItem_MatchingMeetingTips,两行拆开:上行 TxtNum 出「(2/5)」,与预设自带静态文案同行排列;下行 TxtCD 出配置文案(含倒计时)。
提示由关卡级 DungeonGatherController 驱动:监听 EntityGatherUpdateSCNotify(CmdId 14050)刷新,文案按广播里的 entityConfigId 直接查静态配置表(关卡实例ID高位为 0,与 PrefabDataId 同域),不依赖集合区实体是否在本地视野内;进关卡 / 重连靠 EndClientLoading 用全量 gatherInfo 补一次。
倒计时完全以服务器 forceTeleportTimeMs 时间戳为准,本地只起一个 1s Timer 逐秒重算剩余秒数;算出 ≤ 0 就停 Timer 并隐藏 UI。
编辑器侧文案只出一行:TidTeleportText 挂 HideFlag: ETabFlag.AlwaysHide 隐掉正式文本行,Key 由 ObjectScheme.OnBaseModify 从 EdTid 自动同步,多语言不受影响。
总结:这次踩到的五条模块知识,除第①条外都跟集合区本身无关,做任何新 UI 都会撞上:
① 关卡级广播驱动的 UI,不能挂在 AOI 实体组件上。最初把提示做成实体组件 EtDungeonGatherComponent(照 EtBoundaryRespawnComponent 的样子写),结果离集合区远的玩家完全看不到提示:服务器 EntityGatherComp::bcUpdateNotify() 走 LevelInst::bc() 遍历 player_map_ 全员下发(不是 bcAoi),而客户端场景物件实体是 AOI 增删的(ServerLevel.UpdatePartial 按 enterEntityList / leaveEntityList),远处玩家的组件压根不存在,广播只写进 Model 没人消费;跑远时 AOI 销毁实体触发 OnEnd() → Hide(),倒计时恰好在最需要它的那个人脸上被关掉;进关卡就在远处时组件不存在,全量兜底也不执行。改成关卡级 Controller 后组件整体删除。判据:这个 UI 该由关卡状态决定可见性,还是由某个实体在不在视野里决定?前者一律挂 Controller;实体组件只做「近处才该看见」的本地表现(特效 / 音效 / 交互提示)。同理适用于任何 LevelInst::bc() 全员下发的协议。
② 客户端运行时的 UI 资源配置读的是 SQLite,不是 json / csv。真正被读的文件是 Client/Content/Aki/ConfigDB/db_ui_resource.db,表里存的是 FlatBuffers blob(SELECT BinData FROM UiResource WHERE Id = ?)。改完 u.UI资源.csv 必须导表,只改 csv 或 UiResource.json 运行时完全看不到。单表导出命令:dotnet ConfigTool.Cmd.dll config -j <proj> -r <configdir> -e -d -a -l -t 1 -f <filter>(-t 1 只导客户端,-f 指定单表过滤文件,几秒钟完事);导表工具是删掉旧 db 重新建,所以编辑器开着占用文件也不影响。另外查不到的 Id 会被 ConfigCommon.SaveConfig(key, NotFound) 缓存,改完 db 必须重启客户端,热重载 TS 没用。
③ 一个新 UI 界面要登记四处:EUiViewName 枚举 → UiViewStorage.RegisterUiTsInfo → UI资源表(即上面那个 db) → UiShow 表显示配置。少任何一处报的都是 [UiConfig.TryGetViewInfo] 找不到界面配置。
④ LGUI 控件是按预设 LGUIComponentsRegistry.Components 的数组下标取的,不是按名字。代码里 ComponentRegisterInfos 填的那个数字就是 registry 里的位置,UI 那边调整登记顺序,代码必须跟着改;没有「按路径取嵌套控件」的 API。数量或类型对不上时报 [FindInitedComponentRegistryActor]。
⑤ 集合区功能单人测不出来。服务器 total_num = getPlayerNum() 不可覆盖,startGather() 里紧接着就判 in_range_uid_set_.size() ≥ total_num,单人时 1≥1 直接进 FINISHED 并把 force_teleport_time_ms 清 0,客户端连倒计时都收不到。必须 2 人以上联机验证——也正因为如此,上面第①条那个「远处看不到」的问题单人自测根本暴露不出来。
遗留:① 协议缺状态字段:force_teleport_time_ms == 0 同时表示 IDLE(未开始)和 FINISHED(已完成),客户端区分不了。本期两种都按隐藏处理没问题,但要做「集合完成」的成功表现(音效 / 特效 / 文案切换)就必须让服务器加 state 字段。
② 文案重复:配置 TidTeleportText 目前是 {0}/{1} 玩家已集合 | {2}秒后自动传送,拆成两行后人数在上下两行各出一次。需要策划把它改成只剩 {2}秒后自动传送,客户端不用动代码。
③ 边界进出特效没落点:原计划挂在已删除的实体组件上。特效本来就该由实体组件做(本地表现,也本该只有近处的人看得见),要做时新建一个只管特效的组件,别把 UI 驱动搬回实体上。
④ 尚未联机实测:远处玩家能否看到提示需 2 人以上验证,改完 db 记得重启客户端(NotFound 缓存不吃热重载)。
e. 【主线】任务513000281,软边界坠落重生回来的地方又有问题,卡流程
模块:软边界重生 / 坠落重生
问题描述详细版本:关卡 40065010,掉进坠落区域触发软重生后落点在门外侧,门是单向的,回不去,主线卡死。同时报「14 秒才重生」。
原因:复活点配置位置错误,坐标落在模型内部。
个人复活点是在管道传输期间被刷新掉的:玩家被管道带着飞过 Entity 513002412「存档04_通道复活点」的范围盒,这段路玩家不可控、也没有停留,但盒子上挂的 UpdatePersonalRevivePoint 照常触发,把个人复活点覆盖成这个 Entity;坠落后走 FallZoneRebornRuleId = 1(服务器复活点),三级查找命中第二级,返回它的 Transform.Pos (-18754.02, -121482.02, -7155)—— 这个点埋在 StaticMesh 里,FixBornLocation 检到 bStartPenetrating 后抬高射线贴地到 Z = -7081.28,正好门外。
「14 秒」里代码只占 3.2 s(2.5 s Tip + 0.6 s 黑屏流送),其余 11.6 s 是玩家在触发盒边缘反复起落。
解决方案:策划把 513002412 的 Transform.Pos 移到门内侧可站立地面。待配置修改后进游戏复测落点。
总结:落点类问题先做数值比对——最终落点和 Entity 的 Transform.Pos 完全一致,既 ≠ 关卡出生点也 ≠ 客户端安全点,不读一行逻辑就锁定来源。
耗时投诉要先拆「代码耗时」和「玩家耗时」,从真正的触发事件开始算,别从玩家开始下坠算。
顺带发现:三个待改项,跟这条一起处理:
① Params.Location 填了不生效——服务器 findRevivePointOpt() 只读 Entity 的 Transform.Pos,本次两者差 1400+ 单位,策划在编辑器界面无从察觉。要么让它真生效、要么在 Scheme 层面藏掉。
② FallingRebornController.ts:511 覆盖成功那条路径没日志,只在被拒绝时打 Warn。
③ EtBoundaryRespawnComponent.ts:585 注释写「不使用个人复活点」,下一行却传 FallZoneRebornRuleId = 1。1 是对的,注释是错的,②③ 一个 CL 提。
遗留风险:挪坐标只治好这一处。管道传输途中不屏蔽复活点记录这件事没解—— 被动位移(管道 / 传送 / 剧情移动)穿过任意挂了 UpdatePersonalRevivePoint 的范围盒,个人复活点都会被覆盖成一个玩家其实从没落地站过的点。只要还有关卡把复活点盒子摆在管道路径上,同一个现象会换个关卡再来一次。要定的是:被动位移期间是否应该整体屏蔽记录点触发。
f. 【主线】野外事件倒计时过程中退出事件或完成事件,倒计时 UI 仍留在屏幕上
模块:行为树计时器(TimerAction)· 野外事件
问题描述详细版本:进 30100112 关卡 → #pre_start_wild_event 514000077 开启事件 → 传送进事件范围 → 秒杀怪进入下一节点显示倒计时,此时走出事件范围或 #finish_wild_event 514000077,事件已经结束了但倒计时 UI 还在跑,一直挂到倒计时自然归零才收起。
服务器侧原来是「玩家离开副本时下发一条定时器协议通知关闭」修的,组长要求改成客户端自主控制(少一次往返,也不会因丢包导致 UI 与玩法状态不一致),服务器会删掉那段下发逻辑,并在增量协议 15016 里补了归属字段。
原因:BombTimerModel.Timers 以 timerName 为 key,存的计时器不知道自己属于哪棵行为树。15016 WorldBehaviorTreeTimerParameterSCNotify 已经带了 type / btTypeId,但客户端只取 btTimerMsg 就丢掉了。
于是退出 / 完成野外事件、行为树被销毁时,客户端手里没有任何线索能判断该清哪条计时器,只能等 GetDisplayTime 把值推到 0、状态自动置 Stopped 后才被 GetDisplayTimerName 过滤掉——这就是「倒计时仍显示」。
解决方案:① 计时器落地时记住归属:SetInfo / SetList 增加 treeIncId(type_btTypeId)与 btType 必填参数,存进 TBombTimerWithExtra。
② 清理挂在既有事件 EEventName.GeneralLogicTreeRemove 上,按 TreeIncId 精确删(只删归属相符的,避免不同树的同名计时器互删),删到了就 Emit BombTimerChanged 让 UI 自己收起。
③ OnLeaveLevel 兜底:只保留任务树(跨关卡存活)的计时器,其余含归属缺失的一并清掉。
④ 顺手修了 UI 侧的残留:见「顺带发现 ①」。
总结:行为树派生出来的 UI,清理时机统一挂 GeneralLogicTreeRemove,一个监听点就够——它由 ApplyRemoveBehaviorTree 统一 Emit,天然覆盖退出野外事件(34504)、完成(34505)、事件结束(34507)、服务器删树(15004)、以及离开副本时批量删树五个场景,不用每条协议各接一次,也不需要服务器专门下发一条「关闭 UI」。
前提是这类数据落地时必须带上 treeIncId 归属;只按业务名(这里是 timerName)做 key 的缓存,树一销毁就成了没人认领的孤儿数据。
顺带发现:① 倒计时走完只停刷新没收面板。LevelUiTimerView.OnUpdate 里「正计时超目标值」「倒计时到负数」两处只调 ClearTimer() 就 return,定时器停了但面板还开着,数字冻结在屏幕上;而且 IsUiActive 停留 true,下次显示不播 Start 动画。抽 HideTimerUi()(停刷新 + 收面板 + 复位标志)统一调用。
② 重进事件不显示倒计时是另一条。复现路径不一样(退出后再次加入),按「一个现象一条」单独立条:重进野外事件不显示倒计时 UI。
复测说明:本地起不了复现环境,改动只过了自定义编译并 grep 产物核对 emit,等 QA 复测。
g. 【主线】显示倒计时时走到事件范围外退出事件,再次加入同一事件不显示倒计时
模块:行为树计时器(TimerAction)· 野外事件
问题描述详细版本:进 30100112 关卡 → #pre_start_wild_event 514000077 → 进事件范围秒杀怪,倒计时正常出来 → 走出事件范围直到服务器判定退出事件 → 再走回去重新加入,事件本身还在跑(倒计时也还在走),但屏幕上不出倒计时 UI。与退出 / 完成事件倒计时仍显示是同一模块的两个方向,复现路径不同,分开两条。
原因:客户端手里的计时器只有增量 15016 一个来源,而增量只在计时器发生变化时下发——重新加入一个已经在跑的事件,服务器不会再补一条增量。
恢复用的数据其实一直都发了:34501 WorldJoinWildEventSCNotify 带 wildEventBt(BehaviorTreeMsg),里面就有 timerList。但 WildEventController.CreateWildEventBT 只把它整个交给 GeneralLogicTreeModel.CreateBehaviorTree 建树,timerList 全程没人读(客户端只在进关卡时消费过 levelBt.timerList)。
Model 里没数据 → GetDisplayTimerName() 返回 undefined → UI 判定「没有要显示的计时器」。
解决方案:① BombTimerController 加公开方法 ApplyTreeTimerList(treeInfo):从全量里取 type / btTypeId / timerList,走已有的 SetList 带归属落地,再 Emit BombTimerChanged 让 UI 自己刷出来。
② WildEventController.CreateWildEventBT 里加一行调用。它是野外事件建树的唯一入口,所以一处改动同时覆盖「重新 join」(34501)和「进关卡 / 重连时重建进行中的事件」两条路径。
③ 全量与增量两条落地路径写入前,都先把该树在分帧队列里积压的增删同步排空。CreateBehaviorTree / RemoveBehaviorTree 都是入队、下一帧 Tick 才执行,退出与重进间隔不到一帧时,队列里那条待执行的删树会 Emit GeneralLogicTreeRemove,把刚落地的计时器又清掉(不管数据来自全量还是 15016)。ApplyCreateBehaviorTree 开头自带的 FlushByTree 救不了——Process() 期间 IsDraining 为 true,flush 直接 no-op。
总结:一个 msgId 在 Net 层只能注册一个 handler——NetConnection.Register 发现 NotifyHandles 里已有就直接打 Error 并 return false,不是叠加也不是覆盖,后注册的那个静默失效。所以想在别的模块里消费一条已被占用的协议(这里 34501 归 WildEventController),只能从原注册点调过来,不能自己再 Register 一次。做法是把逻辑留在自己模块里暴露成公开方法,调用点只占一行,模块边界不糊。
另一条更通用:增量协议只在「变化时」下发,凡是「重进 / 重连要恢复显示」的数据,都必须另有一个全量来源。写消费代码时顺手确认全量里那个同名字段有没有人读——这次的 timerList 在协议里躺了很久没人碰。
复测说明:根因尚未确认,不算已解决。服务器称重进时他有下发定时器通知:若下发的是 15016 增量,客户端本来就会落地,那问题在显示条件(showUi / status,或 currentValue 与 sendTimeStamp 的组合被 GetDisplayTime 判成 Stopped 过滤掉)而不是漏读全量,本条的修法就没打中根因。已向服务器要日志,过滤行为树计时器看有没有 SetInfo 落地记录即可分流。本地也起不了环境复测。
2.下周工作计划:
a. 跟进复活点配置修改,完成落点复测与视频留档。
b. 对齐 Params.Location(参数位置)的服务端语义,并提交日志、注释修正。
c. 联调野外事件倒计时,覆盖退出、完成、离开关卡和重进场景。
d. 确认任务行为树是否使用计时器全量数据,按结论补齐消费链路。
3.待讨论的问题/需要分享的内容:
a. 【待讨论】Params.Location(参数位置)由服务器读取还是由编辑器隐藏;被动位移期间是否屏蔽复活点记录。
b. 【待讨论】任务行为树是否消费 timerList(计时器列表)全量数据。
c. 【分享】坐标问题优先做来源数值比对;耗时问题要拆分代码耗时与玩家操作耗时。
条目格式(本站统一)
标题行 {序号}.【类型】任务{任务ID},{一句话现象},下面每个单元各占一行:
没有的单元可以不写,但有的单元不许改名、不许调序。
类型标签:【主线】
【衍生】【关卡工具】
【日常】。
没有任务号就整行不出——「无任务号」三个字是废话。
所有条目只展示模块与结论,不展示责任归属;需求 / 经验 / 复盘也不出重复的「状态」「标签」。
不写逐文件逐行号的改动清单,也不写已还原 / 已撤销的改动——前者 CL 里有, 后者等于没做;是教训就写进问题复盘,别在需求卡里充数。
Bug
模块 → 问题描述详细版本 → 原因 → 解决方案 → 总结
需求
模块 → 需求(什么需求)→ 实现细节 → 总结
所有条目只展示模块与结论,不展示责任归属;需求 / 经验 / 复盘也不出重复的「状态」「标签」。
不写逐文件逐行号的改动清单,也不写已还原 / 已撤销的改动——前者 CL 里有, 后者等于没做;是教训就写进问题复盘,别在需求卡里充数。
6. 下周待办
- 把坠落重生落点在门外(任务513000281)的问题结论与修法同步到飞书群,并跟进配置修改
- 拉服务器 + 策划对齐
Params.Location填了不生效的语义 (顺带发现 ①)——要么让它真生效,要么在 Scheme 层面藏掉,定下走哪条路 - 提交一个 CL:补
FallingRebornController.ts:511的覆盖成功日志 + 改EtBoundaryRespawnComponent.ts:585写反的注释 (顺带发现 ②③,改完跑 npm run compile) - 策划改完 513002412 的坐标后进游戏复测落点,录一段视频存到 weekly/img/ 同级
- 拉策划 + 服务器定被动位移期间要不要屏蔽复活点记录 (遗留风险)——本次就是玩家被管道带着穿过盒子记下来的, 挪坐标只治这一处;两条路选一条:管道 / 传送期间整体屏蔽触发,或明令复活点盒子不许摆在管道路径上
- 让策划在 Condition 节点实配一条「等待副本集合完成」跑通全流程 (需求),确认服务器判定生效——服务器答复「不用加」,需实测验证这个前提
- 等服务器删掉「离开副本下发关闭定时器」的逻辑后,跟服务器一起复测野外事件倒计时的两条路径: 退出 / 完成事件仍显示 与 重进事件不显示,确认客户端自主控制不依赖那条协议
- 跟策划确认任务行为树里会不会配计时器
(重进事件不显示的剩余风险)——
BT_TYPE_QUEST的全量timerList客户端仍然没人读,真有人配就会重现同一现象