Unity Photon Fusion 2D多人游戏冲刺机制卡顿问题咨询
2D多人游戏Photon Fusion冲刺机制抖动问题解决方法
问题描述
我基于Unity Photon Fusion开发2D多人游戏的冲刺机制,需求如下:
- 玩家按下LShift时进入低速移动并显示蓄力滑块UI
- 按住LMb进行蓄力,释放后执行冲刺,冲刺结束后恢复默认移动速度
- 按下RMb可在蓄力过程中或蓄力前取消蓄力,不执行冲刺并恢复默认速度
目前所有功能均正常,仅冲刺部分存在客户端抖动的移动效果。尝试过调整NetworkRigidbody2D的插值数据源(Auto、snapshots、predicted、无插值),最初本地处理DashMove时,状态authority会覆盖客户端玩家位置;改用RPC后因基于时间而非tick导致抖动;让状态authority本地处理冲刺时,玩家会直接跳转到冲刺最终位置。
当前实现代码:
using UnityEngine; using Fusion; public class Player : NetworkBehaviour { public Rigidbody2D PlayerRigidbody; private float _dashSpeed = DataUtils.DashSpeed; // Speed of the dash private float _lastDashTime = -Mathf.Infinity; private Vector2 _movement; public float minDuration; // Minimum ability duration public float maxDuration = DataUtils.MaxDashDuration; // Maximum ability duration public CharacterCanvasBase Canvas; public override void FixedUpdateNetwork() { // player direction input to dash _movement = DirectionInput; // below is psuedo code if (LShiftPressed) { StartCoroutine(Dash()); } } private IEnumerator Dash() { _lastDashTime = Time.time; Vector2 startDashPosition = PlayerRigidbody.position; Vector2 dashDirection = _movement.normalized; // Check if the player is moving. If not, dash downward if (dashDirection == Vector2.zero) { dashDirection = Vector2.down; // Or any default direction you prefer } // Get charge level to determine dash speed and possibly duration float chargeLevel = Canvas.GetComponent<HexCanvas>().ChargeController.GetChargeProgress(); float adjustedDuration = minDuration + (maxDuration - minDuration) * Mathf.Pow(chargeLevel, 2); float dashEndTime = Time.time + adjustedDuration; while (Time.time < dashEndTime) { float dashProgress = (Time.time - _lastDashTime) / adjustedDuration; RPC_DashMove(startDashPosition, dashDirection, dashProgress, _dashSpeed * adjustedDuration); yield return null; } } [Rpc(RpcSources.All, RpcTargets.All)] public void RPC_DashMove(Vector2 startDashPosition, Vector2 dashDirection, float dashProgress, float dashSpeed) { PlayerRigidbody.MovePosition(Vector2.Lerp(startDashPosition, startDashPosition + dashDirection * dashSpeed, dashProgress)); } }
正确实现方式
1. 用Networked变量同步冲刺状态
Fusion依赖tick而非实时时间同步,必须把冲刺的关键状态做成Networked变量,让状态权威端(State Authority)统一控制,其他端通过同步变量实现平滑移动:
// 添加Networked变量存储冲刺状态 [Networked] private bool IsDashing { get; set; } [Networked] private Vector2 DashStartPos { get; set; } [Networked] private Vector2 DashDirection { get; set; } [Networked] private float DashTotalDuration { get; set; } [Networked] private float DashStartTime { get; set; } // 使用NetworkRunner.Time替代本地Time.time
2. 状态权威端处理冲刺逻辑
抛弃本地协程,在FixedUpdateNetwork中由状态权威端触发并计算冲刺进度,所有端基于同步的状态变量执行移动:
public override void FixedUpdateNetwork() { _movement = DirectionInput; // 仅状态权威端处理冲刺触发逻辑 if (HasStateAuthority && LShiftPressed && !IsDashing) { StartDash(); } // 所有端同步处理冲刺移动 if (IsDashing) { UpdateDashMovement(); } } private void StartDash() { IsDashing = true; DashStartPos = PlayerRigidbody.position; DashDirection = _movement.normalized; if (DashDirection == Vector2.zero) DashDirection = Vector2.down; float chargeLevel = Canvas.GetComponent<HexCanvas>().ChargeController.GetChargeProgress(); DashTotalDuration = minDuration + (maxDuration - minDuration) * Mathf.Pow(chargeLevel, 2); DashStartTime = Runner.Time; } private void UpdateDashMovement() { float elapsedTime = Runner.Time - DashStartTime; // 结束冲刺 if (elapsedTime >= DashTotalDuration) { IsDashing = false; return; } float dashProgress = elapsedTime / DashTotalDuration; Vector2 targetPos = DashStartPos + DashDirection * _dashSpeed * DashTotalDuration; Vector2 newPos = Vector2.Lerp(DashStartPos, targetPos, dashProgress); // 状态权威端直接更新刚体位置,其他端通过NetworkRigidbody2D同步插值 if (HasStateAuthority) { PlayerRigidbody.MovePosition(newPos); } }
3. 配置NetworkRigidbody2D插值
- 将NetworkRigidbody2D的Interpolation Source设置为
Predicted,本地玩家使用预测插值降低延迟 - 确保
Prediction Mode设置为Interpolated,让客户端基于同步的tick数据平滑过渡位置
4. 蓄力与取消逻辑优化
- 蓄力阶段仅在本地处理UI显示,释放LMb时再通知状态权威端启动冲刺
- 按下RMb取消时,本地重置UI状态,若冲刺未启动则通知权威端无需触发冲刺
问题根源解析
- 协程依赖本地
Time.time:Fusion同步基于服务器tick,本地时间与服务器tick不同步导致位置偏差 - 频繁RPC调用:每秒数十次RPC会引发网络延迟和位置跳变,Networked变量同步更高效稳定
- 进度计算不一致:之前用本地时间计算冲刺进度,客户端与权威端进度不同步,导致同步时直接跳转到最终位置
内容的提问来源于stack exchange,提问作者levitate
相关产品推荐
相关产品推荐

