您似乎已经开始提出一些关于同步的好主意,但是您遇到的两个问题可能会重叠:游戏时钟的同步和游戏状态的同步。
(1) 同步游戏时钟,您需要为您的游戏提供一些“游戏时间”的表示。对于一个 2 人游戏,简单地声明一个人是非常合理的。
等等权威客户端:
OnUpdate()
gameTime = GetClockTime();
msg.gameTime = gameTime
SendGameTimeMessage(msg);
在另一个客户端上可能是这样的:
OnReceivGameTimeeMessage(msg)
lastGameTimeFromNetwork = msg.gameTime;
lastClockTimeOfGameTimeMessage = GetClockTime();
OnUpdate()
gameTime = lastGameTimeFromNetwork + GetClockTime() - lastClockTimeOfGameTimeMessage;
有一些复杂的问题,比如跳过/滑动(即从网络上获取时间,向前/向后太多)需要进一步的工作,但希望你能明白这一点。如果需要,请跟进另一个问题。
注意:此示例不区分“滴答声”与“秒”,也不与您的网络协议或运行游戏的设备类型相关(保存“设备具有本地时钟”的要求)。
(2) 同步游戏状态 在你有一个一致的游戏时钟之后,你仍然需要弄清楚如何一致地模拟和传播你的游戏状态。对于同步游戏状态,您有几个选择:
异步
- 每个游戏状态单元都由一个进程“拥有”。只有那个过程被允许改变那个游戏状态。这些更改会传播到所有其他进程。
- 如果一切都归一个进程所有,这通常被称为“客户端/服务器”游戏。
- 请注意,使用此模型,每个客户端在任何时候都有不同的游戏世界视图。
- 示例游戏:地震、魔兽世界
为了优化带宽和隐藏延迟,您通常可以对更新频率较高的字段进行一些本地模拟。例子:
drawPosition = lastSyncPostion + (currentTime - lastSyncTime) * lastSyncVelocity
当然,在这种情况下,您必须将新信息与您的模拟版本进行协调。
同步
- 每个游戏状态单元在所有过程中都是相同的。
- 来自每个进程的命令以其所需的启动时间(将来的某个时间)相互传播。
- 在最简单的形式中,一个进程(通常称为主机)发送特殊消息,指示何时提前游戏时间。当每个人都收到该消息时,他们被允许模拟游戏直到那时。
- “未来”的要求导致输入命令和游戏状态变化之间的高延迟。
- 在像文明这样的非实时游戏中,这很好。在星际争霸这样的游戏中,通常会立即确认输入的声音,但实际影响游戏状态的动作会延迟。这种风格不适合需要时间敏感动作(约 100 毫秒规模)的射击游戏。
与重新模拟同步
- 每个游戏状态单元在所有过程中都是相同的。
- 每个进程向所有其他进程发送其输入及其当前时间戳。另外会定期发送“什么都没发生”消息。
- 每个进程都有 2 个游戏状态副本。
- 游戏状态的副本 1 被传播到它从所有其他客户端收到的“最后一条最早的消息”。这相当于同步模型,但有一个弱点,它代表了“一点点以前”的游戏状态
- 游戏状态的副本 2 是副本 1 加上所有剩余的消息。它是对客户端当前时间的游戏状态的预测,假设没有新的事情发生。
- 玩家与两种游戏状态的某种组合进行交互(理想情况下是 100% 副本 2,但必须考虑避免在新消息出现时弹出)
- 示例游戏:街头霸王 4(网络游戏)
根据您的描述,选项(1)和(3)似乎适合您的问题。如果您还有其他问题或需要更多详细信息,请再次询问。