客户端-服务器游戏的算法

16
对于独立游戏来说,基本的游戏循环是这样的(来源:维基百科):
while( user doesn't exit )
  check for user input
  run AI
  move enemies
  resolve collisions
  draw graphics
  play sounds
end while

如果我开发像Quake、Ragnarock、Trackmania等客户端服务器游戏,那该怎么办呢?

客户端和服务器部分的循环/算法是什么?

6个回答

20

这将会是类似于:

客户端:

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

12

客户端:

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
客户端命令和世界数据的健全性检查是为了消除由故意作弊(如移动过快、穿过墙壁等)或网络延迟(比如客户端认为门已经打开,而服务器知道门还没打开等)引起的“不可能”情况。
为了处理客户端和服务器之间的延迟,客户端必须根据当前的世界数据和客户端命令做出最佳预测 - 客户端随后需要处理它预测的结果与服务器实际发生的结果之间的任何差异。通常这种差异足够小,玩家不会注意到差别。但如果延迟很大,或者客户端和服务器不同步(例如由于作弊),那么当客户端收到来自服务器的数据时,客户端将需要进行突然的纠正。
此外,将这些流程的分段拆分成单独的线程以优化响应时间也存在许多问题。
开始的最佳方式之一是从一个有着活跃的modding社区的游戏中获取SDK-深入了解其工作原理将提供一个良好的概述。

4

这并不是一个简单的问题。在最基本的层面上,你可以说网络提供了与原始循环中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

当客户端请求敌人位置时,服务器从共享内存块中读取所有其他玩家的位置并将其发送回来。
这只是一个极为简化的概述,还有许多其他调整可以提高性能(例如,服务器向客户端发送敌人位置可能比客户端请求它们更值得),您需要决定某些逻辑决策的位置(例如,客户端是否根据自己最新的位置决定他是否被击中,或者服务器防止作弊)。

3
客户端部分基本相同,只需替换

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
}

3
你可以使用几乎相同的东西,但大部分逻辑在服务器上,你可以在客户端应用程序上放置定时器、声音、图形和其他 UI 组件。任何业务规则 (AI,Movements) 都放在服务器端。

2
一篇非常有用且我认为相关的论文是这篇:客户端-服务器架构
我读了它并从中学到了很多,它让我感到很有道理。通过将游戏分成战略定义的组件或层,您可以创建一个更易于维护的架构。该程序比您描述的传统线性程序模型更易编码且更健壮。
这种思维过程在先前的帖子中提出了“共享内存”来在程序的不同部分之间进行通信,并因此克服了单个线程和步骤跟随步骤的游戏逻辑的限制。
您可以花费数月时间研究完美的架构和程序流程,读一篇论文,意识到您已经在错误的方向上努力。
简而言之,请阅读它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接