如何通过UNet或Mirror实现房间匹配与玩家同步?
游戏开发RAIN
2025年04月16日 14:48

在 Unity 中,使用 UNet(旧版) 或 Mirror(UNet 的现代化替代) 实现房间匹配与玩家同步,主要涉及以下核心流程:

1. 基础网络架构选择

  • UNet(已弃用,不推荐新项目)

  • Unity 原生的 HLAPI(High-Level API),功能有限,社区支持少。

  • Mirror(推荐)

  • 基于 UNet 重构,性能更好,支持更多传输层(TCP、WebSockets、SteamP2P 等),社区活跃。

安装 Mirror:通过 Unity Package Manager 或 GitHub 安装。

2. 实现房间匹配(Matchmaking)

Mirror 提供 NetworkManager 扩展,但房间匹配需自定义逻辑。常见方案:

方案 1:使用 Mirror 内置的 NetworkRoomManager

适用于简单房间匹配(如固定人数房间):

csharp

复制

using Mirror; public class MyRoomManager : NetworkRoomManager { public override void OnRoomServerConnect(NetworkConnection conn) { base.OnRoomServerConnect(conn); Debug.Log($"Player {conn.connectionId} joined the room."); } public override void OnRoomServerDisconnect(NetworkConnection conn) { base.OnRoomServerDisconnect(conn); Debug.Log($"Player {conn.connectionId} left the room."); } }

流程

  1. 客户端调用 StartClient() 连接服务器。

  2. 服务器通过 OnRoomServerConnect 管理玩家进入。

  3. 房间满员后自动切换至游戏场景(需配置 offlineScene 和 onlineScene)。

方案 2:自定义房间匹配(推荐)

更灵活,适合动态房间列表、最大玩家数限制等需求。

(1) 服务器端:房间管理

csharp 复制 using Mirror; using System.Collections.Generic; public class Room { public string Name; public List<NetworkConnection> Players = new List<NetworkConnection>(); public int MaxPlayers = 4; } public class CustomRoomManager : NetworkBehaviour { public static CustomRoomManager Instance; public List<Room> Rooms = new List<Room>(); private void Awake() { if (Instance == null) Instance = this; } // 客户端调用,请求加入房间 [Command(requiresAuthority = false)] public void CmdJoinRoom(string roomName, NetworkConnectionToClient conn = null) { Room targetRoom = Rooms.Find(r => r.Name == roomName); if (targetRoom == null) { targetRoom = new Room { Name = roomName }; Rooms.Add(targetRoom); } if (targetRoom.Players.Count < targetRoom.MaxPlayers) { targetRoom.Players.Add(conn); TargetOnJoinedRoom(conn, roomName); } else { TargetOnRoomFull(conn); } } [TargetRpc] private void TargetOnJoinedRoom(NetworkConnection conn, string roomName) { Debug.Log($"Joined room: {roomName}"); // 客户端逻辑:加载游戏场景等 } [TargetRpc] private void TargetOnRoomFull(NetworkConnection conn) { Debug.Log("Room is full!"); } }

(2) 客户端:加入房间

csharp 复制 using Mirror; using UnityEngine; public class PlayerLobby : MonoBehaviour { public void JoinRoom(string roomName) { NetworkClient.RegisterHandler<RoomJoinedMessage>(OnRoomJoined); CustomRoomManager.Instance.CmdJoinRoom(roomName); } private void OnRoomJoined(RoomJoinedMessage msg) { Debug.Log("Room joined successfully!"); } }

3. 玩家同步(Player Synchronization)

Mirror 提供多种同步方式:

(1) 同步变量 [SyncVar]

csharp 复制 public class Player : NetworkBehaviour { [SyncVar] public string playerName; [SyncVar(hook = nameof(OnHealthChanged))] public int health = 100; private void OnHealthChanged(int oldValue, int newValue) { Debug.Log($"Health changed from {oldValue} to {newValue}"); } }

(2) 远程调用 [Command] 和 [ClientRpc]

csharp 复制 public class Player : NetworkBehaviour { [Command] public void CmdShoot() { // 服务器验证逻辑 RpcOnShotFired(); } [ClientRpc] private void RpcOnShotFired() { // 所有客户端播放射击特效 PlayShootEffect(); } }

(3) 同步变换 NetworkTransform

csharp 复制 [RequireComponent(typeof(NetworkTransform))] public class PlayerMovement : NetworkBehaviour { public float speed = 5f; private void Update() { if (isLocalPlayer) { float moveX = Input.GetAxis("Horizontal"); float moveZ = Input.GetAxis("Vertical"); transform.Translate(new Vector3(moveX, 0, moveZ) * speed * Time.deltaTime); } } }

4. 完整流程示例

  1. 服务器启动

csharp 复制 public void StartServer() { NetworkManager.singleton.StartServer(); }

  1. 客户端连接并加入房间

csharp 复制 public void JoinGame() { NetworkManager.singleton.StartClient(); CustomRoomManager.Instance.CmdJoinRoom("Room1"); }

  1. 玩家同步

    • 使用 [SyncVar] 同步数据(如血量、名字)。

    • 使用 [Command] 和 [ClientRpc] 处理射击、技能等交互。

5. 进阶优化

  • 房间列表广播:使用 [TargetRpc] 或 NetworkDiscovery(局域网发现)。

  • 断线重连:记录玩家 netId 并恢复状态。

  • 权威服务器:关键逻辑(如伤害计算)在服务器执行,避免作弊。

总结

功能实现方式房间匹配NetworkRoomManager 或自定义 Room 类 + [Command]/[TargetRpc]。玩家同步[SyncVar]、[Command]、[ClientRpc] + NetworkTransform。数据传输优化使用 [SyncVar] 的 hook 减少带宽,压缩 NetworkMessage。

Mirror 的 API 设计类似 UNet,但更稳定、功能更全。推荐结合 FishNet 或 LiteNetLib 传输层进一步提升性能。