You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Photon Fusion主机模式下UI按钮控制玩家移动异常及实现方案咨询

解决Photon Fusion主机模式下UI按钮控制玩家移动的问题

问题根源

  1. 输入未通过网络同步:Host模式下主机是状态权威端,直接在客户端修改rb.velocity的操作不会同步给主机,主机会用自己的权威状态覆盖客户端的本地修改,导致角色移动后立刻回退。
  2. 本地玩家判断逻辑错误:BeforeUpdate中只设置一次isLocalPlayer,会导致主机进程中所有玩家对象的isLocalPlayer都被设为true,进而主机同时控制多个角色。
  3. Shared模式与Host模式的差异:Shared模式下客户端和服务器是同一进程,本地修改直接生效;但Host模式下客户端和主机是独立的网络逻辑,必须遵循Fusion的输入同步规则。

正确实现步骤

1. 定义网络输入结构体

首先创建一个实现INetworkInput的结构体,用来打包UI按钮的输入:

public struct PlayerMovementInput : INetworkInput
{
    public float Horizontal; // 左右移动:-1左,1右,0无
    public bool Jump; // 跳跃触发
}

2. 绑定UI按钮事件,收集本地输入

在你的玩家NetworkBehaviour脚本中,添加本地输入变量,并绑定UI按钮的按下/抬起事件:

private PlayerMovementInput _localInput;

// 绑定UI右移按钮的Down事件
public void OnMoveRightDown() => _localInput.Horizontal = 1f;
// 绑定UI右移按钮的Up事件
public void OnMoveRightUp() => _localInput.Horizontal = 0f;

// 绑定UI左移按钮的Down事件
public void OnMoveLeftDown() => _localInput.Horizontal = -1f;
// 绑定UI左移按钮的Up事件
public void OnMoveLeftUp() => _localInput.Horizontal = 0f;

// 绑定UI跳跃按钮的Click事件
public void OnJumpButtonClick() => _localInput.Jump = true;

3. 重写OnInput发送网络输入

通过Fusion的OnInput方法将本地输入发送给主机:

public override void OnInput(NetworkRunner runner, NetworkInput input)
{
    // 只有拥有输入权限的本地玩家才发送输入
    if (Object.HasInputAuthority && runner.IsLocalPlayer(Object.InputAuthority))
    {
        input.Set(_localInput);
        // 跳跃输入发送后重置,避免持续触发
        _localInput.Jump = false;
    }
}

4. 在权威端处理移动逻辑

只有主机(拥有状态权限的端)处理移动,客户端仅同步主机的状态:

public override void FixedUpdateNetwork()
{
    // 仅状态权威端(主机)执行移动逻辑
    if (Object.HasStateAuthority)
    {
        // 获取当前玩家的输入
        if (TryGetInput(out PlayerMovementInput input))
        {
            // 处理水平移动
            var currentVelocity = rb.velocity;
            currentVelocity.x = input.Horizontal * 3f;
            rb.velocity = currentVelocity;

            // 处理跳跃(根据你的地面检测逻辑调整)
            if (input.Jump && IsPlayerGrounded())
            {
                rb.AddForce(Vector2.up * 6f, ForceMode2D.Impulse);
            }
        }
    }
}

// 示例地面检测方法(替换成你自己的实现)
private bool IsPlayerGrounded()
{
    // 这里写你的射线检测或碰撞器检测逻辑
    return Physics2D.Raycast(transform.position, Vector2.down, 0.2f, GroundLayer);
}

5. 修正本地玩家判断逻辑

如果需要区分本地玩家(比如显示UI),在Update中实时判断,避免一次性赋值的错误:

private bool isLocalPlayer;

private void Update()
{
    isLocalPlayer = Object.HasInputAuthority && Runner.IsLocalPlayer(Object.InputAuthority);
}

关键注意点

  • 永远不要在客户端直接修改网络同步的状态(比如Rigidbody的velocity),必须通过输入同步让权威端处理。
  • Host模式下,主机同时扮演服务器和客户端角色,所以要严格区分HasInputAuthority(客户端权限)和HasStateAuthority(服务器权限)。
  • UI按钮的输入必须在本地收集,然后通过OnInput发送给主机,由主机统一计算状态并同步给所有客户端。

内容的提问来源于stack exchange,提问作者Raj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 12:17:51