Unity Mirror:如何等待Cmd命令执行完成后再执行本地方法
解决多人背包系统中Cmd方法同步等待问题
方案1:利用服务器回调通知(适配Unity Netcode/UNet)
核心思路是让服务器在执行完CmdAddItemToDatabase或CmdChangeItemAmount后,主动给发起请求的客户端发送确认信号,客户端收到信号后再继续后续逻辑。
以UNet为例的代码实现:
private bool _awaitingServerConfirm; // 客户端入口方法 public void AddItem(Item targetItem) { StartCoroutine(ProcessAddItem(targetItem)); } private IEnumerator ProcessAddItem(Item targetItem) { // 先做本地插槽校验 if (!CanAddItem(targetItem)) yield break; _awaitingServerConfirm = true; // 调用对应Cmd方法 if (HasSameItemInSlot(targetItem)) { CmdChangeItemAmount(targetItem); } else { CmdAddItemToDatabase(targetItem); } // 等待服务器确认 while (_awaitingServerConfirm) { yield return null; } // 这里执行添加完成后的后续逻辑(比如处理下一件物品) } // 服务器端执行完操作后,给发起请求的客户端发确认 [TargetRpc] private void TargetNotifyAddComplete(NetworkConnection target) { _awaitingServerConfirm = false; } // 在Cmd方法末尾调用确认Rpc [Command] private void CmdAddItemToDatabase(Item item) { // 执行添加至SyncList的逻辑 TargetNotifyAddComplete(connectionToClient); } [Command] private void CmdChangeItemAmount(Item item) { // 执行修改物品数量的逻辑 TargetNotifyAddComplete(connectionToClient); }
方案2:监听SyncList的变更事件
SyncList自带OnChanged事件,可通过监听这个事件来判断服务器的同步操作是否完成,以此触发后续逻辑。
代码示例:
private SyncList<Item> _backpackSlots; private Queue<Item> _pendingItems = new Queue<Item>(); private bool _isProcessing = false; private void Awake() { _backpackSlots.OnChanged += OnBackpackSlotsUpdated; } // 客户端调用的添加方法 public void AddItem(Item item) { _pendingItems.Enqueue(item); if (!_isProcessing) { ProcessNextPendingItem(); } } private void ProcessNextPendingItem() { if (_pendingItems.Count == 0) { _isProcessing = false; return; } _isProcessing = true; var currentItem = _pendingItems.Peek(); if (CanAddItem(currentItem)) { if (HasSameItemInSlot(currentItem)) { CmdChangeItemAmount(currentItem); } else { CmdAddItemToDatabase(currentItem); } } else { _pendingItems.Dequeue(); ProcessNextPendingItem(); } } // 监听SyncList变更,确认操作完成后继续处理下一件 private void OnBackpackSlotsUpdated(SyncList<Item>.Operation op, int index, Item oldItem, Item newItem) { if ((op == SyncList<Item>.Operation.Add || op == SyncList<Item>.Operation.Set) && _isProcessing) { _pendingItems.Dequeue(); ProcessNextPendingItem(); } }
方案3:使用Netcode的ServerRpc回调(Unity Netcode for GameObjects)
如果用的是新版Netcode,ServerRpc支持直接传递回调委托,服务器执行完后可直接触发客户端的回调逻辑:
// 客户端添加方法 public void AddItem(ItemData item) { if (CanAddItem(item)) { AddItemServerRpc(item, OnAddItemFinished); } } [ServerRpc(RequireOwnership = true)] private void AddItemServerRpc(ItemData item, ServerRpcParams rpcParams) { // 执行添加或修改数量逻辑 if (HasSameItem(item)) { UpdateItemAmount(item); } else { AddItemToSyncList(item); } // 触发客户端回调 AddItemCompleteClientRpc(rpcParams.Receive.SenderClientId); } [ClientRpc] private void AddItemCompleteClientRpc(ulong clientId) { if (NetworkManager.Singleton.LocalClientId == clientId) { OnAddItemFinished(); } } // 后续逻辑的回调方法 private void OnAddItemFinished() { // 处理下一件物品或后续流程 }
内容的提问来源于stack exchange,提问作者Draas
相关产品推荐
相关产品推荐

