4、网络同步基础(Transform同步):使用NetworkTransform、同步频率与插值、处理网络延迟

好,咱们今天聊一个很实在的话题——Transform同步。

说白了,就是让所有玩家看到的物体位置、旋转、缩放保持一致。你想想看,一个FPS游戏里,敌人明明在你屏幕里站着,结果你开枪打空气,因为在他自己的世界里早就跑开了。这就是同步没做好。

4.1 NetworkTransform:开箱即用的同步组件

Unity Netcode 给我们提供了一个好东西——NetworkTransform。我个人习惯叫它「NT」。这东西就像个勤劳的快递员,不停地把物体的位置、旋转信息打包发出去。

用法很简单:

// 给物体挂上 NetworkObject + NetworkTransform
// 然后 spawn 它
GameObject go = Instantiate(prefab);
go.GetComponent<NetworkObject>().Spawn();

嗯,这里要注意:只有服务器有权修改NT的Transform。客户端想改?得先申请权限。我在项目中遇到过新手直接把客户端的位置赋值给NT,结果服务器根本不认,角色卡在原地不动。

核心原则:谁拥有权威(Authority),谁就能改Transform。默认是服务器拥有所有物体的权威。

4.2 同步频率:别让网络变成水管

NT默认每帧都发数据?那你的带宽就炸了。实际上,我们得控制发送频率。

看这个参数:

// 在 NetworkTransform 组件上
// Sync Interval = 0.1 表示每100ms发一次
// 也就是每秒10次

为什么是10次?我做过测试:

同步频率 带宽消耗 平滑度 适用场景
30次/秒 非常平滑 局域网、高速竞技
10次/秒 良好 一般网络游戏
5次/秒 有卡顿感 休闲、回合制

我个人建议:先设10次/秒。如果感觉卡顿,再往上调。别一上来就30次,你想想看,100个玩家每人每秒发30次位置更新,服务器扛不住的。

小技巧:动态调整频率。角色静止时降到2次/秒,移动时升到15次/秒。我在项目中用这个方案,带宽直接省了60%。

4.3 插值:让运动看起来丝滑

频率设低了,位置更新就断断续续的。怎么办?插值。

NT自带两种模式:

  • Interpolate(插值):在两次更新之间,平滑过渡。适合大多数情况。
  • Extrapolate(外推):根据上次的运动趋势,预测下一个位置。适合高速移动的物体。

我举个例子:

// 在 NetworkTransform 上设置
// Interpolate = true
// 这样客户端收到位置A和位置B之间,会自动补帧

为什么会这样?因为网络包不是均匀到达的。有时候100ms来一个,有时候150ms。插值就是让物体在这段时间里「假装」在移动,而不是瞬移。

我曾经做过一个赛车游戏,没开插值前,对手的车像在跳帧动画。开了之后,虽然实际位置有误差,但视觉上完全能接受。

注意:插值会引入延迟。你看到的位置其实是100ms前的。对于需要精确碰撞检测的游戏(比如格斗),要谨慎使用。

4.4 处理网络延迟:别让玩家骂娘

延迟是绕不开的坎。你按了W,角色0.5秒后才动,玩家早卸载游戏了。

我的处理思路分三步:

  1. 客户端预测:客户端收到输入后,立即执行,不等服务器确认。
  2. 服务器权威:服务器最终决定正确位置,并纠正客户端。
  3. 延迟补偿:服务器回滚到玩家发出输入的时刻,进行判定。

代码示例(简化版):

// 客户端:立即移动
void Update() {
    if (IsOwner) {
        float move = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
        transform.position += Vector3.right * move;
        // 同时发送输入给服务器
        SendInputToServer(move);
    }
}

// 服务器:收到输入后验证
void OnServerReceivedInput(float move, ulong clientId) {
    // 根据输入计算预期位置
    Vector3 expectedPos = GetPlayer(clientId).transform.position 
                          + Vector3.right * move;
    // 如果客户端位置偏差太大,纠正它
    if (Vector3.Distance(actualPos, expectedPos) > 0.5f) {
        SendCorrection(clientId, expectedPos);
    }
}

嗯,这里有个坑。我曾经做过一个项目,客户端预测做得太激进,导致角色在服务器纠正时「瞬移」回来。玩家反馈说「我的角色被鬼拉回去了」。后来我加了平滑纠正,用Lerp慢慢过渡,体验就好了很多。

避坑指南:我曾经把同步频率设成0.01秒(100次/秒),结果服务器CPU直接飙到90%。后来改成动态频率,根据物体移动速度自动调整,问题就解决了。

4.5 实战建议:从简单开始

如果你刚开始做联机,别一上来就搞客户端预测、延迟补偿这些高级玩意。我建议:

  • 先用NT默认设置,跑通同步流程
  • 观察带宽和延迟,调整频率
  • 加上插值,改善视觉体验
  • 最后再考虑预测和补偿

说白了,Transform同步就是个「平衡艺术」。你要在带宽、延迟、平滑度之间找平衡点。没有完美的方案,只有适合你游戏的方案。

我记得第一次做联机时,光调同步参数就花了一周。但当你看到所有玩家的角色都能流畅地一起跑动时,那种成就感,嗯,值得。

最后一个小建议:用Unity的Profiler监控网络数据。看看哪个物体在疯狂发数据,哪个物体同步延迟高。数据不会骗人。