Mirror官方案例操作

一、导入Mirror

在unity商城订阅Mirror https://assetstore.unity.com/packages/tools/network/mirror-129321

使用unity创建工程    (推荐版本:目前建议使用 Unity 2020 或 2021 LTS 版本;超出这些版本的可能可以运行,但用户需自行承担风险,尤其是预览版或测试版。)  并导入Mirror。

二、初步设置

新建一个场景并放在Build Settings里

新建一个空物体,命名为NetworkManager 并添加以下三个组件:

  • NetworkManager             ( 对整个网络游戏对象进行管理。)

  • KCPTransport (TelepathyTransport is older, you do not need KCP and Telepathy)    

  • NetworkManagerHUD      ( 一个启动的面板,方便客户端或服务器端启动停止。)

                NetworkManager组件中把场景拖拽到Offline 和 Online中(拖拽的场景必须在Build Settings里)

三、设置场景

添加一个简单的Plane 作为地面     设置 position(0,-1,0) 比例(2,2,2)

添加一个空物体 为他添加组件:NetworkStartPosition (作为出生点)复制多个放在Plane的各个角

四、创建玩家

创建一个胶囊  并添加组件:NetworkTransform(Reliable)  其中Sync Direction选项选为Client To Server                               自动会再添加一个NetworkIdentity

                        在 NetworkTransform勾选 Client Authority (我没找到)

                        重命名胶囊为:Player 新建PlayerScript代码挂在Player上

                        把Player这个物体拽成预制体,并在场景里删了他

  Network Manager中的Player Prefab选择Player                      

五、PlayerScript

添加以下代码到playerscript里

using Mirror;
using UnityEngine;

namespace QuickStart
{
    public class PlayerScript : NetworkBehaviour
    {
        public override void OnStartLocalPlayer()
        {
            Camera.main.transform.SetParent(transform);
            Camera.main.transform.localPosition = new Vector3(0, 0, 0);
        }

        void Update()
        {
            if (!isLocalPlayer) { return; }  //如果不是本地玩家就return

            float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;
            float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;

            transform.Rotate(0, moveX, 0);
            transform.Translate(0, 0, moveZ);
        }
    }
}

六、第一次play

点击Play 然后点击左上角 Host (server + client)测试人物移动   成功后可以发布一版本联机试试效果。

七、添加玩家名称

在player预制体中,创建一个空的GameObject

                                                                        命名FloatingInfo

                                                                        y轴位置设置为1.5

                                                                        缩放x轴 -1

                                添加3DText子物体

八、更新PlayerScripts代码

using Mirror;
using UnityEngine;

namespace QuickStart
{
    public class PlayerScript : NetworkBehaviour
    {
        public TextMesh playerNameText;
        public GameObject floatingInfo;

        private Material playerMaterialClone;

        [SyncVar(hook = nameof(OnNameChanged))]//当playerName变了之后调用OnNameChange
        public string playerName;

        [SyncVar(hook = nameof(OnColorChanged))]
        public Color playerColor = Color.white;

        void OnNameChanged(string _Old, string _New)
        {
            playerNameText.text = playerName;
        }

        void OnColorChanged(Color _Old, Color _New)
        {
            playerNameText.color = _New;
            playerMaterialClone = new Material(GetComponent<Renderer>().material);
            playerMaterialClone.color = _New;
            GetComponent<Renderer>().material = playerMaterialClone;
        }

        public override void OnStartLocalPlayer()//当该对象成为本地玩家时调用的方法
        {
            Camera.main.transform.SetParent(transform);
            Camera.main.transform.localPosition = new Vector3(0, 0, 0);
            
            floatingInfo.transform.localPosition = new Vector3(0, -0.3f, 0.6f);
            floatingInfo.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);

            string name = "Player" + Random.Range(100, 999);
            Color color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
            CmdSetupPlayer(name, color);
        }

        [Command]  //以Cmd开头的方法或标记[Command]的方法,只能由客户端调用,在服务器上执行

        public void CmdSetupPlayer(string _name, Color _col)
        {
            // player info sent to server, then server updates sync vars which handles it on all clients
            playerName = _name;
            playerColor = _col;
        }

        void Update()
        {
            if (!isLocalPlayer)
            {
                // make non-local players run this
                floatingInfo.transform.LookAt(Camera.main.transform);
                return;
            }

            float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;
            float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;

            transform.Rotate(0, moveX, 0);
            transform.Translate(0, 0, moveZ);
        }
    }
}

八、第二次Play

这时候测试人头上会有随机数字的名字

九、场景

新建一个空物体命名为SceneScript。并新建一个SceneScript.cs挂在这个物体上。也挂载NetworkIdentity代码。

创建一个button 和 text

修改PlayerScript代码

添加以下内容

private SceneScript sceneScript;

void Awake()
{
    //allow all players to run this
    sceneScript = GameObject.FindObjectOfType<SceneScript>();
}

[Command]
public void CmdSendPlayerMessage()
{
    if (sceneScript) 
        sceneScript.statusText = $"{playerName} says hello {Random.Range(10, 99)}";
}

[Command]
public void CmdSetupPlayer(string _name, Color _col)
{
    //player info sent to server, then server updates sync vars which handles it on all clients
    playerName = _name;
    playerColor = _col;
    sceneScript.statusText = $"{playerName} joined.";
}

public override void OnStartLocalPlayer()
{
    sceneScript.playerScript = this;
    //. . . . ^ new line to add here

-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

SceneScript代码如下:

using Mirror;
using UnityEngine;
using UnityEngine.UI;

namespace QuickStart
{
    public class SceneScript : NetworkBehaviour
    {
        public Text canvasStatusText;
        public PlayerScript playerScript;

        [SyncVar(hook = nameof(OnStatusTextChanged))]
        public string statusText;

        void OnStatusTextChanged(string _Old, string _New)
        {
            //called from sync var hook, to update info on screen for all players
            canvasStatusText.text = statusText;
        }

        public void ButtonSendMessage()
        {
            if (playerScript != null)  
                playerScript.CmdSendPlayerMessage();
        }
    }
}

十、第三次play

这个时候play

点击右上角button会有个says hello

十一、添加武器切换

添加以下代码到PlayerScript.cs

private int selectedWeaponLocal = 1;
public GameObject[] weaponArray;

[SyncVar(hook = nameof(OnWeaponChanged))]
public int activeWeaponSynced = 1;

void OnWeaponChanged(int _Old, int _New)
{
    // disable old weapon
    // in range and not null
    if (0 < _Old && _Old < weaponArray.Length && weaponArray[_Old] != null)
        weaponArray[_Old].SetActive(false);
    
    // enable new weapon
    // in range and not null
    if (0 < _New && _New < weaponArray.Length && weaponArray[_New] != null)
        weaponArray[_New].SetActive(true);
}

[Command]
public void CmdChangeActiveWeapon(int newIndex)
{
    activeWeaponSynced = newIndex;
}

void Awake() 
{
    // disable all weapons
    foreach (var item in weaponArray)
        if (item != null)
            item.SetActive(false); 
}

添加武器切换代码,要放在!isLocalPlayer检查后面

void Update()
{if (!isLocalPlayer){// make non-local players run thisfloatingInfo.transform.LookAt(Camera.main.transform);return;}float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;transform.Rotate(0, moveX, 0);transform.Translate(0, 0, moveZ);if (Input.GetButtonDown("Fire2")) //Fire2 is mouse 2nd click and left alt{selectedWeaponLocal += 1;if (selectedWeaponLocal > weaponArray.Length) selectedWeaponLocal = 1; CmdChangeActiveWeapon(selectedWeaponLocal);}
}

玩家按鼠标右键 →

本地修改 selectedWeaponLocal(临时记录) →

调用 CmdChangeActiveWeapon(将本地值发给服务器) →

服务器修改 activeWeaponSynced(权威同步变量) →

Mirror 自动将 activeWeaponSynced 同步到所有客户端 →

所有客户端触发 OnWeaponChanged(根据同步值切换武器模型)

十二、武器制作

双击player进入预制体进行制作

添加空物体WeaponHolder,位置旋转设置0,0,0

                                            添加Cube子物体并移除Cube碰撞体

                                             重命名Cube为Weapon1并调整参数

复制Weapon1并重命名为Weapon2

修改参数

点击Player把武器拖给代码位置

十三、第四次Play

运行后点击鼠标切换武器。

十四、小调整(新增SceneReference)

因为使用 GameObject.Find() 可能无法保证找到SceneScript。NetworkIdentity场景对象被禁用,它们会被禁用,直到玩家处于“就绪”状态(就绪状态通常在玩家生成时设置)。

创建一个名为 SceneReference.cs 的新脚本

using UnityEngine;

namespace QuickStart
{
    public class SceneReference : MonoBehaviour
    {
        public SceneScript sceneScript;
    }
}

打开SceneScript.cs并添加以下变量。

public SceneReference sceneReference;

现在,在 Unity 场景中创建一个游戏对象,将其命名为 SceneReference,并添加新脚本。在两个游戏对象上将引用设置为彼此。因此,SceneReference 可以与 SceneScript 通信,SceneScript 可以与 SceneReference 通信。

打开PlayerScript.cs并将 Awake 函数覆盖为以下内容:

void Awake()
{//allows all players to run thissceneScript = GameObject.Find(“SceneReference”).GetComponent<SceneReference>().sceneScript;
}

Mirror的核心用法

public class Player : NetworkBehaviour{// 自动同步,只能在服务器上被修改[SyncVar] public int health = 100;// 列表SyncList<Item> inventory = new SyncList<Item>();// 只有服务器或客户端执行[Server] void LevelUp() {}[Client] void Animate() {}void Update(){// 运行时检查是在服务器还是在客户端if (isServer) Heal();if (isClient) Move();}// 零开销远程调用[Command]   void CmdUseItem(int slot) {} // 客户端到服务器[ClientRpc] void RpcRespawn() {}         // 服务器到所有的客户端[TargetRpc] void Hello() {}              // 服务器到单个客户端
}
  • RPC( Remote Procedure CallsRemote Procedure Calls):跨网络执行操作,也叫远程过程调用,Mirror的网络系统中的RPC分为两种:CommandClientRpc。
    • Command:在客户端被调用,在服务端运行。
    • ClientRpc:在服务端被调用,在所有客户端运行。
      • TargetRpc:在服务端被调用,在某个客户端运行。
  • SyncVars
    • 一个特性,被修饰的字段会自动同步,且其只能在服务端被修改,同步方向为从服务端到客户端。当一个游戏对象被生成后,或者有新的玩家加入时,他们就会得到最新的状态信息。该特性带有一个hook(SyncVar Hooks)参数,在服务器上的值被修改时在所有的客户端调用这个钩子函数。
    • 同样的还有SyncLists和SyncDictionary等,不过用法略微不同,详情请看 SyncLists 以及 SyncDictionary
  • NetworkManager
    网络管理器,管理网络游戏对象的组件,其一些主要的功能包括:
    • 游戏状态管理
    • 游戏对象的生成管理
    • 场景管理
    • 调试信息
    • 自定义等

以下是该组件的生命周期:

  • NetworkBehaviour
    • 网络行为组件,可以在这里实现联网对象的网络行为,做法是新建一个类继承这个组件。配合NetworkIdentity组件一起使用,一般在这里使用Command、ClientRpc、SyncVars等高级API(high-level API),以下是这个组件的生命周期:
    • 负责处理游戏对象在网络中的数据传输,确保游戏状态的一致性和实时性。
    • 参考链接:(1 封私信) Unity 多人联机库Mirror教程 - 知乎



      镜像快速入门项目 |镜子

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/92340.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92340.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/92340.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R4周打卡——Pytorch实现 LSTM火灾预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、准备工作 1.1导入数据 1.2数据集可视化 二、构建数据集 2.1数据集预处理 2.2设置X、Y 2.3检查数据集中有没有空值 2.4划分数据集 三、构建模型 3.1定义训…

【视觉识别】Ubuntu 22.04 上编译安装OPENCV 4.12.0 鲁班猫V5

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

基于vue的财务管理系统/基于php的财务管理系统

基于vue的财务管理系统/基于php的财务管理系统

机器学习技术在订单簿大单检测中的应用研究

订单簿数据的特点 订单簿&#xff08;Order Book&#xff09;是记录市场上所有未成交买卖订单的数据结构&#xff0c;通常包括价格、数量、买卖方向等信息。订单簿数据具有以下特点&#xff1a; 高频率&#xff1a;订单簿数据更新速度极快&#xff0c;通常以毫秒甚至微秒为单位…

Spring MVC框架中DispatcherServlet详解

1. DispatcherServlet概述1.1 什么是DispatcherServlet&#xff1f;DispatcherServlet是Spring MVC框架的核心组件&#xff0c;它本质上是一个Java Servlet&#xff0c;作为前端控制器(Front Controller)负责接收所有HTTP请求&#xff0c;并根据特定规则将请求分发到相应的处理…

DBA急救手册:拆解Oracle死锁图,ORA-00060错误秒级定位终极指南

关于“死锁图”&#xff08;Deadlock Graph&#xff09;的一点浅见 当 Oracle 检测到死锁时&#xff0c;检测到死锁的会话中的当前 SQL 将被取消&#xff0c;并执行“语句级回滚”&#xff0c;以释放资源并避免阻塞所有活动。 检测到死锁的会话仍然“存活”&#xff0c;并且事务…

C++中的默认函数学习

今天在学习QT别人的项目时看到有个函数在声明和调用时参数个数不一样&#xff0c;查了下是c中的一种函数类型&#xff0c;这个类型的函数可以让代码更简洁、灵活。定义&#xff1a;在函数声明时&#xff0c;给某些参数预先设定一个默认值。调用函数时&#xff0c;如果省略这些参…

HBase分片技术实现

HBase分片技术实现概述HBase是基于Hadoop的分布式、可扩展的NoSQL数据库&#xff0c;采用列族存储模型。HBase的分片机制通过Region自动分割和负载均衡实现水平扩展&#xff0c;支持PB级数据存储和高并发访问。HBase架构核心组件HMaster: 集群管理节点&#xff0c;负责Region分…

Python爬虫实战:研究awesome-python工具,构建技术资源采集系统

1. 引言 1.1 研究背景 Python 凭借语法简洁、生态丰富等特点,已成为全球最受欢迎的编程语言之一。截至 2024 年,PyPI(Python Package Index)上的第三方库数量已突破 45 万个,涵盖从基础工具到前沿技术的全领域需求。然而,海量资源也带来了 "信息过载" 问题 —…

【实时Linux实战系列】实时视频监控系统的开发

随着技术的不断发展&#xff0c;实时视频监控系统在安防、交通管理、工业自动化等领域得到了广泛应用。实时Linux系统因其高效的实时性和稳定性&#xff0c;成为开发高性能视频监控系统的理想选择。掌握基于实时Linux的视频监控系统开发技能&#xff0c;对于开发者来说不仅能够…

力扣-11.盛最多水的容器

题目链接 11.盛最多水的容器 class Solution {public int maxArea(int[] height) {int res 0;for (int i 0, j height.length - 1; i < j; ) {res Math.max(res, Math.min(height[i], height[j]) * (j - i));if (height[i] < height[j]) {i;} else {j--;}}return r…

大型音频语言模型论文总结

大型音频语言模型&#xff08;Large Audio Language Model, LALM&#xff09;是一类基于深度学习的智能系统&#xff0c;专门针对音频信号&#xff08;如语音、音乐、环境声等&#xff09;进行理解、生成、转换和推理。它借鉴了大型语言模型&#xff08;LLM&#xff09;的“预训…

如何解决网页视频课程进度条禁止拖动?

function skip() {let video document.getElementsByTagName(video)for (let i0; i<video.length; i) {video[i].currentTime video[i].duration} } setInterval(skip,6666)无法拖动视频进度。 使用F12启动调试模式。 function skip() {let video document.getElements…

基于deepSeek的流式数据自动化规则清洗案例【数据治理领域AI带来的改变】

随着AI大模型的大量普及&#xff0c;对于传统代码模式产生了不小的影响&#xff0c;特别是对于大数据领域&#xff0c;传统的规则引擎驱动的数据治理已经无法满足数据增长带来的治理需求。因此主动型治理手段逐渐成为主流&#xff0c;因此本文介绍一个基于deepSeek的流式数据自…

【论文分析】【Agent】SEW: Self-Evolving Agentic Workflows for Automated Code Generatio

1.论文信息标题&#xff1a;SEW: Self-Evolving Agentic Workflows for Automated Code Generatio&#xff1a;用于自动代码生成的自我进化的代理工作流程收录的会议/期刊&#xff1a;作者信息&#xff1a;arxiv&#xff1a;&#x1f517;github网站&#xff1a;&#x1f517;g…

MCP 协议:AI 时代的 “万能转接头”,从 “手动粘贴” 到 “万能接口”:MCP 协议如何重构 AI 工具调用规则?

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

VUE本地构建生产环境版本用于局域网访问

&#x1f680;构建生产环境版本用于局域网访问&#xff08;适用于 Vue 项目&#xff09; 在开发 Vue 项目的过程中&#xff0c;很多人使用 yarn serve 启动开发服务器进行调试。但开发模式存在以下问题&#xff1a; 访问速度慢&#xff0c;特别是局域网访问&#xff1b;热更新频…

【密码学】5. 公钥密码

这里写自定义目录标题公钥密码密码学中的常用数学知识群、环、域素数和互素数模运算模指数运算费尔马定理、欧拉定理、卡米歇尔定理素性检验欧几里得算法中国剩余定理&#xff08;CRT&#xff09;离散对数二次剩余循环群循环群的选取双线性映射计算复杂性公钥密码体制的基本概念…

VINS-Fusion+UWB辅助算法高精度实现

VINS-FusionUWB辅助算法高精度实现 摘要 本文详细介绍了基于VINS-Fusion框架结合UWB辅助的高精度定位算法实现。通过将视觉惯性里程计(VIO)与超宽带(UWB)测距技术融合&#xff0c;显著提高了复杂环境下的定位精度和鲁棒性。本文首先分析了VINS-Fusion和UWB各自的技术特点&#…

新手向:Python实现简易计算器

你是否一直想学习编程但不知从何入手&#xff1f;这篇详细的教程将带领完全零基础的读者&#xff0c;循序渐进地掌握如何用Python实现一个简易计算器。我们将从最基本的编程概念讲起&#xff0c;确保每一位初学者都能跟上进度。准备工作在开始之前&#xff0c;你需要&#xff1…