客户端-服务器游戏算法
Posted
技术标签:
【中文标题】客户端-服务器游戏算法【英文标题】:Algorithm for Client-Server Games 【发布时间】:2009-05-01 15:38:43 【问题描述】:对于单机游戏,基本的游戏循环是(来源:***)
while( user doesn't exit )
check for user input
run AI
move enemies
resolve collisions
draw graphics
play sounds
end while
但是,如果我开发类似客户端服务器的游戏,例如 Quake、Ragnarock、Trackmania 等,会怎样?
游戏的客户端和服务器部分的循环/算法是什么?
【问题讨论】:
【参考方案1】:应该是这样的
客户:
while( user does not exit )
check for user input
send commands to the server
receive updates about the game from the server
draw graphics
play sounds
end
服务器:
while( true )
check for client commands
run AI
move all entities
resolve collisions
send updates about the game to the clients
end
【讨论】:
【参考方案2】:客户:
connect to server
while( user does not exit && connection live)
check for user input
send commands to the server
estimate outcome and update world data with 'best guess'
draw graphics
play sounds
receive updates about the game from the server
correct any errors in world data
draw graphics
play sounds
end
服务器:
while( true )
check for and handle new player connections
check for client commands
sanity check client commands
run AI
move all entities
resolve collisions
sanity check world data
send updates about the game to the clients
handle client disconnects
end
对客户端命令和世界数据的健全性检查是为了消除任何由故意作弊(移动过快,穿过墙壁等)或延迟(穿过客户端认为打开的门,但服务器知道已关闭,等等)。
为了处理客户端和服务器之间的延迟,客户端必须对接下来会发生什么做出最好的猜测(使用它的当前世界数据和客户端命令) - 然后客户端必须处理它之间的任何差异预测会发生以及服务器后来告诉它实际发生了什么。通常这将足够接近以至于玩家不会注意到差异 - 但如果延迟很明显,或者客户端和服务器不同步(例如由于作弊),那么客户端将需要在以下情况下进行突然更正它从服务器接收数据。
还有很多关于将这些进程的部分拆分为单独的线程以优化响应时间的问题。
最好的开始方法之一是从一个拥有活跃改装社区的游戏中获取一个 SDK - 深入研究它的工作原理将提供一个很好的概览,了解它应该如何完成。
【讨论】:
【参考方案3】:这真的不是一个简单的问题。在最基本的层面上,您可以说网络提供的数据与原始循环的 MoveEnemies 部分提供的数据相同。因此,您可以简单地将循环替换为:
while( user doesn't exit )
check for user input
run AI
send location to server
get locations from server
resolve collisions
draw graphics
play sounds
end while
但是,您需要考虑延迟,因此您真的不想通过调用网络来暂停主循环。为了克服这个问题,经常会看到网络引擎位于第二个线程上,尽可能快地从服务器轮询数据并将对象的新位置放入共享内存空间中:
while(connectedToNetwork)
Read player location
Post player location to server
Read enemy locations from server
Post enemy locations into shared memory
那么你的主循环应该是这样的:
while( user doesn't exit )
check for user input
run AI
read/write shared memory
resolve collisions
draw graphics
play sounds
end while
这种方法的优点是您的游戏循环将尽可能快地运行,但来自服务器的信息只有在完成与服务器之间的完整发布后才会更新。当然,您现在遇到了跨线程共享对象以及随之而来的锁等乐趣的问题。
在服务器端,循环大致相同,每个播放器都有一个连接(通常每个播放器也在一个单独的线程上,因此其中一个的延迟不会影响其他线程)它将运行的每个连接像这样的循环
while (PlayerConnected)
Wait for player to post location
Place new location in shared memory
当客户端机器请求敌人的位置时,服务器会从共享内存块中读取所有其他玩家的位置并将其发回。
这是一个非常简化的概述,还有更多可以提高性能的调整(例如,服务器将敌人的位置发送给客户端而不是客户端请求它们可能是值得的),并且您需要在逻辑上确定确定的位置做出决定(客户端是否决定他被枪杀是因为他有自己的最新位置,还是服务器停止作弊)
【讨论】:
【参考方案4】:客户端部分基本相同,除了替换
run AI
move enemies
resolve collisions
与
upload client data to server
download server updates
而服务器只是这样做:
while (game is running)
get all clients data
run AI
resolve collisions
udpate all clients
【讨论】:
【参考方案5】:您可以使用几乎相同的东西,但大多数逻辑将在服务器上,您可以将计时器、声音、图形和其他 UI 组件放在客户端应用程序上。 任何业务规则(人工智能、运动)都进入服务器端。
【讨论】:
【参考方案6】:这是一篇非常有用且我认为值得阅读的相关论文:Client-Server Architectures
我读了它,从中学到了很多东西,很有意义。通过将游戏分成战略性定义的组件或层,您可以创建更易于维护的架构。该程序比您描述的传统线性程序模型更容易编码,并且更健壮。
这个思考过程出现在之前的一篇文章中,关于使用“共享内存”在程序的不同部分之间进行对话,从而克服了具有单线程和逐步游戏逻辑的限制。
您可以花费数月时间研究完美的架构和程序流程,阅读一篇论文并意识到您一直在寻找错误的树。
tldr;阅读它。
【讨论】:
以上是关于客户端-服务器游戏算法的主要内容,如果未能解决你的问题,请参考以下文章