TeleportController 重构 · 第一次:结构拆分
本文记录 2026-09-07 的第一次重构:把 TeleportController 从 3526 行收敛到 467 行,保留稳定门面与外部入口;表现、地面探测、同场景流程、过场序列、Seq3 绑定和转场编排分别进入独立对象。
2026-09-07
3526 → 467 行
6 个子职责
PIE 功能回归待测
本文只记录第一次重构
第二次重构(2026-10-12 · 流程命令化与 V2 拆分)把传送表现改为分阶段命令驱动,并把 V2 单体拆为流程 / 组装器 / 命令 / 协作类,见 TeleportController 重构 · 第二次:流程命令化与 V2 拆分。两页相互独立,可分开阅读。
1. 第一次重构结果(2026-09-07)
| 文件 | 行数 | 重构后定位 |
|---|---|---|
| TeleportController.ts | 467 | 稳定门面、网络入口、相机事件处理与 Tick(帧更新)转发 |
| TeleportPresentation.ts | 379 | 玩家显隐、输入与主界面控制、相机复位、淡入淡出、黑屏与无缝遮罩 |
| TeleportGroundProbe.ts | 153 | 3×3 地面可用性探测、贴地位置计算、重试与超时放行 |
| TeleportSameSceneFlow.ts | 521 | 同场景通知接管、移动锁、流送命令、LoadDone 上报重试、协议缓存收尾 |
| TeleportSequenceFlow.ts | 654 | 过场关卡与 Seq1/Seq2/Seq3 资源、角色和相机绑定、播放与卸载 |
| TeleportSeq3Binder.ts | 666 | Seq3 真实角色绑定、换装等待、移动模拟冻结、逐帧纠正、尾段融合与还原 |
| TeleportTransitionFlow.ts | 601 | 传送类型分流、上升/下降技能、跨场景与淡入淡出编排、入场等待和统一结束 |
本次拆分新增 2974 行职责明确的协作对象;控制器减少 3059 行。行数变化来自代码迁移与边界显式化,不代表删减传送功能。
2. TeleportController 保留的边界
TeleportController 继续作为外部系统可见的控制器,不把调用方迁移成本扩散到全工程。它只保留以下四类工作:
| 保留项 | 具体内容 | 为什么留在门面 |
|---|---|---|
| 门面接口 | OnStartUp、OnStartLoad、OnStartDown、OnEnd、显隐与淡入淡出等既有公开方法 | 外部调用路径和控制器注册方式保持不变 |
| 网络入口 | WorldTeleportEnterSCNotify 与 WorldSyncDirPosSCNotify 的注册、注销和首层转发 | 协议生命周期仍跟随控制器 |
| 相机事件 | TeleportDownCameraTransition 的曲线组装、剩余时长限制和相机复位调用 | 该事件同时连接控制器公开能力与 Seq3 状态 |
Tick(帧更新)转发 | OnPostUpdateWorkTick 只调用 SequenceFlow.Tick() | 控制器继续接入框架帧更新,具体逻辑下沉 |
3. 子职责划分
3.1 表现与地面探测
TeleportPresentation 统一管理可见性、输入、主界面、相机、黑屏、无缝遮罩和材质淡入淡出;TeleportGroundProbe 只依赖物理查询与计时器,负责“地面是否真的可站”和“角色应贴到哪个高度”。表现状态与物理判定不再混入流程编排。
3.2 三条流程与一个绑定器
| 对象 | 持有的状态 | 完成时输出 |
|---|---|---|
| TeleportSameSceneFlow | 同场景流送、相机朝向等待、移动锁、LoadDone 重试、缓存原因与流程代次 | 发出流送完成事件,清理遮罩与缓存 |
| TeleportTransitionFlow | 吟唱/上升/下降等待、入场双层超时、流送完成监听、淡入淡出代次 | 统一结束传送并发出开始/结束事件 |
| TeleportSequenceFlow | 过场关卡、序列播放器、替身角色、序列相机和待应用材质 | 完成过场加载、Seq3 播放与资源卸载 |
| TeleportSeq3Binder | 真实角色绑定快照、换装与重绑计时器、落点锚点、尾段相机支点 | 还原角色、碰撞、移动同步、移动模拟和流送锁 |
4. 依赖方向
外部调用 / 网络通知 / 相机事件
↓
TeleportController
├─ TeleportPresentation
├─ TeleportGroundProbe
├─ TeleportSameSceneFlow ──→ Presentation + GroundProbe
├─ TeleportSequenceFlow ──→ GroundProbe + TeleportSeq3Binder
└─ TeleportTransitionFlow ─→ Presentation + SameSceneFlow + SequenceFlow
依赖由门面向子对象单向建立。子流程不反向持有 TeleportController,需要的少量跨边界能力通过构造参数中的回调接口注入。TeleportGroundProbe 与 TeleportPresentation 是底层能力;TeleportTransitionFlow 位于编排层。
5. 回调治理
| 治理点 | 落地方式 | 解决的问题 |
|---|---|---|
| 显式依赖 | 三个流程类分别声明只读回调接口,构造时由门面注入 | 子对象只看到所需能力,避免获取整个控制器并形成反向依赖 |
| 稳定函数引用 | 事件与协议处理继续使用只读箭头函数属性 | 注册与注销使用同一引用,兼容事件系统的弱引用行为 |
| 一次完成 | 序列换装、轨道重绑等异步路径使用完成标志,保证回调只触发一次 | 正常完成、超时和对象失效同时到达时不重复收尾 |
| 成对取消 | 计时器句柄、事件注册状态和异步解析函数都有独立清理方法 | 新流程开始、OnEnd 或 Clear 时可撤销旧等待 |
| 流程代次 | FlowGeneration 与 FadeGeneration 标识当前流程 | 旧异步回调晚到时会被丢弃,不再污染新一轮传送 |
| 统一恢复 | 过场失败、Seq3 结束、控制器清理分别进入明确的恢复出口 | 输入、界面、遮罩、移动模拟、碰撞和流送锁可完整归还 |
6. 迁移阶段
| 阶段 | 迁移内容 | 边界结果 |
|---|---|---|
| 阶段 1 | 抽出 TeleportPresentation 与 TeleportGroundProbe | 先隔离表现副作用和物理探测,减少控制器中的工具型方法 |
| 阶段 2 | 抽出 TeleportSameSceneFlow | 同场景通知、流送、移动锁、LoadDone 和协议缓存形成闭环 |
| 阶段 3 | 抽出 TeleportSequenceFlow | 过场关卡与三段序列的资源生命周期独立 |
| 阶段 4 | 从序列流程中再拆出 TeleportSeq3Binder | 真实角色绑定、落点纠正和还原不再挤占序列装载代码 |
| 阶段 5 | 抽出 TeleportTransitionFlow,控制器改为转发 | 传送类型和表现路径的编排集中到单一流程对象,门面稳定 |
| 阶段 6 | 统一清理入口、代次校验和构造回调 | 各子对象可独立结束自己的事件、计时器与临时状态 |
7. 保留的不变量
- 外部入口不变:调用方仍通过 ControllerHolder.TeleportController 使用原有公开方法。
- 网络入口不变:进入通知和相机朝向通知仍由控制器注册并在原生命周期注销。
- 协议语义不变:同场景 LoadDone 继续携带服务器移动版本,并按 5/5/10/20/40 秒退避重试。
- 表现分流不变:无缝、飞天遁地、淡入淡出与系统 Loading(加载界面)替代路径继续保留。
- 地面安全不变:流送完成后仍执行 3×3 射线检查。(2026-09-21 更新:原来的「约 1 秒未确认就超时放行」已移除,改为持续轮询到就绪;详见 同图安全等待)
- 遮罩所有权不变:无缝遮罩和黑屏仍只关闭本流程持有的那一份,并保留超时告警(只打日志、不撤罩)。
- Seq3 收尾不变:角色、碰撞、移动同步、移动模拟、相机支点与流送锁必须成组恢复。
- 日志口径不变:传送链路日志继续带“传送”前缀,便于按同一关键词复测。
8. 验证结果
| 检查项 | 结果 | 覆盖范围 |
|---|---|---|
| Prettier(代码格式化) | 通过 | 本次 7 个 TypeScript(类型脚本)文件 |
| ESLint(静态检查) | 0 error | 本次改动文件 |
npm run tsc-native | 通过 | 原生 TypeScript(类型脚本)编译链 |
| JavaScript(脚本)产物 | 7 个已更新 | 控制器与 6 个新增职责文件均已生成对应产物 |
| PIE(编辑器内运行)功能回归 | 待测 | 同关卡、跨关卡、传送点、复活、淡入淡出、Seq3 与异常兜底路径 |
静态检查与编译产物验证已经完成;当前结论只证明代码可格式化、可检查、可编译且产物已刷新,不能替代 PIE(编辑器内运行)中的功能回归。功能行为仍需按现网入口逐项验证。