理解 E · 同步策略不是一种,是一族
这一章抬升一层:第 25 章和第 26 章给的字段几乎不重叠,让人感觉是两套不同技术。本章把两章并排看,发现底下的是同一套架构在两组参数下的两种实例化。下一步进入第六部「体感」。
回合制和轻实时是同一套架构(PlayerObject + GameState + 命令版本号)的两组参数化。差异只在「tick 由什么驱动」「字段怎么命名」「同步频率多高」,不在「有没有 Owner」「有没有版本号」「有没有迟入恢复」。
第 25、26 章把两种范式分开写是为了让读者看清各自的字段和 Update 逻辑。但本质上这两章描述的是同一架构骨架(闭环 3)在不同 tick 节奏下的两次复用。
第 25、26 章共享的部分占了大头。
共享的字段:phase、stateVersion、lastProcessedRequestId[]、ownerCandidateOrder[] + ownerCandidateCount、spectatorPlayerIds[]、teamScore[] / totalScore。这些字段不依赖范式,回合制和轻实时都用同一套定义,只是写入时机不同。
共享的回调:OnPreSerialization / OnDeserialization gating、OnPlayerJoined / OnPlayerLeft / OnOwnershipTransferred、OnPlayerLeftCleanup 扩展点、IssueRequest / OnPlayerRequestUpdated / ProcessRequest。这些回调由 SDK / 闭环 3 定义,与玩法范式无关。
共享的设计原则:写权清晰(每个字段属于 GameState Owner / 玩家本人 / 客户端本地三者之一);迟入恢复(所有「迟入需要看到」的状态走同步字段,事件不重放);请求式架构(玩家发请求、Owner 裁决、字段落地);命令版本号(远端客户端通过 stateVersion gating 不应用中间态)。
「不变」的工程量在闭环 1 / 2 / 3 已经累积。第 25、26 章只在它上面加自己的字段,不重新发明这一架构。
落在两个地方。
变化点 1:tick 驱动
Section titled “变化点 1:tick 驱动”| 范式 | tick 由什么触发 | 谁推动 |
|---|---|---|
| 回合制(第 25 章) | 玩家提交 / 行动超时 / 特殊事件 | 玩家行动驱动,Update 只检测超时 |
| 轻实时(第 26 章) | 时间间隔(1Hz / 5Hz / 10Hz) | Update 主动推进 |
代码层面只是 Update 函数里的判断条件不同:
// 回合制if (elapsed >= turnDeadlineSeconds) AdvanceTurn();
// 轻实时if (now - lastTickTime >= tickInterval) { lastTickTime = now; TickGameState();}剩下的代码(HandleXxx / OnPlayerLeft / OnDeserialization)共享 90%。
变化点 2:核心字段命名
Section titled “变化点 2:核心字段命名”回合制和轻实时各自加几个字段,名字不一样但形态相似。
| 概念 | 回合制字段 | 轻实时字段 |
|---|---|---|
| 「现在到了第几个单位」 | turnNumber | waveIndex |
| 「这个单位从哪一秒开始」 | turnStartServerTime | waveStartServerTime |
| 「这个单位预计持续多久」 | turnDeadlineSeconds | waveDuration |
| 「这个单位的当前主体」 | currentTurnPlayerId | enemyAliveCount 等聚合状态 |
字段名换一下,对应的逻辑共享。这一对应让两章不是「两套独立设计」,是「同一形态的两份具体填写」。
节奏感落到字段写权
Section titled “节奏感落到字段写权”第三部 17 / 18 章给了分数三层架构和旁观席。第四部决策表给了通道选择五维度。第五部 25 / 26 章在这些工具上落两种节奏感。
回合制玩家行动稀疏(每位玩家每分钟行动 1–2 次),字段写权集中在「玩家提交时」和「Owner 裁决时」两个时刻。Update 只在 Owner 上跑超时检测,频率低。命令日志几乎必接(走子记录是回合制的核心信息)。
轻实时玩家行动密集(每秒可能多次),字段写权分散到 Update tick + 玩家请求 + 事件回调。Update 在 Owner 上持续跑,需要节流避免预算超标。命令日志可选(不是所有轻实时玩法都需要战报历史)。
两种节奏感对同一架构的不同压力对应的工程取舍也不同:回合制对单条命令的可靠性要求高(一手牌不能丢),靠请求式架构 + requestId 幂等。轻实时对整体节奏的连续性要求高(每秒进度不能跳),靠 Update tick + 字段节流。
但两者都不要求同步频率高。VRChat 的 11 KB/s 总预算约束让任何范式都不能把同步当成「实时广播」。两种范式都靠事件做反馈、状态做共识这一通用模式(第 26 章一节)。
串第三、四、五部
Section titled “串第三、四、五部”第三部是流程:大厅、开局、计分、旁观。定义游戏循环的骨架。读到第三部末,闭环 2 + Lobby/Ready/Team/Score/Result 一整套已经能跑。
第四部是通道:状态 vs 命令、版本号、参数化事件、命令模式、字节打包、JSON。它把字段「装进 VRChat 给的同步通道」这一步铺开,并以闭环 3 修复闭环 2 的三个缺陷。
第五部是范式:回合制和轻实时把第三、四部的工具组装成两种节奏感的玩法骨架。每一种范式都把第三部的流程和第四部的通道按节奏感重新参数化。第 27、28 章再把分数和旁观从第 17、18 章扩到「助攻 / 时间奖励 / 跨阶段重入 / 阶段间策略」。这些扩展不创造新工具,沿用第 17、18、19、22 章已经定义的模式。
读完第五部回看第三、四部:第三部是「房间生命周期的标准动作」,第四部是「字段同步的标准通道」,第五部是「这两种工具组装出的两种典型形态」。三部连起来构成了 VRChat Game World 的核心架构图。
剩下两章在哪里
Section titled “剩下两章在哪里”第六部「体感」展开预测、补偿、Lag 补偿。它在第四部决策表上加一层:事件类通道是体感优化的载体,瞬时反馈通常不超过几十 byte。读完第六部回看第四部,会有「为什么参数化网络事件 16 KB 上限对体感够用」的体会。
第七部「高风险系统」展开 NPC、对象池、VRC Object Sync。它在第五部范式上加一类对象:轻实时玩法的对象池(第 36 章)就是第 26 章「对象池接口边界」的实装。回合制玩法通常不需要对象池(手牌本身是数据,不是物理对象)。
第八部「完整项目」把第三 + 第四 + 第五 + 第六 + 第七部一次性串起来,做一个 4 人合作防守世界。它不再铺新概念,是把已有工具按主项目的具体需求落地。
挑一条试。
- 把第 25 章回合制骨架的字段名按第 26 章规则改一遍(
turnNumber → eventIndex/turnStartServerTime → eventStartServerTime/turnDeadlineSeconds → eventDeadline)。改完后两个范式的代码差异主要在哪几行?这一改名练习有助于看清「同一形态」吗? - 设计第三个范式:「混合制」。游戏前期是回合制(玩家轮流出牌),到某个阈值后切换为轻实时(玩家可同时行动)。需要哪些新字段?
Update函数怎么写?哪些字段在切换时要重置? - 在闭环 3 / 第 25 / 26 / 27 / 28 章的所有同步字段里挑 5 个,按第 19 章五维度(迟入 / 频率 / 字节 / 调试 / 历史)打分。打分一致的字段成簇了吗?这种聚类有助于理解架构吗?
- 能列出回合制和轻实时共享的字段、回调、设计原则
- 能用一句话说清楚「同一架构在两组参数下的两次实例化」的具体含义
- 能识别两种范式的两个变化点(tick 驱动方式 + 字段命名)
- 能解释「事件做反馈、状态做共识」在两种范式里的共同应用
- 能说出第五部和第三、四部的关系:组装而非新建
- 闭环 3 · 加上命令版本号的工程化一局 — 两种范式的共同骨架。
- 第 19 章 · 状态同步还是命令同步 — 两种范式都用的五维度判断。
- 第 25 章 · 回合制范式 — 事件驱动 tick。
- 第 26 章 · 轻实时范式 — 时间驱动 tick。
- 第 27 章 · 计分、奖励与结算进阶 — 三层分数架构的扩展。
- 第 28 章 · 观战、旁观、掉线后重入进阶 — 旁观席的扩展。
- 创作者视角 5 · 节奏感是设计出来的 — 节奏感差异的设计含义。
- 附录 · 术语表 — 本理解章涉及的所有术语。