Mirror學習筆記

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…