问题?

  • 在一定延迟的情况下,敌方角色在 ta 所属的客户端的位置A,服务器当前敌方角色的位置B,以及我方客户端下敌方角色的位置C是不一致的;这个不一致与两者连接服务器的Ping值有关;假设双方RTT为50ms;则
    • A位置在当前服务器50ms后的位置;B位置在0ms后的位置,而C位置在服务器-50ms的位置;这会导致双方命中率都不高(需要命中后50ms的位置);

思路核心总结

  • 服务器倒带算法的核心总结:服务器端记录一段时间内所有角色的位置信息,客户端记录击中时间并RPC调用服务器倒带算法 还原击中时间下的 被击中角色位置信息,用于验证客户端是否击中角色。
  • 缺点:如果玩家躲到掩体后死亡,则对于该玩家不公平。
    • 解决方案:1、限制在一定Ping值下使用该算法;

整个使用算法找到当前位置的流程

  1. 定义服务器倒带算法在开枪判断命中时倒带的是什么物件;
    • 如果记录角色的 location or capsule 会太简陋了。
    • 如果记录骨骼的location和rotation 太多,消耗内存。
    • 答案:创建一群HitBoxes的Box 绑定在角色的每一个位置上;
  2. 创建一个FBoxInformation 保存HitBox的location,rotation,BoxExtent;
  3. 创建一个FramePackage,用于每帧保存角色当前的状态,FramePackage包含 FBoxInformation。
  4. 编写 SaveFramePackage函数,保存当前一帧时间下服务器的角色当前的FramePackage。
  5. 更新 FrameHistory 链表的内容,这个包含了从前N帧到当前结束保存的角色Frame。
    • 流程:如果列表有内容,则判断链表中的值是否为当前所允许的范围(PS:如果延迟过大,则不计入),如果不是就移除;并把最新帧插入。
  6. 服务器找到 (如果找不到就插帧) 距离最近的当前客户端的一帧的角色信息。
    1. 初始化链表History,头指针NewestHistoryTime,尾指针OldestHistoryTime。
    2. 如果当前客户端时间比OldestHistoryTime还小,那不能使用服务器倒带算法。(PS:延迟过大了)
      • 如果当前客户端时间比OldestHistoryTime相等,则已经找到当前的最近的时间。(这里直接返回)
      • 如果当前客户端时间比NewestHistoryTime还大,把头指针给他就完事了。(这里直接返回)
    3. 如果没找到值,并且在头指针与尾指针之间的时间,遍历找到OlderTime < HitTime < YoungerTime这个关系后,对OlderTime的帧以及YoungerTime的帧进行插值后返回
  7. (如果找不到就插帧) 对于两帧之间进行插值计算当前客户端的角色位置
    1. 计算两帧时间的距离 Distance ,计算 HitTime 到 OlderFrameTime 以及距离的比值 InterpFraction。
    2. 通过这个比值InterpFraction从 OlderBox 到 YoungerBox 对 InterpBoxInfo 进行插值计算。

确认是否击中流程

  1. CacheBoxPositions 函数把被击中角色的当前状态保存到 CurrentFrame 中。

  2. MoveBoxes 函数使用变量 Package 把角色移动到服务器倒带的位置上(就是上面找到的位置)。

  3. EnableCharacterMeshCollision函数将 HitCharacter 关闭碰撞。

  4. 开启 HitCharacter 头部的碰撞,检测是否击中头部。

    • 如果击中头部,ResetHitBoxes 重新初始化角色位置;EnableCharacterMeshCollision 重新设置;返回 return 击中头部 = true 以及击中确认 = true;
    • 如果没击中头部;开启 HitCharacter 所有碰撞;检测是否击中角色;击中返回 return 击中头部 = false 以及击中确认 = true;没击中返回 return 击中头部 = false 以及击中确认 = false;
  5. ResetHitBoxes 重新初始化角色位置;EnableCharacterMeshCollision 重新设置。