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; } }
问题原因与解决方案
核心问题分析
- 静态变量全局污染:
PlayerNameInput.DisplayName是静态变量,主机进程内所有玩家对象(包括远程客户端的玩家实例)都会读取主机设置的名称值,导致远程玩家的名称被错误覆盖。 OnStartClient执行范围错误:OnStartClient会在客户端的所有玩家对象上执行(本地+远程),当前代码中所有玩家都会触发名称设置逻辑,远程玩家实例在主机上执行时会用主机名称覆盖服务器端数据。- 冗余服务器端调用:
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
相关产品推荐
相关产品推荐

