Unity Mirror多玩家卡牌游戏卡牌计数同步异常问题求助
问题分析
你的核心问题是每个Player实例都独立维护一套卡牌计数变量,包括主牌库deckCount、己方手牌myDeck、敌方手牌enemyDeck。这导致:
- 新玩家加入时,
OnStartClient会重置自己的计数为初始值(50/0/0),覆盖正确的同步状态 - 抽卡逻辑在客户端全量执行(
RpcSetDeck会让所有客户端各自修改本地计数),不同客户端的计数无法统一
SyncVar没生效是因为变量没有放在唯一的服务器权威源上,而是分散在每个Player对象中,自然无法跨玩家同步。
解决方案
把全局卡牌状态(主牌库、双方手牌数)移到一个服务器端的GameManager中,用SyncVar自动同步到所有客户端。Player只负责触发抽卡命令,由服务器统一处理逻辑并更新全局状态。
步骤1:创建服务器端GameManager
这个脚本仅在服务器运行,维护全局卡牌数据并同步到客户端:
using UnityEngine; using UnityEngine.Networking; public class GameManager : NetworkBehaviour { // 全局主牌库计数,SyncVar自动同步到所有客户端,变化时触发钩子更新UI [SyncVar(hook = nameof(OnDeckCountChanged))] public int globalDeckCount = 50; // 玩家1手牌数 [SyncVar(hook = nameof(OnPlayer1DeckChanged))] public int player1Deck = 0; // 玩家2手牌数 [SyncVar(hook = nameof(OnPlayer2DeckChanged))] public int player2Deck = 0; // 单例,方便Player直接调用 public static GameManager Instance; private void Awake() { Instance = this; } // 服务器端专属抽卡逻辑 [Server] public void HandleDrawCard(NetworkIdentity playerIdentity) { if (globalDeckCount <= 0) return; globalDeckCount--; // 根据玩家连接ID判断抽卡者身份 if (playerIdentity.connectionToClient.connectionId == 0) { player1Deck++; } else { player2Deck++; } } // 主牌库变化时更新所有客户端UI private void OnDeckCountChanged(int oldValue, int newValue) { GameObject.Find("Deck").GetComponent<UnityEngine.UI.Text>().text = "Deck: " + newValue; } // 玩家1手牌变化时触发UI更新 private void OnPlayer1DeckChanged(int oldValue, int newValue) { UpdatePlayerUI(newValue, player2Deck); } // 玩家2手牌变化时触发UI更新 private void OnPlayer2DeckChanged(int oldValue, int newValue) { UpdatePlayerUI(player1Deck, newValue); } // 根据本地玩家身份,动态分配"我的手牌"和"敌方手牌"显示内容 private void UpdatePlayerUI(int p1Count, int p2Count) { Player localPlayer = FindObjectOfType<Player>(); if (localPlayer == null) return; GameObject myHandText = GameObject.Find("Me"); GameObject enemyText = GameObject.Find("Enemy"); bool isLocalPlayer1 = localPlayer.connectionToClient.connectionId == 0; myHandText.GetComponent<UnityEngine.UI.Text>().text = "My Deck: " + (isLocalPlayer1 ? p1Count : p2Count); enemyText.GetComponent<UnityEngine.UI.Text>().text = "Enemy: " + (isLocalPlayer1 ? p2Count : p1Count); } }
步骤2:修改Player脚本
移除本地计数变量,改为调用GameManager的服务器端逻辑:
using UnityEngine; using UnityEngine.Networking; public class Player : NetworkBehaviour { private void Update() { // 仅本地玩家能触发抽卡操作 if (hasAuthority && Input.GetKeyDown("e")) { CmdDrawCard(); } } [Command] public void CmdDrawCard() { // 委托服务器端GameManager处理抽卡逻辑 GameManager.Instance.HandleDrawCard(this.netIdentity); } }
关键改动说明
- 统一权威源:所有卡牌计数由服务器端的GameManager维护,确保所有客户端状态完全一致
- SyncVar自动同步:无需手动发送Rpc更新UI,SyncVar会自动将服务器状态同步到所有客户端,并通过钩子触发UI更新
- 身份统一判断:通过玩家的
connectionId区分身份,避免每个Player自己维护敌我手牌的混乱逻辑 - 移除本地重置:删除Player中
OnStartClient的计数重置代码,改为从GameManager同步初始状态
额外注意事项
- 确保GameManager对象在服务器端生成(添加到NetworkManager的Spawnable Prefabs列表,或在服务器启动时手动实例化)
- 测试时先启动服务器,再加入客户端,避免
connectionId分配混乱 - 可给Player添加
[SyncVar] public int playerId;,在服务器端为每个Player分配唯一ID,替代connectionId判断(更稳定)
内容的提问来源于stack exchange,提问作者Twonk
相关产品推荐
相关产品推荐

