如何实现子弹的Server Authoritative
解读
国内主流多人射击、MOBA、动作类游戏在版号合规与反外挂高压下,服务器权威(Server Authoritative) 是必答题。面试官想确认三点:
- 你是否理解“客户端只表现、服务器才裁决”的生死权归属;
- 能否把 Unity 的物理/碰撞逻辑无损迁移到 Linux 服务器,并保证帧一致;
- 在高并发、高 ping 的国服环境下,如何兼顾手感、带宽、反作弊。
知识点
- 确定性(Deterministic):浮点误差、Unity.Physics 与 PhysX 差异、Fixed Timestep 对齐。
- 状态同步 vs 命令同步:子弹用状态同步(位置+速度),玩家输入用命令同步(输入+时间戳)。
- 时间回滚(Rewind & Replay):服务器保存 1 s 环形缓冲区,收到客户端射击命令后回滚到射击帧再前向模拟,解决高 ping 误判。
- 碰撞层级:服务器只保留**“子弹-受击盒”**层级,剔除渲染、特效、音效,降低 CPU 占用。
- 帧号(Frame ID):客户端与服务器统一30 Hz 物理帧,所有命令带帧号,防止客户端插值错位。
- 快照压缩:子弹状态只同步出生帧号+初始坐标+方向+速度,服务器每 50 ms 广播一次死亡事件(命中/超时),带宽 <1 KB/s/玩家。
- 热更新友好:服务器逻辑用纯 C# .NET 6 实现,不依赖 UnityEngine.dll,方便在 Linux 云函数上热更。
答案
-
协议设计
客户端发FireCmd:{frame, origin, dir, weaponId, seed},服务器立即校验武器冷却、弹夹、射线遮挡(反无后坐力外挂)。 -
服务器模拟
- 使用开源确定性物理库(如 BEPUphysics 或自写简版 AABB+射线),Fixed Timestep = 0.033 s,与客户端对齐。
- 子弹按匀速直线模拟,每帧更新:pos += velocity * dt;命中检测用线段检测(Raycast) 而非连续碰撞,降低复杂度。
- 保存世界快照环形缓冲区(Dictionary<frame, Snapshot>),收到延迟命令时回滚到对应帧重新模拟,再把“真正命中”事件下发。
-
结果下发
服务器只同步三件事:- BulletSpawn:出生帧号+初始参数,客户端做预测表现;
- BulletHit:{hitFrame, hitEntity, hitPoint, hitNormal},客户端收到后校正表现(血条、特效、音效);
- BulletMiss:超时未命中,客户端销毁子弹。
-
客户端表现
- 本地立即生成**“假子弹”**做特效与音效,保证手感;
- 收到服务器 Hit/Miss 后,用插值/淡出方式平滑校正,若误差 > 0.5 m 则瞬移并打印日志供运营反外挂。
-
性能与反作弊
- 服务器单核可跑1 万颗并发子弹(Linux 云主机 2.4 GHz),通过对象池+结构体避免 GC;
- 对每颗子弹记录射击者 UID+帧号,若客户端伪造 Hit 事件,服务器直接拒绝并封号日志入库。
拓展思考
- 子弹下坠与风力:若需抛物线,把重力加速度写成定点数(FixedPoint128),保证跨平台一致。
- 霰弹枪多发弹丸:服务器只模拟逻辑弹片(如 3 片),客户端表现 12 片,用随机种子同步保证特效对齐。
- 穿墙外挂:服务器在Rewind阶段用地图静态碰撞+动态掩体双重检测,客户端无法提前获知掩体状态。
- WebGL 观战:服务器把关键事件流(出生+命中)写入 Redis,WebGL 端用Timeline 插值还原,无需跑物理。