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

Unity Mirror网络中玩家名称同步异常问题求助

问题:Mirror多人赛车游戏玩家名称同步异常

首次创建服务器时,两名玩家的名称均显示为主机名称;重启服务器后客户端再加入,名称可正常同步。

玩家名称输入管理器脚本
using UnityEngine;
using UnityEngine.UI;
using Mirror;

public class PlayerNameInput : NetworkBehaviour
{
    [Header("UI")]
    [SerializeField] private InputField nameInputField = null;

    public static string DisplayName { get; private set; }

    private const string PlayerPrefsNameKey = "PlayerName";

    private void Start() => SetUpInputField();

    private void SetUpInputField()
    {
        if (!PlayerPrefs.HasKey(PlayerPrefsNameKey)) { return; }

        string defaultName = PlayerPrefs.GetString(PlayerPrefsNameKey);

        nameInputField.text = defaultName;
    }

    public void SavePlayerName()
    {
        DisplayName = nameInputField.text;

        PlayerPrefs.SetString(PlayerPrefsNameKey, DisplayName);
    }
}
玩家控制器脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class WheelController : NetworkBehaviour {

    public TextMesh NameMesh;

    [SyncVar(hook = nameof(OnNameChanged))]
    public string PlayerName;

    void OnNameChanged(string _Old, string _New)
    {
        if (!this.isLocalPlayer)
        {
            NameMesh.text = _New;
        }
    }

    [Command]
    public void CmdSetupPlayer(string _name)
    {
        PlayerName = _name;
    }

    public override void OnStartClient()
    {
        base.OnStartClient();
        string playerName = PlayerNameInput.DisplayName;
        CmdSetupPlayer(playerName);
        SetDisplayName(playerName);
    }

    [Server]
    public void SetDisplayName(string displayName)
    {
        PlayerName = displayName;
    }
}
问题原因与解决方案

核心问题分析

  1. 静态变量全局污染:PlayerNameInput.DisplayName是静态变量,主机进程内所有玩家对象(包括远程客户端的玩家实例)都会读取主机设置的名称值,导致远程玩家的名称被错误覆盖。
  2. OnStartClient执行范围错误:OnStartClient会在客户端的所有玩家对象上执行(本地+远程),当前代码中所有玩家都会触发名称设置逻辑,远程玩家实例在主机上执行时会用主机名称覆盖服务器端数据。
  3. 冗余服务器端调用:OnStartClient中直接调用[Server]标记的SetDisplayName,主机作为服务器会执行该方法,进一步加剧名称覆盖问题。

修复步骤

1. 限制名称同步逻辑仅本地玩家执行

修改WheelController的OnStartClient方法,只让本地玩家发送名称同步命令:

public override void OnStartClient()
{
    base.OnStartClient();
    // 仅本地玩家执行名称同步逻辑
    if (isLocalPlayer)
    {
        string playerName = PlayerNameInput.DisplayName;
        CmdSetupPlayer(playerName);
    }
}

2. 移除冗余的服务器端调用

删除OnStartClient中的SetDisplayName(playerName);行,CmdSetupPlayer已经在服务器端完成PlayerName的设置,无需额外调用。

3. 修正名称显示逻辑

原钩子仅更新非本地玩家名称,导致本地玩家名称无法显示,去掉判断条件:

void OnNameChanged(string _Old, string _New)
{
    NameMesh.text = _New;
}

4. 优化名称输入脚本

将PlayerNameInput改为普通MonoBehaviour(无需继承NetworkBehaviour,UI输入仅本地处理),并初始化静态变量:

using UnityEngine;
using UnityEngine.UI;

public class PlayerNameInput : MonoBehaviour
{
    [Header("UI")]
    [SerializeField] private InputField nameInputField = null;

    public static string DisplayName { get; private set; }

    private const string PlayerPrefsNameKey = "PlayerName";

    private void Start() => SetUpInputField();

    private void SetUpInputField()
    {
        if (!PlayerPrefs.HasKey(PlayerPrefsNameKey)) { return; }

        string defaultName = PlayerPrefs.GetString(PlayerPrefsNameKey);

        nameInputField.text = defaultName;
        DisplayName = defaultName; // 初始化静态变量
    }

    public void SavePlayerName()
    {
        DisplayName = nameInputField.text;
        PlayerPrefs.SetString(PlayerPrefsNameKey, DisplayName);
    }
}

验证逻辑

  • 主机启动服务器后,本地玩家通过OnStartClient发送自身名称到服务器,服务器同步至所有客户端。
  • 客户端加入时,仅自身本地玩家发送名称命令,主机进程中的远程玩家实例不会执行该逻辑,避免名称覆盖。
  • 所有客户端(包括主机)通过SyncVar钩子正确显示所有玩家的名称。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:54:54