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