Skip to content

射击游戏:服务端权威与瞄准同步 ​

适用于顶视角射击、TPS、FPS,以及上下半身独立朝向的合作或对抗游戏。相机视角和模型动画可以不同,但共同使用以下链路:

text
客户端输入 -> 服务端校验与模拟 -> 权威状态下行
    |                              |
    +-> 本机即时瞄准/预测表现        +-> 远端角色平滑表现

本篇定义通用同步配方。gamegraph_top_down_pve_template 已使用输入消息、服务端模拟、上下身子节点复制和视觉平滑实现顶视角合作,完成 Windows 编辑器普通单人及双人、四人调试,覆盖晚加入、独立控制、远端瞄准、共享战斗、个人重生和全队重开。本样例的持续瞄准输入使用 30 Hz、0.5 度变化阈值及 100 ms 静默刷新间隔;这些是该样例的起点,并非所有射击游戏的通用最佳值。FPS/TPS 的移动预测、历史快照插值、延迟补偿,以及网络断线、丢包和抖动仍需按项目实现与验收。

1. 分清输入和状态的方向 ​

内容方向实现选择
移动意图、瞄准方向、开火/换弹请求客户端 -> 服务端跨端消息;高频输入优先紧凑二进制
权威位置、下半身朝向、上半身瞄准服务端 -> 客户端replicated Node transform,或项目姿态快照
血量、弹药、死亡、装备状态服务端 -> 客户端PropertyHostComponent;已有 Unit 项目用其属性体系
后坐力、步态、枪口闪光、音效客户端本地由输入预测或服务端确认事件驱动

PropertyHostComponent 和 Node.SetSyncVar(...) 的同步写入由服务端负责,不能作为客户端输入上行接口。客户端决定“想朝哪里瞄准”;服务端决定这一输入是否可接受,以及是否开枪、命中和扣血。服务器权威不要求本机瞄准等待回包。

保留项目已有路线:Unit/Ability 项目复用其移动、伤害和表现桥接;直接 GameGraph 项目可以继续使用 Node 和项目战斗逻辑。不要仅因有角色、血量或联机需求就重建项目架构。

2. 输入上行:表达意图,关联具体的一枪 ​

现有通信入口见二进制消息、强类型消息和双端事件。代码项目也可使用 IProtocolClientTransient<T> 定义 unmanaged 输入协议,由 SendToServer() 发送,服务端在 Handle(Player player) 中取得实际发送者。消息接口本身没有提供完整的射击输入缓存、预测或确认协议。

以下字段是项目协议建议,不是 SDK 已定义的消息类型:

字段用途
LifeId / RoundId服务端分配的重生或回合代次,拒绝上一生命的滞留输入
InputSequence对本次生命内的输入排序、去重,支持确认与预测回放
MoveAxes移动意图;服务端限制长度并用自身时间步模拟
AimYaw / AimPitch约定坐标系中的瞄准角;顶视角通常只需 yaw
Buttons / ShotSequence持续按键状态和需要单独确认、去重的开火事件
SampleTime可选时间样本;需时钟映射和边界限制,不能直接信任客户端时间

在服务端按真实发送者找到其可控制角色,校验代次、序号、有限数值、pitch 范围、消息频率、存活状态和武器规则。客户端提交的位置、枪口、伤害或命中对象不能直接成为权威结果。自由鼠标瞄准通常允许快速甩枪;转向速度限制只在玩法明确要求时启用。

协议显式约定角度单位、零方向和正方向。新项目可以使用弧度,yaw 为 0 指向世界 +X,正 yaw 朝 +Y,正 pitch 朝 +Z;对应单位方向为 (cos(pitch)*cos(yaw), cos(pitch)*sin(yaw), sin(pitch))。已有项目可保留自己的约定,但在消息边界统一转换;模型前向和相机局部轴不能直接混入协议。

持续输入可以采样最新状态,不必对每次鼠标事件发送消息。开火、停火和换弹等边沿要有明确的送达/确认策略;不要因为瞄准角差未达阈值而漏发按钮变化。输入停止或失焦后发送中立状态,服务端也设置可调输入失效时间,避免断线后持续移动或开火。序号采用回绕安全比较,重生后重建其作用域。

具体的一枪需要关联其瞄准方向。 单次开火请求携带该枪的 aim 和 ShotSequence;按住连射时可以提交 fire-held,由服务端根据弹药、射速和最新有效 aim 生成各枪。释放 fire-held 不得被角度限流吞掉。服务端返回确认的 shot id、实际发射方向和结果,客户端据此合并预测的子弹/闪光,避免重复播放。

不要假定独立的移动、aim、开火消息总按理想顺序到达。输入流的序号和开火去重各自定义清楚;较新的移动状态不能无条件吞掉仍在允许时间窗口内、尚未处理的一枪。已有 SendToServer() 也不能被解释为自动提供“可丢弃旧输入”的网络通道。

发送返回值不等于服务端处理确认。涉及输入回放、换弹结果或重试去重时,仍需要协议中的 ack/确认事件。

3. 下行:控制节点与玩法属性分别承载 ​

对于直接 GameGraph 的分离身体角色,优先考虑少量 replicated 控制节点:

text
EntityRoot (replicated,位置,Rotation 保持单位旋转)
  +-- LowerPose (replicated,下半身朝向)
  |     +-- 下半身模型、步态关节 (client local)
  +-- AimPose (replicated,上半身瞄准姿态)
        +-- 上半身模型、武器、后坐力 (client local)

Root 无旋转时,两条姿态分支可直接表达约定的世界方向;如果 Root 本身有旋转,则子节点需由世界目标姿态换算成父坐标系下的局部姿态。下半身停下时保留最后方向,不能每帧从零速度重算朝向。服务器逻辑姿态使用游戏坐标系;模型资源的 Y-up/Z-up、尺寸和前向修正放在客户端 local 模型层,避免把模型导入偏置误当瞄准状态。

服务端创建子节点时显式传 NodeReplication.Replicated,因为 Node.CreateChild(...) 默认是 local。服务端不需要加载角色模型或逐骨骼模拟,客户端根据控制姿态、移动状态与确认事件生成动画。枪口的权威偏移/姿态应由项目武器配置和服务端逻辑计算,不能依赖客户端模型骨骼查询。

replicated Node 的旋转已通过引擎复制并由客户端 SmoothedTransform 平滑到最新目标。属性组件支持把血量、弹药等挂到节点,并按属性指定同步范围;属性组件和节点是两条独立到达的同步流,读取须使用 TryGetPropertyHost / OnPropertyHostReady。详见节点复制和属性组件。

另一种合理路线是用属性或小型 SyncVar 保存语义上的 AimYaw/AimPitch,在客户端驱动骨骼瞄准、IK 或上半身节点。这些值不会自动获得 Node 的旋转平滑,需要项目按最短角度/四元数插值。需要时间戳、成组姿态或精确输入确认时,可使用专门的姿态消息;不要把其时间语义假定为普通属性同步已经提供。

同一个姿态只选一个权威来源。不要同时同步 AimPose.Rotation 和 AimYaw,再让两套数据竞争控制上半身。用于日志的镜像值可以存在,但应明确哪一项控制表现。多个 Node、属性和确认消息也不保证组成原子快照;需要位置、朝向和输入确认对应同一模拟 tick 时,应发送包含这些字段的成组快照。

独立发布图由服务端 Publish(player) 给两位玩家;已有 SCE 场景通过自身 sync root 同步,不对内置图调用 Publish。已有 Unit 项目可使用 UnitNodeBinding 等桥接,继承该 Unit 的可见性。静态场地/渲染资源尽量客户端 local,服务端持有对应的碰撞数据;只复制有运行时变化的状态。

4. 本机预测与远端平滑 ​

本机相机和瞄准表现每帧立即更新;权威节点作为服务端状态来源,预测的本地表现通过独立节点或项目控制器渲染。不要每帧覆盖收到的 replicated transform,与其内置平滑争抢同一节点。也不要叠加两层未经设计的平滑,使操作额外迟滞。

移动预测需要明确的闭环:

  1. 本机按输入推进预测位置,缓存输入序号和模拟步长。
  2. 服务端使用相同移动规则,在自己的模拟时间步处理输入。
  3. 服务端返回权威状态、server tick 和已处理输入序号。
  4. 本机丢弃已确认输入,从该权威状态回放未确认输入,对显示偏差做纠正。

仅收到 Node 的平滑位置不等于已经具备这个闭环。若实现输入回放,确认快照需包含对应的权威位置/姿态,不能把独立到达的 ack 序号与当前平滑显示位置拼接成权威快照。刚体预测也不默认具有跨端确定性;小型合作游戏可先验证服务端模拟与本机即时瞄准,再按实际手感加入移动预测。

远端玩家可先使用现有 Node 平滑。它是向最新目标收敛,不是带 server tick 的历史快照插值缓冲,不自动提供外推、回滚或命中回溯。死亡、重生和传送需要明确的姿态重置策略,并重建输入/预测缓存;短距离重生也不能只依赖位置超过 snap 阈值。

5. 将角度阈值、发布频率和表现平滑分开 ​

参数作用当前实现方式
输入发送角差减少上行微小鼠标变化项目发送策略
输入最短发送间隔、最大静默间隔控制频率和状态新鲜度项目发送策略
权威姿态发布角差/频率减少下行姿态变化项目发布姿态代理,或后续复制 API
客户端收敛速度远端 Node 追随网络目标的速度TransformSmoothingConstant
客户端位置贴合距离大幅位移时直接贴合TransformSnapThreshold,单位为世界距离
玩法最大转向速度限制角色或炮塔实际转动服务端玩法规则,与网络阈值独立

当前节点旋转通过压缩四元数变化检测复制,没有公开的“超过 N 度才同步”配置。压缩精度也不等于业务角度容忍度。ConfigureTransformSmoothing(...) 是客户端图级设置,不能用作发送频率或旋转角差设置。

角差必须相对上次实际发送值计算,并处理圆周边界。例如 yaw 由 179 度转到 -179 度时,角差是 2 度。四元数采用归一化后的最短旋转角,q 与 -q 表达同一个旋转。发布成功前不要更新 lastSent,以免发送失败后错误抑制重试。

一种项目策略是:在发送机会到来时,角差超过阈值或达到最大静默间隔则发送;按钮变化、开火和生命代次变化独立处理。顶视角原型可从 0.5 度、持续变化时 20-30 Hz、最大静默间隔 100 ms 开始测量。这不是 SDK 默认值或已验证推荐值,输入失效时间应留出合理的网络抖动余量。

角度误差在远处会放大:横向偏移约为 距离 * tan(角差),100 米处 0.5 度约为 0.87 米。远距离精密射击应缩小输入角差,且开火请求携带该枪的完整 aim;远端观看姿态可以采用更宽松阈值。时间延迟带来的误差仍需另外处理。

若第一版在项目里限流下行,用独立发布姿态承载低频显示目标,服务端逻辑始终保存完整精度的 aim。初始化、重生、停转及新订阅时确保目标状态就绪,避免阈值永久留下旧方向。不要降低权威旋转精度来实现省流。移动/物理需要的根节点也不能随意限流写入而改变实际模拟。

星火当前使用自己的传输链路承载引擎复制。不能假定开放 Urho Network、Controls 或 NetworkPriority,就能自动获得可配置的输入通道、快照丢弃策略、预测与延迟补偿。新增传输策略需沿实际客户端/Host 路径验证。

6. 顶视角、TPS 和 FPS 的不同点 ​

视角瞄准意图本机表现服务端射击判定关注点
顶视角鼠标射线与游戏瞄准平面得到方向,通常 yaw上下身分离,固定或跟随相机枪口偏移、子弹连续扫掠、停转与绕角
TPS相机准星射线,yaw + pitch相机、身体/上身分离、肩位和遮挡准星与枪口视差、近身掩体、可信的视点规则
FPS视线 yaw + pitch本机相机与武器立即响应,远端身体单独驱动视点高度、枪口规则、远距离精度、射线延迟补偿

TPS 的相机准星先选出期望瞄准点,再从枪口向该点确定发射方向。服务端按照自己的角色状态、武器偏移和视点规则重建或校验意图,并检查枪口前的遮挡。客户端相机绕过墙看到目标,不代表枪口也能穿墙射击。视点/目标点可以作为请求数据,但不能成为免校验的命中结果;准星应能反馈枪口路径被挡。

FPS 也要明确视线命中与枪口弹道的武器规则,而不是假定两者重合。自由观察、死亡观战或 TPS 的相机 yaw 不一定是武器 aim;协议按玩法区分 view、aim 和 body,不能把相机旋转直接当作所有身体姿态。

若射线命中需要延迟补偿,服务端保留带时间的 hitbox 历史,在限制窗口内映射发射时间并执行历史查询,规定移动掩体、死亡/重生边界和超时输入的处理。它是需要新增实现与验收的机制。飞行子弹先采用服务端当前位置连续扫掠,不自动套用射线武器的历史回溯策略。

7. 后续 API 收口与验收 ​

以下是设计候选项,均不能写成现有能力:

  • 输入采样/发送策略:角差、频率、keepalive、按钮边沿、代次与序号,尽量在托管层复用。
  • 节点复制策略:位置/角度阈值、最大发布间隔,独立于权威 transform,并保证首次订阅和重生快照。
  • 姿态消费接口:最新网络目标与显示值的区分、按对象平滑/立即重置,支持本机预测和远端观看采用不同策略。
  • 预测确认快照:权威状态、server tick、ack 序号、同一步长回放;不承诺所有物理系统都能确定性回放。
  • FPS 的 hitbox 历史与时间映射,仅在武器需求明确后实现。

优先用双人合作样例验证现有机制,再由真实重复代码决定 helper/API 的边界。普通编辑器调试需贯通服务端编译/上传、服务端开局和两客户端连接;纯客户端只能验证表现。最终验收至少覆盖:

  • 两玩家横移、背向移动仍可独立瞄准,停下后姿态不归零。
  • 持续小幅转向、跨 +/-180 度、快速甩枪、开火时方向一致;无重复子弹/音效。
  • 延迟、抖动、丢包或重发条件下按钮释放有效,输入过期后停止控制;服务端射速/弹药/伤害保持权威。
  • 本机输入确认与纠偏,远端平滑,重生/回合重开拒绝旧请求;后加入取得当前完整状态。
  • TPS 相机与枪口视差、近身墙角遮挡;FPS 远距离精度和采用的命中时间策略。

记录实际发送率、消息大小、输入年龄、朝向误差、纠偏幅度和双方日志,用结果选择参数,不能只凭单客户端截图认定同步正确。