理解 D · 选哪条通道
这一章抬升一层:第四部六章 + 闭环 3 + 对照小练 C 走完后,把通道选择从「七选一」抬升到「五个压力推出哪一条」。下一步进入第五部「玩法范式」。
通道选择不是「找最好那条」,是「让这一段同步背负的压力把通道推出来」。
第 21 章决策表是七条通道的横向对比。但读者真正要做的不是在七条之间比,而是把当前需求的五种压力(迟入 / 字节 / 频率 / 调试 / 扩展)想清楚,剩下的通道几乎自动浮现。
每一段同步同时承受这五种压力。每种压力的强度决定通道往哪一边偏。
压力 1:迟入
Section titled “压力 1:迟入”迟入玩家进来时这段状态需不需要看到。
- 必须看到:通道要支持状态恢复。事件类(
SendCustomNetworkEvent/ 参数化)出局,剩下五条同步字段类。 - 不需要看到:所有通道都行。瞬时反馈(飘字、音效、特效)通常落在事件类。
压力 2:字节
Section titled “压力 2:字节”单次同步的字节大小。
- 几个 byte:直接
[UdonSynced]简单类型,最省事。 - 几十到几百 byte:基础类型 + 数组 + JSON 都行。
- 几 KB:JSON 字符串是首选;如果还需要高频改动,拆字段或上字节打包。
- 几十 KB:必须拆字段或走 manual sync 多对象分摊;不能用单字段。
压力 3:频率
Section titled “压力 3:频率”每秒改几次。
- 每秒一次以下:什么都行。
- 每秒几次到几十次:避免 JSON 字段(CPU 解析代价),也避免把整对象当作一个大字段反复重发,倾向命令同步「最近一条」或字段分散。
- 每帧改动:物理类用
VRC Object Sync;非物理慎用,要算 11 KB/s 总预算。
压力 4:调试
Section titled “压力 4:调试”这一段出 bug 时调试有多难。
- 调试简单:状态字段直接读 Debug View 就能看;JSON 字段读字符串。这两类适合复杂业务。
- 调试中等:字段拆得多但每个字段名清晰。本卷闭环 1/2/3 都属此类。
- 调试困难:位掩码 / 字节打包 / 自定义二进制格式。要在性能瓶颈上才用。
压力 5:扩展
Section titled “压力 5:扩展”这段需求未来会怎么演化。
- 不会变:选代码最简单那条。
- 可能加字段:JSON 天然支持,拆字段要每加一个字段改一处同步定义。
- 可能扩状态规模(小棋盘到大棋盘、单人到 16 人):命令同步在状态规模扩大时字节代价低。
- 可能加历史 / 回放需求:命令日志(第 22 章)是顺路加的,状态同步不行。
五种压力推出五种倾向
Section titled “五种压力推出五种倾向”把五种压力的高 / 低组合落到推荐通道:
| 高压力组合 | 推荐通道 | 例子 |
|---|---|---|
| 迟入要看 + 字节小 + 频率低 | [UdonSynced] 基础类型 | 闭环 1/2/3 的 phase / totalScore |
| 迟入要看 + 字节中 + 频率中 | [UdonSynced] 数组 | 闭环 1/2/3 的 lastProcessedRequestId[] |
| 迟入要看 + 字节大 + 频率低 + 易扩展 | VRCJson 字段 | 复杂配置、棋盘 metadata、关卡数据 |
| 迟入要看 + 字节高 + 频率高 | 命令同步 + 快照 | 多人战场状态(位置走 VRC Object Sync,HP 走命令) |
| 不需迟入 + 字节小 + 频率任意 | 参数化网络事件 | 飘字、音效、按钮反馈 |
| per-player 状态 | VRCPlayerObject | 玩家个人分、Ready 状态、角色选择 |
| 物理 | VRC Object Sync | 抓取的物体、被丢的弹药 |
这张表不是替代第 21 章决策表,是从「压力」侧而非「通道」侧切入。两张表对照看。
闭环 3 的选择被什么推出来
Section titled “闭环 3 的选择被什么推出来”把闭环 3 的几个字段拿出来逐个回看,验证「压力推出通道」的工作方式。
phase:3 种取值(LOBBY/INGAME/RESULT)。迟入要看(必须)、字节极小(1 byte)、频率每局几次。压力组合 → [UdonSynced] 基础类型 → 选了 byte。
totalScore:迟入要看、字节小(4 byte)、频率每场几十次。组合 → [UdonSynced] 基础类型 → 选了 int。
isReady(在 PlayerLobbyState 上):迟入要看(必须)、per-player 属性、字节极小。组合 → VRCPlayerObject + [UdonSynced] bool。
stateVersion(闭环 3 新增):不与 UI 关联但需要稳定单调递增。压力是「让其他字段的 gating 工作」,不是常规 UI 同步。选了 [UdonSynced] int,OnPreSerialization 写入。
lastHandleScoreTime[](闭环 3 新增):per-player 冷却时间记忆。迟入不必看(玩家 A 离开后 D 加入不需要看到 A 上次什么时候得分)但 Owner 转移需要看(新 Owner 要继承谁还在冷却)。组合 → [UdonSynced] 数组。
每个字段的通道选择都能从压力推出来。这一过程在写新功能时也一样:先想清楚这一段同步背负的压力,通道几乎自动出现。
不要先选通道再去补缺口
Section titled “不要先选通道再去补缺口”第 19 章已经讨论过反向选法的代价。这里再总结一次。
反向选法 1:先决定「我要全部走命令同步省字节」,写到迟入恢复时发现状态不能恢复,硬补一份快照同步。结果是「命令 + 状态」两份都同步,没节省字节,反而多写一段同步代码。
反向选法 2:先决定「我要全部走 JSON 简单」,写到一段字段每秒改 30 次时发现 CPU 卡,硬拆字段。结果是 JSON + 拆字段两套都有。
反向选法 3:先决定「我要全部走参数化事件」,写到迟入玩家看不到任何东西时硬补一份持久化字段。结果同反向 1。
正向选法是:先想压力,再选通道。每一段同步都过一遍五种压力,根据组合自然落到一条通道上。每条通道都有它该承担的场景。
串起第三、四、五部
Section titled “串起第三、四、五部”第三部讲了游戏循环(大厅 / 开局 / 计分 / 旁观)。第四部讲了通道选择。这两部加起来给了「一局游戏怎么组织 + 状态怎么同步」的完整答案。
第五部接下来讲玩法范式(回合制 / 轻实时)。范式的差异不是「不同的同步技术」,是「同一套架构(PlayerObject + GameState + 命令版本号)在不同节奏感下的两种参数化」。回合制让 phase 变化频率低、字段稳定;轻实时让 phase 变化频率高、字段动态。每种范式选通道时仍然套五种压力。
第六部体感(预测、补偿、可接受的不一致)会在第四部决策表的基础上加一层「事件类通道是体感优化的载体,不是状态层」。读完第六部回过头来看第四部,会有「事件类的 16 KB 上限到底为什么够用」的体会:因为体感优化只用瞬时反馈,瞬时反馈只用几十 byte 就够。
闭环 3 之后玩法层面够用,第八部主项目(合作防守世界)会把第三 + 第四 + 第五 + 第六部一次性串起来。本理解章是这一串起来的入口。
挑一条试。
- 拿一段你目前正在做的同步代码(不一定是闭环 3 的),按五种压力打分(每种 1–3 分),看推出来的通道和你实际选的是不是一致。不一致的话原因是什么?
- 闭环 3 的
lastHandleScoreTime[]改成「不同步、各客户端自己管」会怎样?这一字段背负的压力清单变不变?决策结果变不变? - 第 22 章的命令日志加到闭环 3 上时,命令日志背负的压力清单是什么?为什么本闭环没接它?
- 能默写五种压力(迟入 / 字节 / 频率 / 调试 / 扩展)
- 能用五种压力推出闭环 3 各字段的通道选择
- 能识别三种「先选通道再补缺口」的反向选法
- 能解释为什么没有「最佳通道」
- 能说出第五部范式与第四部通道的关系
- 第 19 章 · 状态同步还是命令同步 — 五维度判断的来源。
- 第 21 章 · 参数化 Network Event 与决策表 — 七通道决策表。
- 闭环 3 · 加上命令版本号的工程化一局 — 本理解章逐字段回看的母本。
- 对照小练 C · 同一玩法两种通道 — 通道在同一玩法上的并排实验。
- 附录 · 术语表 —
Channel Decision Table的客观定义。