【Unity網絡同步框架 - Nakama研究(三)】

文章目錄

  • 【Unity網絡同步框架 - Nakama研究(三)】
    • 準備工作
    • 前言
    • Unity部分
      • 連接服務器
      • 創建并進入房間
      • 創建人物
      • 人物移動和同步

【Unity網絡同步框架 - Nakama研究(三)】

以下部分需要有一定的Unity基礎,在官方的案例Pirate Panic基礎上進行修改而成。如果沒有下載并熟悉過官方案例,最好先下載對應的工程查看。工程地址為:https://github.com/heroiclabs/unity-sampleproject,對應的案例文檔為:https://heroiclabs.com/docs/zh/nakama/tutorials/unity/pirate-panic/,以下運行的Unity版本為2022LTS,一般關系不大

準備工作

  • Unity2022或者隨便一個LTS版本
  • VS2022
  • Nakama Unity SDK(官網或者Unity商店都有,實在找不到把上面的案例的程序集偷出來)

前言

  • Nakama是一個網絡同步庫,兼容很多游戲引擎,名字取自于日語伙伴,底層由Go開發,所以性能上有保證(可以對比其他流行的網絡框架)。并且擁有大量已經開發好而且經過檢驗的功能(聊天,排行榜,群組,房間,身份驗證,存儲,好友等等),但是之前在網絡上,甚至官網上找到的博客或者文章要么是性質雷同,要么就是空談。
  • 以下的改變主要是用于網上找不到,AI提供不準確,論壇全英文,翻找資料麻煩的基礎上提供的。

Unity部分

連接服務器

  • 我喜歡盡量把邏輯精簡,讓程序能跑起來,再去研究里面的細節,就像鋼鐵俠里面的臺詞“有時候你得先跑起來,再學會走路”
	[SerializeField] private GameConnection _connection;public static string DeviceIdKey => "nakama.deviceId" + UserData.Id;public static string AuthTokenKey => "nakama.authToken" + UserData.Id;public static string RefreshTokenKey => "nakama.refreshToken" + UserData.Id;private Client client;private ISocket socket;private const string ServerIp = "xxx.xxx.xx.xx"; // 你的ip地址public async void RequireEnterRoom(){if (_connection.Session == null){string deviceId = GetDeviceId();if (!string.IsNullOrEmpty(deviceId)){PlayerPrefs.SetString(DeviceIdKey, deviceId);}await InitializeGame(deviceId);}}private async Task InitializeGame(string deviceId){client = new Client("http", ServerIp, 7350, "defaultkey", UnityWebRequestAdapter.Instance);client.Timeout = 5;socket = client.NewSocket(useMainThread: true);string authToken = PlayerPrefs.GetString(AuthTokenKey, null);bool isAuthToken = !string.IsNullOrEmpty(authToken);string refreshToken = PlayerPrefs.GetString(RefreshTokenKey, null);ISession session = null;// refresh token can be null/empty for initial migration of client to using refresh tokens.if (isAuthToken){session = Session.Restore(authToken, refreshToken);// Check whether a session is close to expiry.if (session.HasExpired(DateTime.UtcNow.AddDays(1))){try{// get a new access tokensession = await client.SessionRefreshAsync(session);}catch (ApiResponseException){// get a new refresh tokensession = await client.AuthenticateDeviceAsync(deviceId);PlayerPrefs.SetString(RefreshTokenKey, session.RefreshToken);}PlayerPrefs.SetString(AuthTokenKey, session.AuthToken);}}else{session = await client.AuthenticateDeviceAsync(deviceId);PlayerPrefs.SetString(AuthTokenKey, session.AuthToken);PlayerPrefs.SetString(RefreshTokenKey, session.RefreshToken);}Connect(socket, session);IApiAccount account = null;try{account = await client.GetAccountAsync(session);}catch (ApiResponseException e){Debug.LogError("Error getting user account: " + e.Message);}_connection.Init(client, socket, account, session);}private async void Connect(ISocket socket, ISession session){try{if (!socket.IsConnected){await socket.ConnectAsync(session);}}catch (Exception e){Debug.LogWarning("Error connecting socket: " + e.Message);}}private string GetDeviceId(){string deviceId = "";deviceId = PlayerPrefs.GetString(DeviceIdKey);if (string.IsNullOrWhiteSpace(deviceId)){deviceId = Guid.NewGuid().ToString();}return deviceId;}

上面的這部分就是連接的函數部分,其中的結構GameConnection如下:

using Nakama;
using UnityEngine;public class GameConnection : ScriptableObject
{private IClient _client;public IClient Client => _client;public ISession Session { get; set; }public IApiAccount Account { get; set; }private ISocket _socket;public ISocket Socket => _socket;private IChannel _channel;public IChannel Channel => _channel;public string MatchID { get; set; }public void Init(IClient client, ISocket socket, IApiAccount account, ISession session){_client = client;_socket = socket;Account = account;Session = session;}
}

上面大部分的代碼都能在案例中找到,有些小修改。需要注意的是,如果要在電腦上實現多開(非編輯器模式下,處于打完包的exe狀態),需要修改DeviceIdKey等參數,不然服務器接收到的時候,這倆會識別成同一個帳號(因為傳入的參數deviceId一致),會給后續操作帶來麻煩。

創建并進入房間

  • 這一步開始就跟案例中的不一樣了,案例使用的是AddMatchmakerAsync,這個方法在文檔中說明是不會創建房間的,只是簡單的匹配機制,所以如果這個時候你寫了如下代碼:
	private async void ListMatchesAndJoin(){var minPlayers = 0;var maxPlayers = 10;var limit = 10;var authoritative = true;var label = "";var query = "*";try{var result = await client.ListMatchesAsync(_connection.Session, minPlayers, maxPlayers, limit, authoritative, null, null);// 添加新的列表項foreach (var match in result.Matches){Debug.LogFormat("{0}: {1}/{2} players", match.MatchId, match.Size, maxPlayers);JoinMatch(match.MatchId);break;}}catch (System.Exception e){Debug.LogError("Error listing matches: " + e.Message);}}

到時候你就會發現怎么都拿不到房間信息,一直返回空,這里根據需求分為兩步,一是你自己創建的房間(如果人數為0,會被銷毀,而且走的是官方設定好的邏輯,叫非權威比賽),二是服務器創建的權威比賽,這個比賽即使房間內人數為0也不會解散(關服務器還是會解散的)

// 這是非權威比賽(權威比賽會在上述代碼中直接返回對應的matchid)
var matchid = await _connection.Socket.CreateMatchAsync();// 通過返回的matchid加入
var match = await socket.JoinMatchAsync(matchId);

至于如何創建服務器的權威比賽,留到下次講服務器擴展再說。

  • 走到這一步,其實我們已經在房間里了,看服務器的日志,日志
    第一條是連接socket,第二條是加入房間。

創建人物

  • 進入了房間,接下去做的一般是創建你所加入房間的那個擺設,或者新的場景,然后給服務器發送創建人物的信息,涉及到操作信息在房間內的傳遞。
  • 創建新的場景這一點,Unity自己就能做到
  • 發送操作信息要分開,因為Nakama有很多種渠道可以發送消息,這里采用正規一點的房間消息,需要注意的是,如果這個房間是非權威房間,那么房間信息Nakama給你寫好了,如果是服務器自己創建的非權威房間,那么需要你自己寫。
	public static async Task SpawnPlayer(){var matchMessagePlayerCreate = new MatchMessagePlayerCreate(BattleSceneController.Instance.Connection.Session.Username,BattleSceneController.Instance.Connection.Session.UserId,randomPos.x,randomPos.y,randomPos.z,0, 0, 0,selectCharacterId,selectCharacterData);BattleSceneController.Instance.StateManager.SendMatchStateMessage(MatchMessageType.UnitSpawned, matchMessagePlayerCreate);BattleSceneController.Instance.StateManager.SendMatchStateMessageSelf(MatchMessageType.UnitSpawned, matchMessagePlayerCreate);}

創建人物信息的方式跟案例里面的差不多,注意一下時序問題即可。然后在監聽對應事件的地方GameStateManager處理服務器發送過來的消息即可。

private GameConnection _connection;_connection.Socket.ReceivedMatchState += ReceiveMatchStateMessage;private void ReceiveMatchStateMessage(IMatchState matchState){string messageJson = System.Text.Encoding.UTF8.GetString(matchState.State);if (string.IsNullOrEmpty(messageJson))return;ReceiveMatchStateHandle(matchState.OpCode, messageJson);}public void SendMatchStateMessageSelf<T>(MatchMessageType opCode, T message)where T : MatchMessage<T>{switch (opCode){case MatchMessageType.UnitSpawned:OnPlayerCreate?.Invoke(message as MatchMessagePlayerCreate);break;default:break;}}
public void ReceiveMatchStateHandle(long opCode, string messageJson){switch ((MatchMessageType)opCode){case MatchMessageType.UnitSpawned:MatchMessagePlayerCreate matchMessagePlayerCreate = MatchMessagePlayerCreate.Parse(messageJson);OnPlayerCreate?.Invoke(matchMessagePlayerCreate);break;default:break;}}

有一點需要注意的是,Nakama傳遞的消息結構字段是json,而且是Base64轉義之后的,如果你在服務器的日志中看到錯誤信息,記得先轉回正常的字符串。

  • 然后你的人物就能出現在場景中了。人物

人物移動和同步

  • 再往后面就是正常的人物之間的同步信息,比如人物的旋轉,移動,動畫等等,都可以在上面ReceiveMatchStateHandle方法里面進行監聽和執行,涉及到CinemachineTimeline,動畫狀態機等等,就不在這里詳細展開了。

下一章講講服務器的擴展相關和一些可能遇到的問題

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72278.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72278.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72278.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端存儲-indexdb封裝:dexie.js的使用

前言 indexedDB是一個用于在瀏覽器中存儲較大數據結構的Web API&#xff0c;并且提供了索引功能以實現高性能查找。dexie.js是對indexdb的封裝&#xff0c;前端用起來很方便。在此介紹一下項目中用到的操作語句&#xff0c;也方便記錄。我的項目是vue3項目。 開始 1、安裝 …

【AD】6-1 PCB常用規則

間距規則&#xff1a; 可自行修改線寬與間距&#xff08;默認10mil&#xff09; 線寬規則&#xff1a;電源線寬加粗 布線過程中更改線寬&#xff1a;走線狀態下&#xff0c;shiftw更改線寬&#xff0c;線寬要在規則范圍之內過孔規則&#xff1a; 阻焊規則&#xff1a;

MyBatis 的核心配置文件是干什么的? 它的結構是怎樣的? 哪些是必須配置的,哪些是可選的?

MyBatis 的核心配置文件&#xff08;通常命名為 mybatis-config.xml&#xff09;是 MyBatis 應用程序的入口點&#xff0c;它定義了 MyBatis 的全局配置信息 。 核心配置文件的作用&#xff1a; 配置 MyBatis 的運行時行為: 通過 <settings> 標簽設置全局參數&#xff…

搜廣推校招面經四十九

tiktok廣告算法 一、倒排索引原理及Map中Key的處理 具體使用方法見【搜廣推校招面經三十六】 倒排索引&#xff08;Inverted Index&#xff09;是信息檢索系統中常用的一種數據結構&#xff0c;用于快速查找包含某個關鍵詞的文檔。以下是倒排索引的原理及Map中Key的處理方式的…

【零基礎入門unity游戲開發——unity3D篇】3D物理系統之 —— 3D剛體組件Rigidbody

考慮到每個人基礎可能不一樣,且并不是所有人都有同時做2D、3D開發的需求,所以我把 【零基礎入門unity游戲開發】 分為成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要講解C#的基礎語法,包括變量、數據類型、運算符、流程控制、面向對象等,適合沒有編程基礎的…

C# net deepseek RAG AI開發 全流程 介紹

deepseek本地部署教程及net開發對接 步驟詳解&#xff1a;安裝教程及net開發對接全流程介紹 DeepSeekRAG 中的 RAG&#xff0c;全稱是 Retrieval-Augmented Generation&#xff08;檢索增強生成&#xff09;&#xff0c;是一種結合外部知識庫檢索與大模型生成能力的技術架構。其…

用舊的手機搭建 MQTT Broker

MQTT Broker搭建 在Android上搭建MQTT所需工具: termux 通過網盤分享的文件:termux-app_v0.118.1+github-debug_armeabi-v7a.apk 鏈接: https://pan.baidu.com/s/1Iii2szXAc02cKVGdP1EuzQ?pwd=fqsc 提取碼: fqsc 在 Termux 中使用 MQTT(Message Queuing Telemetry Trans…

b站視頻下載工具軟件怎么下載

自行配置FFMPEG環境 請優先選擇批量下載&#xff0c;會自處理視頻和音頻文件。 如果要下載更高質量請登陸。 沒有配置FFMPEG下載后會有報錯提示&#xff0c;視頻音頻文件無法合并生成mp4文件 更新批量下載標題&#xff0c;只取視頻原標題&#xff0c;B站反爬機制登陸后下載多了…

# linux有哪些桌面環境?有哪些顯示服務器協議及顯示服務器?有哪些用于開發圖形用戶界面的工具包?

linux有哪些桌面環境&#xff1f;有哪些顯示服務器協議及顯示服務器&#xff1f;有哪些用于開發圖形用戶界面的工具包&#xff1f; 文章目錄 linux有哪些桌面環境&#xff1f;有哪些顯示服務器協議及顯示服務器&#xff1f;有哪些用于開發圖形用戶界面的工具包&#xff1f;1 顯…

Java 大視界 -- Java 大數據分布式計算中的資源調度與優化策略(131)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

躲藏博弈中的策略優化:整合歷史數據、概率論與博弈論

躲藏博弈中的策略優化&#xff1a;整合歷史數據、概率論與博弈論 一、引言 躲藏博弈(Hiding Games)作為一類特殊的博弈模型&#xff0c;廣泛存在于軍事對抗、網絡安全、商業競爭甚至日常生活中。其核心在于一方(躲藏者)試圖避免被另一方(尋找者)發現&#xff0c;雙方各自選擇…

時序數據庫 TDengine 到 MySQL 數據遷移同步

簡述 TDengine 是一款開源、高性能、云原生的時序數據庫&#xff0c;專為物聯網、車聯網、工業互聯網、金融、IT 運維等場景優化設計。在工業自動化的時代&#xff0c;時序數據庫在電力、軌道交通、智能制造等領域有著廣泛的應用。 MySQL 是全球廣泛使用的開源關系型數據庫&a…

基于YOLO11深度學習的舌苔舌象檢測識別與診斷系統【python源碼+Pyqt5界面+數據集+訓練代碼】

《------往期經典推薦------》 一、AI應用軟件開發實戰專欄【鏈接】 項目名稱項目名稱1.【人臉識別與管理系統開發】2.【車牌識別與自動收費管理系統開發】3.【手勢識別系統開發】4.【人臉面部活體檢測系統開發】5.【圖片風格快速遷移軟件開發】6.【人臉表表情識別系統】7.【…

【愚公系列】《高效使用DeepSeek》003-DeepSeek文檔處理和其他頂級 AI模型的區別

標題詳情作者簡介愚公搬代碼頭銜華為云特約編輯,華為云云享專家,華為開發者專家,華為產品云測專家,CSDN博客專家,CSDN商業化專家,阿里云專家博主,阿里云簽約作者,騰訊云優秀博主,騰訊云內容共創官,掘金優秀博主,亞馬遜技領云博主,51CTO博客專家等。近期榮譽2022年度…

正新雞排:在變革浪潮中領航,打造連鎖餐飲新生態

在當下風云變幻的餐飲市場中&#xff0c;連鎖品牌猶如逆水行舟&#xff0c;不進則退。作為國內坐擁萬店的知名連鎖餐飲品牌&#xff0c;正新雞排2023年賣出了7.2億片雞排&#xff0c;集團營收同比增長28%。在《2024年中國雞排連鎖品牌10強榜單》中&#xff0c;正新雞排以高達95…

MyBatis 的一級、二級緩存

文章目錄 1?? 一級緩存&#xff08;Local Cache&#xff09;&#x1f4cc; 定義&#x1f680; 示例代碼 2?? 二級緩存&#xff08;Global Cache&#xff09;&#x1f4cc; 定義&#x1f680; 使用方式 3?? 一級緩存 vs. 二級緩存 &#x1f4ca;4?? 數據共享問題&#x…

軟件性能測試與功能測試聯系和區別

隨著軟件開發技術的迅猛發展&#xff0c;軟件性能測試和功能測試成為了確保軟件質量的兩個重要環節。那么只有一字之差的性能測試和功能測試分別是什么?又有哪些聯系和區別呢? 一、軟件性能測試是什么?   軟件性能測試是為了評估軟件系統在特定條件下的表現&#xff0c;包…

Vue 框架使用難點與易錯點剖析:避開陷阱,提升開發效率

Vue.js 作為當下最流行的前端框架之一&#xff0c;以其輕量、易用和靈活的特性深受開發者喜愛。然而&#xff0c;即使是經驗豐富的開發者&#xff0c;在使用 Vue 的過程中也難免會遇到一些難點和易錯點。本文將深入分析 Vue 開發中常見的“坑”&#xff0c;并提供解決方案和代碼…

基于大模型的上瞼下垂手術全流程預測與方案優化研究報告

目錄 一、引言 1.1 研究背景與目的 1.2 研究意義 1.3 研究方法與創新點 二、上瞼下垂相關理論基礎 2.1 上瞼下垂的定義與分類 2.2 發病機制與影響 2.3 傳統治療方法概述 三、大模型技術原理與應用 3.1 大模型概述 3.2 在醫療領域的應用現狀 3.3 用于上瞼下垂預測的…

Odoo Http鑒權+調用后端接口

最近在調研Odoo18&#xff0c;包括它的前后端原理、源碼等。發現官方的開發文檔并不十分實用&#xff0c;比如標題這種簡單的實用需求&#xff0c;竟然浪費了一點時間&#xff0c;特此記錄。 官方文檔&#xff1a;External API — Odoo 18.0 documentation 前提&#xff1a;首…