Unity-Mirror网络框架-从入门到精通之Basic示例
文章目录
- 前言
- Basic示例
- 场景元素
- 预制体元素
- 代码逻辑
- BasicNetManager
- Player逻辑
- SyncVars属性
- Server逻辑
- Client逻辑
- PlayerUI逻辑
- 最后
前言
在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人游戏开发设计。它使得开发者能够轻松实现网络连接、数据同步和游戏状态管理。本文将深入介绍Mirror的基本概念、如何与其他网络框架进行比较,以及如何从零开始创建一个使用Mirror的简单网络项目。

Basic示例
运行结果如下:

Basic示例是Mirror的最简单的示例,就展示了一个Player玩家的登录UI状态,
但是它却反应了Mirror框架的所有使用流程,可谓麻雀虽小,五脏俱全。
也从正面反应了,Mirror的简单易用性。
场景元素
我们打开场景,可以看到Basic场景中元素很少,除了一个NetworkManager,就是一个MainPanel的UI页面。
NetworkManager:即Mirror启动所需的网络管理器,有且必须有一个。
MainPanel:是用于存放玩家登录后的玩家UI图标的容器而已。

预制体元素
Basic场景用到的预制体,只有两个,
Player:代表登录后的玩家实体,如果玩家有移动,战斗等逻辑,就在它上面写
PlayerUI:代表玩家登录后,在UI显示的玩家图标,一个Player对应一个PlayerUI。

代码逻辑
BasicNetManager
namespace Mirror.Examples.Basic
{[AddComponentMenu("")]public class BasicNetManager : NetworkManager{public override void OnServerAddPlayer(NetworkConnectionToClient conn){base.OnServerAddPlayer(conn);Player.ResetPlayerNumbers();}public override void OnServerDisconnect(NetworkConnectionToClient conn){base.OnServerDisconnect(conn);Player.ResetPlayerNumbers();}}
}
BasicNetManager,继承了NetworkManager,因为NetworkManager只是包括了网络管理器的一些基本功能,比如:保持连接,维持NetworkIdentity的同步等基本网络同步功能。
Mirror给我们提供的很多Sample示例,其实都重写了NetworkManager,给我们展示了实现不同类型的项目,如何扩展NetworkManager。
NetworkManager内部有很多的生命周期函数,我们可以重写,并实现自己的目的。
比如这里,重写了OnServerAddPlayer和OnServerDisconnect。分别代表,客户端玩家连接上服务器和断开连接服务器。然后调用Player的函数,更新玩家Number显示。
Player逻辑
Player继承自NetworkBehaviour,是一个网络单元的行为组件。
我们都知道Unity是组件式编程,任何游戏对象如果想实现游戏逻辑,都可以由N个组件组成。
同理,在Mirror中,任何物体想要被同步,必须添加NetworkIdentity组件,代表是一个网络单元,然后只要这个网路单元,想实现其他的同步逻辑,比如属性,RPC调用等行为,就必须添加NetworkBehaviour组件才能实现, 而Player就是一个NetworkBehaviour,实现了Player应有的属性同步以及UI图标刷新功能。
SyncVars属性
Mirror提供了一种非常便利的属性同步机制,即[SyncVar],只要一个属性添加了SyncVar属性,就可以在不同的客户端之间实现同步。
如果添加了Hook参数,还可以实现参数变化时的函数回调。如下代码所示:
playerNumber
playerColor
playerData
这三个参数是可以在网络中进行同步的,且修改后,每个玩家的UI图标的数值都会发生自动更新。
#region SyncVars[Header("SyncVars")]/// <summary>/// This is appended to the player name text, e.g. "Player 01"/// </summary>[SyncVar(hook = nameof(PlayerNumberChanged))]public byte playerNumber = 0;/// <summary>/// Random color for the playerData text, assigned in OnStartServer/// </summary>[SyncVar(hook = nameof(PlayerColorChanged))]public Color32 playerColor = Color.white;/// <summary>/// This is updated by UpdateData which is called from OnStartServer via InvokeRepeating/// </summary>[SyncVar(hook = nameof(PlayerDataChanged))]public ushort playerData = 0;// This is called by the hook of playerNumber SyncVar abovevoid PlayerNumberChanged(byte _, byte newPlayerNumber){OnPlayerNumberChanged?.Invoke(newPlayerNumber);}// This is called by the hook of playerColor SyncVar abovevoid PlayerColorChanged(Color32 _, Color32 newPlayerColor){OnPlayerColorChanged?.Invoke(newPlayerColor);}// This is called by the hook of playerData SyncVar abovevoid PlayerDataChanged(ushort _, ushort newPlayerData){OnPlayerDataChanged?.Invoke(newPlayerData);}#endregion
Server逻辑
Server逻辑什么意思呢?为什么要拆分Server和Client逻辑呢?
这就要在此强调下Mirror的网络架构了,我再开篇的:Unity-Mirror网络框架-从入门到精通之Mirror简介中,专门介绍了Mirror的网络结构和传统的CS网络结构的差异,我们不能像写传统的CS架构下客户端一样
去写Mirror框架中的逻辑。因为Mirror框架中,我们写的代码实际是包含了服务器和客户端,双端逻辑的,这样就省去了一个Server端。只是我们在开发时要注意。有些逻辑需要写在Server中,有些需要写在Client中。这一点是非常重要的。
如下代码所示:
可以理解为这些逻辑属于服务器执行的逻辑。
OnStartServer,就是当一个客户端玩家,在服务器上被激活时,一般用于初始化一些同步网络属性,默认情况下SyncVar属性的值,只能在Server下被修改,然后同步给所有客户端。
[ServerCallback]属性修饰了两个函数ResetPlayerNumber和UpdateData,
代表这两个函数,只能在Server端被调用,客户端是 不能调用的。
#region Server/// <summary>/// This is invoked for NetworkBehaviour objects when they become active on the server./// <para>This could be triggered by NetworkServer.Listen() for objects in the scene, or by NetworkServer.Spawn() for objects that are dynamically created.</para>/// <para>This will be called for objects on a "host" as well as for object on a dedicated server.</para>/// </summary>public override void OnStartServer(){base.OnStartServer();// Add this to the static Players ListplayersList.Add(this);// set the Player Color SyncVarplayerColor = Random.ColorHSV(0f, 1f, 0.9f, 0.9f, 1f, 1f);// set the initial player dataplayerData = (ushort)Random.Range(100, 1000);// Start generating updatesInvokeRepeating(nameof(UpdateData), 1, 1);}// This is called from BasicNetManager OnServerAddPlayer and OnServerDisconnect// Player numbers are reset whenever a player joins / leaves[ServerCallback]internal static void ResetPlayerNumbers(){byte playerNumber = 0;foreach (Player player in playersList)player.playerNumber = playerNumber++;}// This only runs on the server, called from OnStartServer via InvokeRepeating[ServerCallback]void UpdateData(){playerData = (ushort)Random.Range(100, 1000);}/// <summary>/// Invoked on the server when the object is unspawned/// <para>Useful for saving object data in persistent storage</para>/// </summary>public override void OnStopServer(){CancelInvoke();playersList.Remove(this);}#endregion
Client逻辑
Client逻辑,就是纯粹的客户端显示逻辑。
比如:
OnStartClient.,当玩家在客户端激活时,创建一个指定的UI显示玩家信息
OnStartLocalPlayer:当本机玩家连接成功后,显示画布UI,代表自己登录成功了
#region Client/// <summary>/// Called on every NetworkBehaviour when it is activated on a client./// <para>Objects on the host have this function called, as there is a local client on the host. The values of SyncVars on object are guaranteed to be initialized correctly with the latest state from the server when this function is called on the client.</para>/// </summary>public override void OnStartClient(){// Instantiate the player UI as child of the Players PanelplayerUIObject = Instantiate(playerUIPrefab, CanvasUI.GetPlayersPanel());playerUI = playerUIObject.GetComponent<PlayerUI>();// wire up all events to handlers in PlayerUIOnPlayerNumberChanged = playerUI.OnPlayerNumberChanged;OnPlayerColorChanged = playerUI.OnPlayerColorChanged;OnPlayerDataChanged = playerUI.OnPlayerDataChanged;// Invoke all event handlers with the initial data from spawn payloadOnPlayerNumberChanged.Invoke(playerNumber);OnPlayerColorChanged.Invoke(playerColor);OnPlayerDataChanged.Invoke(playerData);}/// <summary>/// Called when the local player object has been set up./// <para>This happens after OnStartClient(), as it is triggered by an ownership message from the server. This is an appropriate place to activate components or functionality that should only be active for the local player, such as cameras and input.</para>/// </summary>public override void OnStartLocalPlayer(){// Set isLocalPlayer for this Player in UI for background shadingplayerUI.SetLocalPlayer();// Activate the main panelCanvasUI.SetActive(true);}/// <summary>/// Called when the local player object is being stopped./// <para>This happens before OnStopClient(), as it may be triggered by an ownership message from the server, or because the player object is being destroyed. This is an appropriate place to deactivate components or functionality that should only be active for the local player, such as cameras and input.</para>/// </summary>public override void OnStopLocalPlayer(){// Disable the main panel for local playerCanvasUI.SetActive(false);}/// <summary>/// This is invoked on clients when the server has caused this object to be destroyed./// <para>This can be used as a hook to invoke effects or do client specific cleanup.</para>/// </summary>public override void OnStopClient(){// disconnect event handlersOnPlayerNumberChanged = null;OnPlayerColorChanged = null;OnPlayerDataChanged = null;// Remove this player's UI objectDestroy(playerUIObject);}#endregion
PlayerUI逻辑
PlayerUI比较简单,当Player属性发行变化时,会调用对应的函数,更新PlayerUI显示。
Player中有对应的事件绑定
// Instantiate the player UI as child of the Players PanelplayerUIObject = Instantiate(playerUIPrefab, CanvasUI.GetPlayersPanel());playerUI = playerUIObject.GetComponent<PlayerUI>();// wire up all events to handlers in PlayerUIOnPlayerNumberChanged = playerUI.OnPlayerNumberChanged;OnPlayerColorChanged = playerUI.OnPlayerColorChanged;OnPlayerDataChanged = playerUI.OnPlayerDataChanged;
PlayerUI中只需要更新Text的显示即可
namespace Mirror.Examples.Basic
{public class PlayerUI : MonoBehaviour{[Header("Player Components")]public Image image;[Header("Child Text Objects")]public Text playerNameText;public Text playerDataText;// Sets a highlight color for the local playerpublic void SetLocalPlayer(){// add a visual background for the local player in the UIimage.color = new Color(1f, 1f, 1f, 0.1f);}// This value can change as clients leave and joinpublic void OnPlayerNumberChanged(byte newPlayerNumber){playerNameText.text = string.Format("Player {0:00}", newPlayerNumber);}// Random color set by Player::OnStartServerpublic void OnPlayerColorChanged(Color32 newPlayerColor){playerNameText.color = newPlayerColor;}// This updates from Player::UpdateData via InvokeRepeating on serverpublic void OnPlayerDataChanged(ushort newPlayerData){// Show the data in the UIplayerDataText.text = string.Format("Data: {0:000}", newPlayerData);}}
}
最后
最后运行成功后,会显示玩家编号和信息,代表玩家连接成功。
在此基础上,我们可以自己发挥想象力扩展一下如:
玩家昵称,在PlayeUI上显示。
用一个模型代表玩家身体,可以再Player中做玩家的位移等。

好了,这篇文章就到这里,希望对你有所帮助
相关文章:
Unity-Mirror网络框架-从入门到精通之Basic示例
文章目录 前言Basic示例场景元素预制体元素代码逻辑BasicNetManagerPlayer逻辑SyncVars属性Server逻辑Client逻辑 PlayerUI逻辑 最后 前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人…...
CSS 图片廊:网页设计的艺术与技巧
CSS 图片廊:网页设计的艺术与技巧 引言 在网页设计中,图片廊是一个重要的组成部分,它能够以视觉吸引的方式展示图片集合,增强用户的浏览体验。CSS(层叠样式表)作为网页设计的主要语言之一,提供…...
AI 发展的第一驱动力:人才引领变革
在科技蓬勃发展的当下,AI 成为了时代的焦点,然而其发展并非一帆风顺,究竟什么才是推动 AI 持续前行的关键力量呢? 目录 AI 发展现状剖析 期望与现实的落差 落地困境根源 人才:AI 发展的核心动力编辑 技术突破的…...
[创业之路-229]:《华为闭环战略管理》-5-平衡记分卡与战略地图
目录 一、平衡记分卡 1. 财务角度: 2. 客户角度: 3. 内部运营角度: 4. 学习与成长角度: 二、BSC战略地图 1、核心内容 2、绘制目的 3、绘制方法 4、注意事项 一、平衡记分卡 平衡记分卡(Balanced Scorecard&…...
用uniapp写一个播放视频首页页面代码
效果如下图所示 首页有导航栏,搜索框,和视频列表, 导航栏如下图 搜索框如下图 视频列表如下图 文件目录 视频首页页面代码如下 <template> <view class"video-home"> <!-- 搜索栏 --> <view class…...
【视觉SLAM:八、后端Ⅰ】
视觉SLAM的后端主要解决状态估计问题,它是优化相机轨迹和地图点的过程,从数学上看属于非线性优化问题。后端的目标是结合传感器数据,通过最优估计获取系统的状态(包括相机位姿和场景结构),在状态估计过程中…...
PaddleOCROCR关键信息抽取训练过程
步骤1:python版本3.8.20 步骤2:下载代码,安装依赖 git clone https://gitee.com/PaddlePaddle/PaddleOCR.git pip uninstall opencv-python -y # 安装PaddleOCR的依赖 ! pip install -r requirements.txt # 安装关键信息抽取任务的依赖 !…...
用Python操作字节流中的Excel文档
Python能够轻松地从字节流中加载文件,在不依赖于外部存储的情况下直接对其进行读取、修改等复杂操作,并最终将更改后的文档保存回字节串中。这种能力不仅极大地提高了数据处理的灵活性,还确保了数据的安全性和完整性,尤其是在网络…...
python 桶排序(Bucket Sort)
桶排序(Bucket Sort) 桶排序是一种分布式排序算法,适用于对均匀分布的数据进行排序。它的基本思想是:将数据分到有限数量的桶中,每个桶分别排序,最后将所有桶中的数据合并。 桶排序的步骤: 划…...
Elasticsearch:探索 Elastic 向量数据库的深度应用
Elasticsearch:探索 Elastic 向量数据库的深度应用 一、Elasticsearch 向量数据库简介 1. Elasticsearch 向量数据库的概念 Elasticsearch 本身是一个基于 Lucene 的搜索引擎,提供了全文搜索和分析的功能。随着技术的发展,Elasticsearch 也…...
【每日学点鸿蒙知识】属性变量key、waterflow卡顿问题、包无法上传、Video控件播放视频、Vue类似语法
1、HarmonyOS 属性变量常量是否可以作为object对象的key? a: object new Object() this.a[Constants.TEST_KEY] "456" 可以先定义,再赋值 2、首页点击回到waterflow的首节点,0~index全部节点被重建,导致卡顿 使用s…...
小程序中引入echarts(保姆级教程)
hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的在校大学生…...
基于 Node.js 的 ORM(对象关系映射)工具——Sequelize介绍与使用,并举案例分析
便捷性介绍 支持多种数据库,包括 PostgreSQL、MySQL、MariaDB、SQLite 和 Microsoft SQL Server。Sequelize 提供了丰富的功能,帮助开发者用 JavaScript(或 TypeScript)代码操作数据库,而无需直接书写 SQL 语句。 Se…...
python 插入排序(Insertion Sort)
插入排序(Insertion Sort) 插入排序是一种简单的排序算法。它的基本思想是:将数组分为已排序部分和未排序部分,然后逐个将未排序部分的元素插入到已排序部分的正确位置。插入排序类似于整理扑克牌的过程。 插入排序的步骤&#…...
电子应用设计方案81:智能AI冲奶瓶系统设计
智能 AI 冲奶瓶系统设计 一、引言 智能 AI 冲奶瓶系统旨在为父母或照顾者提供便捷、准确和卫生的冲奶服务,特别是在夜间或忙碌时,减轻负担并确保婴儿获得适宜的营养。 二、系统概述 1. 系统目标 - 精确调配奶粉和水的比例,满足不同年龄段婴…...
JAVA高并发总结
JAVA高并发编程总结 在现代应用中,高并发编程是非常重要的一部分,尤其是在分布式系统、微服务架构、实时数据处理等领域。Java 提供了丰富的并发工具和技术,帮助开发者在多线程和高并发的场景下提高应用的性能和稳定性。以下是 Java 高并发编…...
【AIGC】使用Java实现Azure语音服务批量转录功能:完整指南
文章目录 引言技术背景环境准备详细实现1. 基础架构设计2. 实现文件上传功能3. 提交转录任务crul4. 获取转录结果 使用示例结果示例最佳实践与注意事项总结 引言 在当今数字化时代,将音频内容转换为文本的需求越来越普遍。无论是会议记录、视频字幕生成,…...
arcgis模版空库怎么用(一)
这里以某个项目的数据为例: 可以看到,属性表中全部只有列标题,无数据内容 可能有些人会认为空库是用来往里面加入信息的,其实不是,正确的用法如下: 一、下图是我演示用的数据,我们可以看到其中…...
【电机控制】基于STC8H1K28的六步换向——方波驱动(软件篇)
【电机控制】基于STC8H1K28的六步换向——方波驱动(软件篇) 文章目录 [TOC](文章目录) 前言一、main.c二、GPIO.c三、PWMA.c四、ADC.c五、CMP.c六、Timer.c七、PMSM.c八、参考资料总结 前言 【电机控制】STC8H无感方波驱动—反电动势过零检测六步换向法 …...
小程序配置文件 —— 13 全局配置 - window配置
全局配置 - window配置 这里讲解根目录 app.json 中的 window 字段,window 字段用于设置小程序的状态栏、导航条、标题、窗口背景色; 状态栏:顶部位置,有网络信号、时间信息、电池信息等;导航条:有一个当…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
