用Mirror+Unity快速打造多人同步方块对战游戏
第一次接触网络同步功能的Unity开发者,往往会被各种理论概念和复杂架构吓退。Mirror框架的出现,让多人游戏开发变得前所未有的简单——今天我们就用30分钟,从零开始实现一个可运行的多人同步方块对战Demo。不需要预先理解所有网络原理,跟着做就能看到实时同步效果!
1. 环境准备与Mirror安装
在Unity Hub中创建全新的3D项目(建议命名为"MirrorCubeDemo"),确保使用较新的Unity版本(2021 LTS或更高)。打开项目后,通过Package Manager安装Mirror:
- 菜单栏选择Window > Package Manager
- 点击左上角"+"选择Add package from git URL
- 输入Mirror的Git地址:
https://github.com/vis2k/Mirror.git#upm - 等待安装完成后,检查已安装包列表确认Mirror版本
提示:若遇到安装错误,可尝试先安装依赖项
com.unity.nuget.newtonsoft-json
安装完成后,Assets目录下会出现Mirror文件夹。为快速验证安装成功,可以:
// 在任意脚本中添加测试代码 using Mirror; Debug.Log($"Mirror版本: {Mirror.Version}");2. 创建基础网络架构
在场景中右键创建空对象命名为"NetworkManager",为其添加两个关键组件:
- Network Manager:核心管理组件
- KCP Transport:推荐的高效传输协议
调整NetworkManager配置参数:
// 推荐设置(可在Inspector中修改) maxConnections = 4; // 最大玩家数 playerPrefab = Resources.Load<GameObject>("PlayerCube"); // 稍后创建创建简单的地面平面(Plane)并添加网格碰撞器,这将作为我们的对战舞台。设置位置为(0,0,0),缩放为(5,1,5)形成足够大的区域。
3. 制作可同步的玩家方块
在Prefabs文件夹创建新预制体"PlayerCube",为其添加以下组件:
- Network Identity(必需):标记为网络对象
- Network Transform:自动同步位置/旋转
- Box Collider:添加物理碰撞
- Rigidbody:启用重力效果
为立方体创建简单材质并设置鲜艳颜色(如红色),便于区分玩家。关键配置参数:
| 组件 | 关键参数 | 推荐值 |
|---|---|---|
| NetworkTransform | syncInterval | 0.1 |
| Rigidbody | isKinematic | false |
| BoxCollider | size | (1,1,1) |
回到NetworkManager,将PlayerCube拖入Registered Spawnable Prefabs列表,或点击Populate按钮自动注册所有带NetworkIdentity的预制体。
注意:Populate可能遗漏某些预制体,建议手动检查确认
4. 实现玩家移动同步逻辑
创建新C#脚本"PlayerController"并继承NetworkBehaviour:
using Mirror; using UnityEngine; public class PlayerController : NetworkBehaviour { [SerializeField] float moveSpeed = 8f; Rigidbody rb; void Start() { rb = GetComponent<Rigidbody>(); if (isLocalPlayer) { Camera.main.transform.SetParent(transform); Camera.main.transform.localPosition = new Vector3(0, 3, -5); } } void Update() { if (!isLocalPlayer) return; float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); Vector3 move = new Vector3(h, 0, v) * moveSpeed; rb.velocity = move; } }将此脚本附加到PlayerCube预制体上。代码关键点解析:
isLocalPlayer判断确保只控制自己的角色- 摄像机跟随逻辑让每个玩家有自己的视角
- Rigidbody物理移动比直接修改Transform更真实
5. 添加简单对战功能
让我们扩展功能,使方块可以"攻击"其他玩家——被撞击的玩家会被弹飞。修改PlayerController脚本:
[ServerCallback] void OnCollisionEnter(Collision other) { if (other.gameObject.CompareTag("Player")) { Vector3 force = (other.transform.position - transform.position).normalized * 15f; other.gameObject.GetComponent<Rigidbody>().AddForce(force, ForceMode.Impulse); } } [Command] public void CmdChangeColor(Color newColor) { RpcUpdateColor(newColor); } [ClientRpc] void RpcUpdateColor(Color newColor) { GetComponent<Renderer>().material.color = newColor; }在Update方法中添加颜色切换测试:
if (Input.GetKeyDown(KeyCode.Space)) { CmdChangeColor(Random.ColorHSV()); }6. 运行与测试技巧
点击Play按钮前,在NetworkManager组件选择运行模式:
- Host模式:同时作为服务器和客户端(适合本地测试)
- Client模式:连接到指定服务器
- Server模式:仅作为服务器运行
测试多人效果的最佳实践:
- 菜单栏选择File > Build Settings
- 添加当前场景,勾选"Development Build"
- 点击"Build And Run"创建独立客户端
- 原编辑器窗口以Host模式运行
- 观察两个窗口中的方块同步情况
调试网络问题时,可以启用Mirror的详细日志:
// 在初始化代码中添加 Debug.unityLogger.logEnabled = true; NetworkManager.singleton.logLevel = LogLevel.Verbose;7. 常见问题解决方案
预制体未同步问题:
- 确认所有客户端都有相同预制体
- 检查NetworkManager中的注册列表
- 确保预制体有唯一的NetworkIdentity
移动延迟明显:
- 降低NetworkTransform的syncInterval
- 尝试不同的Transport组件
- 检查物理模拟时间步长(Time.fixedDeltaTime)
命令/RPC不执行:
- 方法命名必须以Cmd/Rpc开头
- 参数必须是Mirror支持的基本类型
- 确保在正确的作用域调用(如Cmd只能在客户端调用)
在项目根目录创建NetworkManager.asset文件保存常用配置,方便团队共享设置。对于更复杂的游戏,建议采用场景网络加载:
// 在NetworkManager中设置 onlineScene = "GameScene"; offlineScene = "LobbyScene";8. 性能优化进阶技巧
当玩家数量增加时,需要优化网络流量:
- 优先级系统:
[SyncVar(hook = nameof(UpdatePriority))] int priority = 0; void UpdatePriority(int oldVal, int newVal) { GetComponent<NetworkProximityChecker>().visRange = newVal; }- 视野范围控制:
// 添加NetworkProximityChecker组件 checkInterval = 1.0f; visRange = 10f;- 数据压缩:
[SyncVar] [SerializeField] [Range(0, 63)] int compressedHealth;- 预测与插值:
// NetworkTransform组件中 interpolatePosition = true; interpolateRotation = true;对于移动同步,可以改用自定义的NetworkTransform脚本:
[SyncVar] Vector3 syncedPosition; void FixedUpdate() { if (isServer) { syncedPosition = transform.position; } else { transform.position = Vector3.Lerp(transform.position, syncedPosition, 0.2f); } }9. 扩展游戏功能思路
基于当前Demo,可以轻松扩展更多玩法:
- 得分系统:添加SyncVar记录玩家得分
- 特殊能力:使用RPC实现全屏特效
- 物品生成:用NetworkServer.Spawn实例化道具
- 房间系统:继承NetworkRoomManager创建大厅
例如实现简单的得分系统:
[SyncVar] int playerScore = 0; [ServerCallback] void OnTriggerEnter(Collider other) { if (other.CompareTag("Coin")) { playerScore++; NetworkServer.Destroy(other.gameObject); } }创建可同步的硬币预制体:
- 新建Sphere并添加NetworkIdentity
- 添加以下脚本:
public class Coin : NetworkBehaviour { [ServerCallback] void Start() { Invoke(nameof(Respawn), 10f); } [Server] void Respawn() { transform.position = new Vector3(Random.Range(-10,10), 0.5f, Random.Range(-10,10)); gameObject.SetActive(true); } }