目前我的游戏服务器很小(一个区域和~50 AI),每次发送状态更新数据包(UDP)时,它会向每个客户端发送一个完整的状态。这会创建大约1100字节的数据包大小。它发送的几乎全部是所有实体的以下信息:
int uid
int avatarImage
float xPos
float yPos
int direction
int attackState
24 bytes
编辑:更高效的结构
int uid
byte avatarImage
float xPos
float yPos
byte direction & attackState
14 bytes
但我最终需要为实体发送更多信息。例如,我补充说:
float targetXPos
float targetYPos
float speed
由于需要为每个实体发送更多数据,我正在快速接近并且很可能已经超过了数据包的最大大小。所以我试着想办法解决我的问题:
1)只需建立状态更新数据包,直到我用完房间,然后忽略其余部分。非常糟糕的客户视图。不是一个选择。
2)仅将N个最近实体的数据发送给客户端。这要求每个状态更新我为每个客户计算最接近的N.这可能非常耗时。
3)一些如何设计数据包以便我可以为同一更新发送多个数据包。目前,客户端假设数据包采用以下结构:
int currentMessageIndex
int numberOfPCs
N * PC Entity data
int numberOfNPCs
N * NPS Entity data
然后,客户端获取此新数据并完全覆盖其状态副本。由于数据包是完全自包含的,即使客户端错过了数据包,也可以。我不确定如何为同一更新实现多个数据包的想法,因为如果我错过其中一个,那么呢?我不能用更新,部分状态覆盖完整的,过时的状态。
4)仅发送变化的实际变量。例如,对于每个实体,我添加一个int,它是每个字段的位掩码。每次更新都不需要发送速度,目标,方向和avatarImage等内容。我仍然回到如果客户端错过了确实需要更新其中一个值的数据包会发生什么的问题。我不确定这会有多重要。这也需要在客户端和服务器端进行更多的计算以创建/读取数据包,但不要太多。
那里有更好的想法吗?
答案 0 :(得分:3)
我会选择4号和2号。 如您所知,通常最好只发送更新而不是完整的游戏状态。但请确保始终发送绝对值而不是增量,以便在丢弃数据包时不会丢失任何信息。您可以在客户端使用航位推算,以便在糟糕的网络条件下尽可能平滑地制作动画。 您必须仔细设计,以便在数据包丢失时并不重要。
对于数字2,如果你设计它,它不一定非常耗时。例如,您可以将游戏区域划分为正方形网格,其中每个实体始终位于一个特定的正方形中,让游戏世界对此进行跟踪。在这种情况下,计算9个周期网格中的实体是O(1)操作。
答案 1 :(得分:1)
这种类型的系统通常使用Dead Reckoning或predictive contract算法来解决。您不能依赖所有客户端同时获取更新,因此您需要根据以前已知的值预测位置,然后根据服务器生成的结果验证这些预测。
答案 2 :(得分:0)
我遇到的一个问题是发送增量更新(基本上是你的第四个选项),数据可能是乱序或旧的,这取决于你的服务器的并发性,基本上是多个客户端更新服务器的竞争条件同一时间。
我的解决方案是向所有客户端发送更新通知,并为每个已更新的项目设置位掩码。
然后客户端根据位掩码请求特定数据的当前值。这也允许客户端只请求它感兴趣的数据。
这样做的好处是可以避免竞争条件,并且客户端始终获得最新价值。
缺点是需要往返才能获得实际值。
更新以证明我想要表达的观点。
假设4个客户A,B,C,D。 A和B同时向服务器Xa和Xb上的可变状态X发送更新。由于B稍晚于A,服务器上的X的最终状态是X = Xb。 p>
服务器在发送到所有客户端时发送更新状态,因此C和D获得X的更新状态,因为交付顺序是不确定的C得到Xa然后Xb和D得到Xb然后是Xa,所以在这一点客户端C和D对X的状态有不同的想法,一个反映服务器具有的另一个没有,它已经弃用(或旧)数据。
另一方面,如果服务器刚刚发出X已更改为所有客户端的通知,则C和D将获得X的两个状态更改通知。它们都会请求当前的X状态,并且它们都是最终得到服务器上X的最终状态Xb。 p>
由于状态通知的顺序无关紧要,因为其中没有数据,并且客户端在每个通知上发出更新状态请求,因此它们都会得到一致的数据,
我希望我更加明确这一点。
是的,确实会增加延迟,但设计人员必须决定什么更重要,延迟或让所有客户端反映相同的可变数据状态。这取决于数据和游戏。