问题?
- 在一定延迟的情况下,敌方角色在 ta 所属的客户端的位置A,服务器当前敌方角色的位置B,以及我方客户端下敌方角色的位置C是不一致的;这个不一致与两者连接服务器的Ping值有关;假设双方RTT为50ms;则
- A位置在当前服务器50ms后的位置;B位置在0ms后的位置,而C位置在服务器-50ms的位置;这会导致双方命中率都不高(需要命中后50ms的位置);
思路核心总结
- 服务器倒带算法的核心总结:服务器端记录一段时间内所有角色的位置信息,客户端记录击中时间并RPC调用服务器倒带算法 还原击中时间下的 被击中角色位置信息,用于验证客户端是否击中角色。
- 缺点:如果玩家躲到掩体后死亡,则对于该玩家不公平。
- 解决方案:1、限制在一定Ping值下使用该算法;
整个使用算法找到当前位置的流程
- 定义服务器倒带算法在开枪判断命中时倒带的是什么物件;
- 如果记录角色的 location or capsule 会太简陋了。
- 如果记录骨骼的location和rotation 太多,消耗内存。
- 答案:创建一群HitBoxes的Box 绑定在角色的每一个位置上;
- 创建一个FBoxInformation 保存HitBox的location,rotation,BoxExtent;
- 创建一个FramePackage,用于每帧保存角色当前的状态,FramePackage包含 FBoxInformation。
- 编写 SaveFramePackage函数,保存当前一帧时间下服务器的角色当前的FramePackage。
- 更新 FrameHistory 链表的内容,这个包含了从前N帧到当前结束保存的角色Frame。
- 流程:如果列表有内容,则判断链表中的值是否为当前所允许的范围(PS:如果延迟过大,则不计入),如果不是就移除;并把最新帧插入。
- 服务器找到 (如果找不到就插帧) 距离最近的当前客户端的一帧的角色信息。
- 初始化链表History,头指针NewestHistoryTime,尾指针OldestHistoryTime。
- 如果当前客户端时间比OldestHistoryTime还小,那不能使用服务器倒带算法。(PS:延迟过大了)
- 如果当前客户端时间比OldestHistoryTime相等,则已经找到当前的最近的时间。(这里直接返回)
- 如果当前客户端时间比NewestHistoryTime还大,把头指针给他就完事了。(这里直接返回)
- 如果没找到值,并且在头指针与尾指针之间的时间,遍历找到OlderTime < HitTime < YoungerTime这个关系后,对OlderTime的帧以及YoungerTime的帧进行插值后返回。
- (如果找不到就插帧) 对于两帧之间进行插值计算当前客户端的角色位置
- 计算两帧时间的距离 Distance ,计算 HitTime 到 OlderFrameTime 以及距离的比值 InterpFraction。
- 通过这个比值InterpFraction从 OlderBox 到 YoungerBox 对 InterpBoxInfo 进行插值计算。
确认是否击中流程
CacheBoxPositions 函数把被击中角色的当前状态保存到 CurrentFrame 中。
MoveBoxes 函数使用变量 Package 把角色移动到服务器倒带的位置上(就是上面找到的位置)。
EnableCharacterMeshCollision函数将 HitCharacter 关闭碰撞。
开启 HitCharacter 头部的碰撞,检测是否击中头部。
- 如果击中头部,ResetHitBoxes 重新初始化角色位置;EnableCharacterMeshCollision 重新设置;返回 return 击中头部 = true 以及击中确认 = true;
- 如果没击中头部;开启 HitCharacter 所有碰撞;检测是否击中角色;击中返回 return 击中头部 = false 以及击中确认 = true;没击中返回 return 击中头部 = false 以及击中确认 = false;
ResetHitBoxes 重新初始化角色位置;EnableCharacterMeshCollision 重新设置。