在线联机原型全集:第 6 章 Pong 对战(Realtime 60 FPS 原型)
类别:1v1 实时对战动作原型 目标:验证 60Hz Tick 同步、客户端预测、服务器纠正、插值/外推、丢包重传与观战延迟管线。 原型代号:
proto-006-pong依赖模块:proto-001-echo-chatroom,proto-005-pictionary推荐语言栈:Go / Rust / C++ / TypeScript 网络协议:UDP (QUIC) / WebSocket (Binary Frame)
一、概述(Overview)
“Pong” 是游戏史上的起点,也是实时对战架构的 Hello World。
两名玩家各自控制一个挡板, 一颗小球在屏幕中弹来弹去, 任何一方未能接到球则对方得分。
这个极简机制背后,隐藏了所有实时同步游戏的关键挑战:
- 输入延迟与预测;
- Tick 驱动的状态更新;
- 客户端插值与服务器权威状态;
- 丢包重传、抖动缓冲;
- 观战延迟同步;
- 状态回放与重演一致性。
一旦你能在 60 FPS 下稳定运行一场跨国 Pong 对战, 你就掌握了所有实时网络游戏的核心原理。
二、核心玩法与系统目标(Gameplay & Objectives)
2.1 基础玩法
- 两名玩家(Player A 与 Player B);
- 各自控制左右两侧挡板(上下移动);
- 小球在场中弹跳;
- 若一方未接到球,对方得分;
- 比赛分若干回合(例如 11 分制)。
2.2 系统目标
| 模块 | 功能目标 | 验证重点 |
|---|---|---|
| Tick 驱动引擎 | 60Hz 固定帧率更新 | 一致性时间步 |
| 客户端预测 | 输入延迟隐藏 | Input Prediction |
| 服务器权威 | 状态校准与纠正 | Server Reconciliation |
| 插值与外推 | 平滑显示 | Network Interpolation |
| 丢包恢复 | 重传或跳过 | Reliable vs Unreliable Channel |
| 延迟观战 | 延迟数 Tick 播放 | Replay Buffer Pipeline |
| 状态快照 | 可重放 | Deterministic Simulation |
三、系统架构设计(Architecture)
graph TD
A1[Client A] -->|Input Frame| S[Server]
A2[Client B] -->|Input Frame| S
S -->|State Snapshot| A1
S -->|State Snapshot| A2
S --> L[Replay Logger]
S --> W[Watcher (Spectator)]
模块说明
| 模块 | 职责 |
|---|---|
| Server | 状态权威、Tick 调度、碰撞检测、回放记录 |
| Client | 输入预测、插值渲染、输入上报 |
| Replay Logger | 记录 Tick 状态 |
| Spectator | 延迟播放(延迟 N Tick) |
| Network Layer | 序列化 / 压缩 / 丢包恢复 |
四、Tick 同步与帧循环(Tick System)
4.1 Tick 模型
服务器以固定频率(60Hz)运行逻辑循环:
[ Tick = \frac{1}{FPS} = \frac{1}{60} \approx 16.67ms ]
每 Tick 执行:
- 收集所有玩家输入;
- 更新游戏状态;
- 发送状态快照(Snapshot)给客户端;
- 记录日志。
func GameLoop() {
for {
tickStart := time.Now()
ProcessInputs()
UpdatePhysics()
BroadcastSnapshot()
SleepUntilNextTick(tickStart, 16*time.Millisecond)
}
}
4.2 客户端预测(Client Prediction)
客户端不等待服务器确认,而是立即执行输入预测:
用户按下“上” → 客户端立即移动挡板 → 同时上报输入。
当服务器状态返回后,客户端进行“回滚与重演”:
predicted_state = apply(local_inputs)
authoritative_state = server_state
if predicted_state ≠ authoritative_state:
rollback to server_state
reapply unconfirmed inputs
4.3 插值与外推(Interpolation & Extrapolation)
-
插值(Interpolation):平滑过渡历史状态
- 当前渲染延迟 100ms,渲染的是 Tick-6 状态;
- 连续两帧插值让运动平滑。
-
外推(Extrapolation):短期预测未来状态
- 当丢包时,根据速度和方向预测物体位置。
五、服务器权威逻辑(Server-Authoritative Core)
5.1 状态结构
type GameState struct {
Tick int
Ball Vector2
PaddleA Vector2
PaddleB Vector2
Velocity Vector2
ScoreA int
ScoreB int
}
5.2 权威更新逻辑
func UpdatePhysics() {
Ball.X += Velocity.X * DeltaTime
Ball.Y += Velocity.Y * DeltaTime
if Collide(PaddleA) || Collide(PaddleB) {
Velocity.X *= -1
}
}
5.3 快照广播
{
"tick": 183,
"ball": [250.3, 141.2],
"paddleA": [10, 120],
"paddleB": [490, 128],
"vel": [3.2, -2.8]
}
六、网络同步机制(Networking Model)
| 通信方向 | 内容 | 协议 | 特点 |
|---|---|---|---|
| Client → Server | 输入帧(InputFrame) | UDP / QUIC | 不可靠、带序号 |
| Server → Client | 状态快照(Snapshot) | UDP / WS Binary | 小包高频 |
| Client ↔ Server | 心跳包(Ping/Pong) | WS Text | 维持连接 |
丢包检测
- 包序号差距 > 1 → 丢包;
- 客户端可请求补发关键帧(Key Snapshot)。
七、观战延迟管线(Spectator Delay Pipeline)
原理
观战者不能与实时玩家看到相同 Tick, 否则可能暴露对战信息(作弊)。
解决方案:
- 延迟 N Tick(例如 300ms = 18 Tick);
- 使用 Replay Buffer 实现延迟播放。
graph LR
S[Server] -->|Tick Data| B[Replay Buffer]
B -->|N Tick Delay| W[Spectator Clients]
八、回放系统(Replay System)
数据格式
{
"tick": 233,
"inputs": {
"A": "Up",
"B": "None"
},
"state": {
"ball": [230, 110],
"vel": [3, -2]
}
}
功能
- 按 Tick 顺序记录;
- 支持 deterministic 重演;
- 用于 debug、反作弊与回放回顾。
九、延迟、抖动与丢包补偿(Latency & Jitter Compensation)
| 问题 | 策略 | 说明 |
|---|---|---|
| 延迟(Latency) | 客户端预测 | 隐藏控制滞后 |
| 抖动(Jitter) | 抖动缓冲区(Jitter Buffer) | 缓冲 2–3 帧 |
| 丢包(Loss) | KeyFrame 重传 | 每 10 Tick 发关键帧 |
| 不一致(Desync) | 状态回滚 | 重演逻辑修正 |
十、性能与验证指标(Metrics & KPI)
| 指标 | 目标 | 说明 |
|---|---|---|
| Tick 同步误差 | ≤1ms | 服务器对齐 |
| 平均延迟 | ≤80ms | 可接受游戏体验 |
| 丢包率耐受 | ≤5% | 有效纠错 |
| 插值平滑度 | ≥95% | 无跳帧 |
| FPS 稳定性 | 60 ± 1 | 稳定逻辑帧率 |
| 观战延迟 | 300ms 固定 | 公平性保证 |
十一、技术选型(Tech Stack)
| 层 | 技术 | 理由 |
|---|---|---|
| 网络层 | QUIC / WebSocket Binary | 支持可靠与不可靠混合 |
| 引擎层 | Go + ECS 架构 | 高性能逻辑更新 |
| 时间同步 | NTP + Ping Offset | Tick 对齐 |
| 存储层 | Redis Stream + PostgreSQL | 回放存档 |
| 客户端 | Pixi.js / Phaser / Unity WebGL | 60 FPS 渲染 |
| 压测工具 | Locust + k6 | 模拟输入流量 |
十二、扩展功能(Extensions)
-
Rank Elo 匹配系统
- 参考第 2 章 RPS;
- 基于胜负更新积分。
-
延迟自适应 Tick
- 低延迟玩家 Tick=60Hz;
- 高延迟客户端动态插值 Tick。
-
AI 代打调试模式
- AI 模拟输入行为;
- 用于测试同步延迟效果。
-
帧压缩与差分广播
- 每 10 帧仅广播差异字段;
- 节省 70% 带宽。
-
自动录像导出
- 以帧为单位导出视频;
- 用于比赛分析与作弊检测。
十三、压测与运维(Load & Ops)
| 测试场景 | 模拟行为 | 目标 |
|---|---|---|
| 10 万 Tick 持续运行 | 负载稳定 | CPU < 80% |
| 高丢包网络 | 10% 丢包率 | 游戏仍可恢复 |
| 跨区域延迟 | 200ms RTT | 延迟补偿成功 |
| 高频广播 | 每秒 120 帧 | 无丢包 |
| 观战同步 | 100 观众延迟播放 | 延迟差 < 50ms |
监控指标:
tick_rate_actuallatency_avg_mspacket_loss_raterollback_totalsnapshot_broadcast_total
十四、设计反思与演化(Reflection & Evolution)
14.1 关键洞察
“Pong 对战”是实时游戏系统的真正考验。 它揭示了**“一致性 vs 延迟”**的永恒矛盾。 —— 越一致,越慢;越实时,越不同步。
因此所有实时游戏都在做一件事:
“制造延迟的幻觉,让玩家相信这就是实时。”
14.2 架构哲学
- 客户端预测是玩家体验的基石;
- 服务器权威是公平性的核心;
- 插值算法是视觉真实性的关键;
- Tick 驱动是物理世界的节奏。
14.3 下一阶段演化
| 能力 | 下一原型 |
|---|---|
| AOI + 区域广播 | → #7 贪吃蛇大作战 |
| 多人同步(>10 人) | → #8 战舰/UNO |
| 状态分片(Shard) | → #10 城格建造 mini-SLG |
| 回滚重演 | → #16 实时战斗核心框架 |
十五、总结
Pong 是网络同步的真理之镜。 在短短 16.67ms 的时间里,你必须完成:
- 输入收集;
- 状态更新;
- 校准;
- 广播;
- 渲染;
- 审计;
- 存档。
任何一环延迟或丢失,整个世界就“不同步”。
因此,Pong 对战 不是一个游戏,而是实时系统架构的微缩宇宙。 你可以把它当作「物理 + 网络 + 时间」的交响乐。
“当两块挡板、一个球和 60 Tick 同步地在全球弹跳时—— 你就理解了分布式的灵魂。”