在 Unity 中,使用 UNet(旧版) 或 Mirror(UNet 的现代化替代) 实现房间匹配与玩家同步,主要涉及以下核心流程:
UNet(已弃用,不推荐新项目)
Unity 原生的 HLAPI(High-Level API),功能有限,社区支持少。
Mirror(推荐)
基于 UNet 重构,性能更好,支持更多传输层(TCP、WebSockets、SteamP2P 等),社区活跃。
安装 Mirror:通过 Unity Package Manager 或 GitHub 安装。
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."); } }
流程:
客户端调用 StartClient() 连接服务器。
服务器通过 OnRoomServerConnect 管理玩家进入。
房间满员后自动切换至游戏场景(需配置 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!"); } }
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); } } }
服务器启动:
csharp 复制 public void StartServer() { NetworkManager.singleton.StartServer(); }
客户端连接并加入房间:
csharp 复制 public void JoinGame() { NetworkManager.singleton.StartClient(); CustomRoomManager.Instance.CmdJoinRoom("Room1"); }
玩家同步:
使用 [SyncVar] 同步数据(如血量、名字)。
使用 [Command] 和 [ClientRpc] 处理射击、技能等交互。
房间列表广播:使用 [TargetRpc] 或 NetworkDiscovery(局域网发现)。
断线重连:记录玩家 netId 并恢复状态。
权威服务器:关键逻辑(如伤害计算)在服务器执行,避免作弊。
功能实现方式房间匹配NetworkRoomManager 或自定义 Room 类 + [Command]/[TargetRpc]。玩家同步[SyncVar]、[Command]、[ClientRpc] + NetworkTransform。数据传输优化使用 [SyncVar] 的 hook 减少带宽,压缩 NetworkMessage。
Mirror 的 API 设计类似 UNet,但更稳定、功能更全。推荐结合 FishNet 或 LiteNetLib 传输层进一步提升性能。