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

Host-Client架构双人游戏OnServerAddPlayer玩家分配异常问题

问题诊断与修复方案

核心问题分析

两次调用AssignPlayer都进入P1分支,说明服务器端的Governor单例中P1字段在第二次调用时仍为空,大概率是以下两种情况:

  • 单例未在服务器端正确初始化:Governor作为单例,可能在客户端也被实例化,导致服务器端的单例未被正确引用,或者初始化时机错误,被客户端实例覆盖。
  • OnServerAddPlayer执行上下文错误:虽然预期该方法仅在服务器端执行,但实际可能由于逻辑疏漏,客户端也触发了该方法,导致客户端的Governor单例被修改,服务器端的单例未得到正确更新。

具体修复步骤

1. 确保Governor单例仅在服务器端生效

修改Governor的单例逻辑,只在服务器/主机端保留实例,客户端直接销毁:

public class Governor : MonoBehaviour
{
    public static Governor Instance { get; private set; }
    public NetworkIdentity P1;
    public NetworkIdentity P2;

    private void Awake()
    {
        // 非服务器/主机端直接销毁实例
        if (!NetworkServer.active)
        {
            Destroy(gameObject);
            return;
        }
        
        // 确保单例唯一
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
    }
}

2. 严格限制OnServerAddPlayer的执行上下文

在NetworkGovernor中,先确认当前处于服务器端再执行逻辑,同时修正玩家身份获取方式:

public class NetworkGovernor : NetworkManager
{
    public override void OnServerAddPlayer(NetworkConnectionToClient conn, short playerControllerId, NetworkReader reader)
    {
        // 强制限定仅服务器端执行
        if (!NetworkServer.active) return;
        
        base.OnServerAddPlayer(conn, playerControllerId, reader);
        
        // 从连接中获取当前玩家的NetworkIdentity
        var playerIdentity = conn.playerControllers[playerControllerId].gameObject.GetComponent<NetworkIdentity>();
        AssignPlayer(playerIdentity);
        
        // 检查两名玩家是否都已加入,且身份分配完成
        if (NetworkServer.connections.Count >= 2 && Governor.Instance.P1 != null && Governor.Instance.P2 != null)
        {
            var p1Manager = Governor.Instance.P1.GetComponent<PlayerManager>();
            var p2Manager = Governor.Instance.P2.GetComponent<PlayerManager>();
            
            p1Manager.RpcLoadData();
            p2Manager.RpcLoadData();
            p1Manager.RpcStartSequence();
            p2Manager.RpcStartSequence();
        }
    }

    // 标记为仅服务器端执行
    [Server]
    private void AssignPlayer(NetworkIdentity player)
    {
        if (Governor.Instance == null) return;
        
        if (Governor.Instance.P1 == null)
        {
            Governor.Instance.P1 = player;
            Debug.Log("分配玩家1");
        }
        else if (Governor.Instance.P2 == null)
        {
            Governor.Instance.P2 = player;
            Debug.Log("分配玩家2");
        }
    }
}

3. 验证玩家连接状态的正确性

依赖NetworkServer.connections判断已连接玩家数(主机作为服务器+客户端,connections.Count为2时代表两名玩家均已加入),避免自定义存储的疏漏。

4. 添加空值断言避免异常

在初始化流程前添加断言,提前定位问题:

Debug.Assert(Governor.Instance.P1 != null, "玩家1未正确分配");
Debug.Assert(Governor.Instance.P2 != null, "玩家2未正确分配");

验证步骤

  1. 启动主机后,查看服务器端日志,确认Governor的Awake仅执行一次。
  2. 加入客户端后,检查服务器端是否依次输出“分配玩家1”和“分配玩家2”日志。
  3. 触发初始化流程时,确认无空指针异常,两名玩家的Rpc方法正常调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:08:12