如何使用客户端服务器架构有效地在游戏中发送对象

时间:2019-05-25 17:50:46

标签: c# networking server xna tcpclient

我制作了一个联网的2d自上而下的射击游戏。这是相当标准的,用户使用WASD移动其播放器,并可以使用12345和鼠标来投射咒语。用户必须杀死敌人并保持生命。

我已经将它联网以供多个用户在同一个竞技场上玩,但是运行起来效率太低。

Sprite存储在Dictionary<string, sprite>中,而KeyboardState存储在客户端和服务器计算机中。

它是这样实现的:

  1. 用户的键盘和鼠标输入每帧(MouseStateBinaryFormatter)发送到服务器。
  2. 服务器接受输入并更新游戏状态。
  3. 服务器向每个客户端发送其所有小精灵的视口(矩形)以及其所有小精灵的位置(浮动,浮动),精灵键(字符串),旋转(浮动),颜色(整数,整数,整数,整数)用户的视口。
  4. 服务器将此信息存储为包含这些值的类的实例。
  5. 要发送它,服务器使用TcpClient来序列化该类,然后使用SocketsselectProduct(item, quantidade){ .... this.setState({ itensNota }); } 发送它。
  6. 客户端将输入的数据反序列化回类中并将其绘制到屏幕上

不幸的是,这使游戏变得异常缓慢。我仅在一台机器上进行了测试,因此没有延迟,因此我假设要发送的数据太多。

要解决此问题,我想我要么需要有效地存储和发送数据,要么以不同的方式实现。

要高效发送数据,我认为我需要将数据打包为字节并发送,但我不知道该怎么做。

我将如何高效地发送数据,或以不同的方式实现数据以提高效率?

1 个答案:

答案 0 :(得分:1)

最小化遍历网络的数据量。仅发送所需数量的数据,并通过时间戳和验证提供您自己的验证。

下面的每个actionId和AnimationId应该显示最小(2的幂)个字节,以唯一地描述所有可能的值。


服务器->客户端:

服务器已知的客户端位置和轮换。最后一个actionId的确认。 客户端视域内所有其他客户端位置,方向(旋转),animationIds ...的列表,+-maxRotationRate,距离MaxViewDistance +-maxMovementRate。

客户端必须将其当前位置与服务器位置的每一步平均。

本地处理所有输入(鼠标和键盘),并且仅将相关数据发送到服务器:

每次更新时,客户端将发送相关数据并更新其自身的内部状态并处理给定的服务器状态,并增加int值(将此称为时间戳)。在此处添加奇偶校验或CRC。


客户端->服务器 当前位置,旋转,actionId,animationId和单调递增的int值(将此称为时间戳)。在此处添加奇偶校验或CRC。

所有渲染决定,视口,当前动画/精灵/颜色应在客户端上确定(登录时需要将颜色和初始位置提供给客户端)。

位置数据将被共享(本地更新和服务器调整)。

位置,旋转和速度数据(线性和旋转)都需要在每次更新时发送到服务器。在服务器上收到后,必须进行验证过程以防止作弊。在错过,乱序或格式错误的更新中,服务器将假定最后一个位置+速度*(与上一个时间戳的时间差+当前服务器时间)。


必须使用UDP进行通信(请注意,您使用自己的时间戳和CRC来验证接收到的数据,无效数据不会再崩溃)。

仅处理奇偶校验/ CRC检查通过的数据包,并拒绝接收时间太短或未通过检查的数据包。这些检查将消除与不良数据包相关的崩溃。

TCP的重试机制和有序交付不利于游戏性能。每次单个数据包丢失或乱序发送时,RTT(往返时间,即Ping)会增加时间增量错误(发送数据的时间与服务器处理数据的时间)。时间)服务器重试和数据包重新排序。这将导致几秒钟的延迟,而延迟只会随着游戏的进行而增加。

网络延迟和丢包/格式错误的包是游戏的正常现象。在基于时间戳值的乱序数据包的情况下,请丢弃除最后一个包以外的所有包,因为这表示玩家最近的愿望(移动)。启用射击(在您的情况下为施法),继续发送咒语位直到被确认(ActionId)。忽略服务器上除收到的第一个咒语外的所有咒语(将确认发送给客户端),直到充值期到期为止。

尽管此答案排除了原始问题中未提出的所有要点,主要是损害,但应创建一种结构,当产生损害诱因元素时,服务器上将根据定义的规则分配损害损害分配,需要将其应用于每个受灾客户。