本教程將詳細展示如何把 Unity Online Services(UOS)Multiverse 與 Netcode for GameObjects 相結(jié)合,完成入門級操作。

通過本教程,你不僅能學會如何將其構(gòu)建為專用服務(wù)器(Dedicated Server),并借助 Multiverse 進行托管,還能掌握運用 Matchmaking 為多人聯(lián)機游戲打造可定制化的對局匹配。此外,教程還會演示并講解將 Demo 工程部署至微信小游戲平臺時所需的各項設(shè)置,帶你一站式打通游戲開發(fā)與部署的關(guān)鍵流程!

大家可以通過 UOS 官網(wǎng)提供的多人聯(lián)機服務(wù)選型參考,來了解 Dedicated Game Server 適用的游戲類型:

https://uos.unity.cn/product/multiplayer

教程中涉及 UOS 服務(wù)包括:

  • 服務(wù)器托管服務(wù) Multiverse:用于部署多人聯(lián)機服務(wù)端程序

服務(wù)器托管服務(wù) Multiverse

Multiverse,由 Unity 資深團隊匠心打造的專業(yè)服務(wù)器托管解決方案,讓您的服務(wù)從上線到運營全程無憂。

它根植于 Agones 的強大基礎(chǔ),提供了更為可靠、豐富且高效的托管服務(wù)。借助 Multiverse,您的游戲能夠迅速啟動并運行于彈性縮擴容的系統(tǒng)中,無論是面對幾百還是幾百萬的玩家,都能輕松應(yīng)對,實現(xiàn)游戲服務(wù)器的無憂托管,讓您全心投入到提升游戲的可玩性之中。

選擇 Multiverse 的卓越優(yōu)勢:

  • 高效可靠的伸縮能力:基于 Agones 的堅實架構(gòu),Multiverse 確保了服務(wù)器資源能夠根據(jù)游戲負載進行即時且高效的調(diào)度,同時提供可靠的伸縮機制,無論是面對突發(fā)的玩家涌入還是日常的流量波動,都能確保游戲運行的平穩(wěn)與流暢。

  • 靈活的 Docker 容器部署:借助 Docker 容器技術(shù)的強大功能,Multiverse 允許開發(fā)者以更加靈活和模塊化的方式組織服務(wù),同時簡化了系統(tǒng)依賴的安裝與管理過程,使得服務(wù)的部署、升級與遷移變得更加便捷與高效。

  • 多地域智能縮擴容:為了滿足全球玩家的需求,Multiverse 支持在全球多個地域部署服務(wù)器,并根據(jù)實時流量數(shù)據(jù)自動調(diào)整服務(wù)器資源,確保服務(wù)的經(jīng)濟性與高效性。這種智能化的縮擴容策略不僅降低了運營成本,還提升了玩家的游戲體驗,讓您的游戲在全球范圍內(nèi)都能保持出色的性能與穩(wěn)定性。

教程視頻

教程學習大綱

  1. Unity 項目工程的準備工作

  2. 創(chuàng)建 UOS App 并啟用 Multiverse / Matchmaking 服務(wù)

  3. 使用 WebSocket 協(xié)議配置并啟動項目

  4. 使用 Matchmaking 實現(xiàn)對局匹配

  5. 使用 KCP 協(xié)議配置并啟動項目

  6. 構(gòu)建 MiniGame 項目,設(shè)置 WXSDK 打包參數(shù)

  7. 在開發(fā)者工具上運行測試小游戲

  8. 更多功能介紹

教程案例工程源文件

教程內(nèi)學習用到的項目工程文件可以通過以下方式進行下載:

教程示例 Demo 項目工程源文件下載鏈接:

https://unitychina.coding.net/public/uos/MultiverseNetcodeDemo/git/files

教程操作步驟

1. Unity 項目工程準備工作

溫馨提醒:本教程的內(nèi)容同時適用于 Global Unity Editor 、中國版 Unity Editor 和 團結(jié)引擎,當前先以在中國版 Unity 引擎中的使用為示例,進行講解演示。

為大家提供兩種準備初始項目的方式:

  • 方式一:直接下載我們提供鏈接里的 Demo 示例項目工程學習,按章節(jié) 1.1 的步驟操作,可跳過章節(jié) 1.2。

  • 方式二:從創(chuàng)建空項目工程開始,按章節(jié) 1.2 的教程步驟逐步學習。

  • 無論選擇哪種方式,后續(xù)都從章節(jié) 2 繼續(xù)學習。

1.1 方式一:直接下載提供的 Demo 示例項目工程 1.1.1 打開項目工程

如果你想直接使用我們提供的示例 Demo 場景進行學習操作的話,請先通過上面提供的 git 鏈接,下載 Demo 項目工程。

在教程中,我們先使用 中國版 Unity 2022.3.53 f1c1 版本來打開項目工程,然后等待項目工程的加載。

1.1.2 安裝 Netcode for GameObjects 資源包

當前項目將使用 Multiverse 結(jié)合 Netcode 來制作 Demo 的效果,所以項目中需要先安裝 Netcode 資源包。

  • 在 Package Manager 窗口中,我們可以看到當前 Demo 已經(jīng)安裝好了 Netcode 資源包。無需再次安裝。

  • 本教程 Demo 的功能是在 Netcode for GameObjects 提供的 Sample 示例的基礎(chǔ)上稍作的調(diào)整,大家可以自行查看原始的 Sample 示例代碼。

1.2 方式二:創(chuàng)建一個空的項目工程,從零開始操作 1.2.1 創(chuàng)建一個空的項目工程

打開 Unity Hub,選擇創(chuàng)建新項目,教程這里使用的 Unity 編輯器版本是 2022.3.53f1c1 版本,大家可以自行選擇電腦上已安裝的版本。

項目模板可以選擇3D(Built-in),自定義項目名稱和位置??梢怨催x選項啟用游戲云服務(wù)勾選后會自動為你的項目工程安裝 UOS Launcher 的,然后可以直接通過 UOS Launcher 來啟用想要使用的服務(wù)即可。

1.2.2 安裝 Netcode for GameObjects 資源包

由于當前項目將使用 Multiverse 結(jié)合 Netcode 來制作 Demo 的效果,所以項目中需要先安裝 Netcode 資源包。

可以打開WindowPackage Manager窗口,然后選擇Unity Registry,搜索找到Netcode for GameObjects,然后點擊Install即可。

教程中我們會在 Netcode 提供的示例的 Sample 基礎(chǔ)上添加 Multiverse 和 Matchmaking 的功能。所以,我們先找到Samples,點擊Import,導入 Netcode 的示例場景。

1.2.3 查看 Sample 場景

找到 Project 窗口中的 Bootstrap.unity 場景并打開,運行查看下當前示例場景的效果。

在彈出的下面窗口中,點擊Yes,則會自動將當前場景添加到FileBuildSettings中。

運行后,Game 場景畫面如下:

可以先在編輯器中點擊Host按鈕,會看到在場景中會生成一個 Sphere 游戲?qū)ο?,點擊Random Teleport按鈕,就可以隨機一個新的位置。

1.2.4 場景中創(chuàng)建對局匹配和隨機位置的 UI 按鈕

由于場景中的按鈕是通過 OnGUI 函數(shù)繪制出來的,為了后面方便操作 UI 對象,我們對場景做一些調(diào)整,使用 UGUI 來創(chuàng)建 Button。在 Hierarchy 窗口中,點擊+UIButton - TextMeshPro來創(chuàng)建一個按鈕。

在彈出窗口中,點擊Import TMP Essentials來導入相關(guān)資源:

然后設(shè)置 UI 自適應(yīng):找到 Canvas 對象上的 Canvas Scaler 組件,將UI Scale Mode設(shè)置為:Scale With Screen Size,分辨率暫時選擇 1920 *1080。

修改匹配按鈕的名字為:Button_StartMatch,大家可以自己調(diào)整想要的按鈕的大小、位置以及按鈕的顏色或者按鈕的背景貼圖等。

為了 UI 界面的美觀,大家可以自行更換按鈕上的貼圖和字體。在教程中,我們創(chuàng)建了一個 Resources 文件夾,然后文件夾內(nèi)放入了自己想要使用的 UI 貼圖和字體了。UI 貼圖的格式,也已經(jīng)提前設(shè)置好 Sprite 格式了。

找到 Button 組件,將Transition類型改為Sprite Swap,在按鈕的「Normal 」狀態(tài)我們使用 Btn_MainButton_Blue 貼圖,在按鈕的「Disabled」禁用狀態(tài)使用 Btn_MainButton_Gray 這張貼圖。然后可以通過激活或者關(guān)閉 Interactable 屬性,測試按鈕的圖片效果。

默認 TextMeshProUGUI 提供的 FontAsset 是不支持輸入中文的開始匹配的,如果你想寫中文文字的話,請自行創(chuàng)建中文字體集,這里暫時先不講解 UGUI 的如何創(chuàng)建中文字體集的知識點了,就直接使用導入的創(chuàng)建好的字體集資源了。

選中 Button_StartMatch 按鈕復(fù)制一份,重命名為:Button_RandomTeleport,作為后面隨機小球位置的按鈕。移動到左上角的位置,按鈕的 Text 先寫:RandomTeleport。

設(shè)置一下初始時場景中隨機位置的按鈕是隱藏的狀態(tài):

接下來,我們就用剛才的新創(chuàng)建的項目工程繼續(xù)下面的步驟講解了。如果你是用我們提供的示例 Demo 的話,后續(xù)教程步驟也是一樣的。

1.2.5 設(shè)置 Network Prefabs Lists

找到場景中的 BootstrapManager 身上的 NetworkManager 組件,可以看到 Player Prefab 參數(shù)已經(jīng)設(shè)為 BootstrapPlayer 預(yù)制物體對象了,表示在客戶端連接上服務(wù)器時,會克隆出一個 BootstrapPlayer 對象。

NetworkPrefabsLists 參數(shù)用于指定在網(wǎng)絡(luò)同步過程中會使用到的預(yù)制體(Prefab)列表。在網(wǎng)絡(luò)多人游戲開發(fā)中,當客戶端和服務(wù)器之間需要同步游戲?qū)ο髸r,Unity 需要知道哪些預(yù)制體可以被實例化并在網(wǎng)絡(luò)上進行同步。如果不指定這些預(yù)制體,當服務(wù)器或客戶端嘗試實例化網(wǎng)絡(luò)對象時,Unity 就無法識別這些對象,從而導致同步失敗。

在這里,我們將 Assets 文件夾下的 DefaultNetworkPrefabs 添加到 NetworkPrefabsLists 中。

然后再手動添加一下場景中要網(wǎng)絡(luò)同步的預(yù)制物體游戲?qū)ο?BootstrapPlayer。

2. 創(chuàng)建 UOS App 并啟用服務(wù)

溫馨提示:當前項目中已安裝好 UOS Launcher,不需要再次安裝了。大家可以參考之前的 的公眾號文章教程,來為你當前的項目綁定你創(chuàng)建好的 UOS App 。

2.1 綁定 UOS App

點擊 Launcher 面板的「LinkApp」按鈕,在彈出窗口中選擇「By Unity project」,在「Select organization」這里選擇一個自己的項目組織,然后在「Select project 」選項這里,我們選擇「Create a new project 」,自行設(shè)置修改項目名字「Project name」。

教程中,我們就先選擇綁定創(chuàng)建好的 NetcodeAndMultiverse_Demo 應(yīng)用了。

2.2 開啟 Multiverse 服務(wù)

在編輯器內(nèi) Unity Online Services 窗口的下拉服務(wù)列表中,找到「Multiverse」,點擊「Enable」開啟服務(wù)。

如果自己項目中沒安裝過的話,可以點擊「Install」安裝 SDK 。當前給大家提供的制作好的示例 Demo 項目已經(jīng)安裝過 Multiverse SDK 了,無需再次安裝。

2.3 開啟 Matchmaking 服務(wù)

接著繼續(xù)在 UOS Launcher 的下拉服務(wù)窗口列表中,找到「Matchmaking Client」,點擊「Enable」開啟服務(wù)。

如果自己項目中沒安裝過的話,可以點擊「Install」安裝 SDK 。當前給大家提供的制作好的示例 Demo 項目已經(jīng)安裝過 Matchmaking Client SDK 了,無需再次安裝。

2.4 安裝 WebSocket 和 KCP 協(xié)議包

最終會將項目部署至微信小游戲平臺,我們已經(jīng)做好了相關(guān)的協(xié)議適配,大家可以在項目中使用提供的 WebSocket 協(xié)議和 KCP 協(xié)議。當前工程中,已經(jīng)安裝好了 WebSocket 協(xié)議和 KCP 協(xié)議。

如果是自己的項目工程的話,可以通過下面提供的 git 鏈接來安裝對應(yīng)的協(xié)議:在「Package Manager」窗口,點擊左上角的「+」,選擇「Add package from git URL」,輸入提供的 git 鏈接,點擊「Add」即可。

  • WebSocket Transport for Netcode for GameObjects 資源包的 git 安裝鏈接:

https://e.coding.net/unitychina/uos/NetcodeTransport.git?path=websocket

  • Kcp Transport for Netcode for GameObjects 資源包的 git 安裝鏈接:

https://e.coding.net/unitychina/uos/NetcodeTransport.git?path=kcp

2.5 安裝 Linux Build 平臺支持模塊包

稍后當我們通過 Multiverse SDK,在 Editor 中自動構(gòu)建并上傳 Linux Dedicated Game Server 鏡像時,會需要將平臺切換到 Linux 平臺。所以需要在當前使用的 Unity 編輯器版本中,安裝 Linux 構(gòu)建平臺模塊包。

打開 Unity Hub ,找到左側(cè)的「安裝」,選擇自己使用的 Unity 編輯器版本,比如 2022.3.53f1c1,點擊「添加模塊」按鈕:

在彈出窗口中,同時勾選模塊 Linux Build Support(IL2CPP)、 Linux Build Support(Mono)、Linux Dedicated Server Build Support ,然后點擊「安裝」。

3. 使用 WebSocket 協(xié)議配置并啟動項目 我們先來講解使用 WebSocket Transport 協(xié)議來運行時的相關(guān)配置! 3.1 選擇 WebSocket 協(xié)議

先找到場景中的對象 BootstrapManager,需要移除 Netcode 示例場景中默認為其添加好的 UnityTransport 組件。

然后在「Select transport...」這里選擇「WebSocketTransport」,添加 WebSocket 協(xié)議對應(yīng)的腳本組件,同時確保 NetworkManager 組件的 NetworkTransport 參數(shù)選擇的協(xié)議為:WebSocketTransport。

3.2 MultiverseSDK 的初始化

我們先來創(chuàng)建 Multiverse 的服務(wù)端程序。找到 BootstrapManager 對象上的 BootstrapManager.cs 腳本,先注釋掉或者刪除該腳本中的 OnGUI 函數(shù)中的所有的代碼,因為接下來我們會一步一步講解要添加使用的代碼。

在這里,需要注意的是,Netcode 的示例代碼添加了自己的應(yīng)用程序集文件 Bootstrap.asmdef,所以想要在 BootstrapManager.cs 腳本中引用 UOS 封裝的相關(guān) API 時,可做以下的兩種處理:

(1)直接刪除 Bootstrap/Scripts/Bootstrap.asmdef 文件即可。我們提供的示例 Demo 項目工程中,是已經(jīng)刪除了該文件,所以不需要 Bootstrap.asmdef 相關(guān)的配置。

(2)如果不想刪除,想保留的話,需要在 Bootstrap.asmdef 文件中添加 UOS 相關(guān)的引用,最后記得點擊右下角的「Apply」。

然后在腳本的 Start 方法中,會根據(jù)宏定義來檢測一下當前運行環(huán)境(服務(wù)器端還是客戶端),來執(zhí)行不同的初始化邏輯:

  • 如果是 Linux Dedicated Server 服務(wù)端,會先進行 Multiverse SDK 初始化,然后啟動服務(wù)器,并標記服務(wù)器為 Ready 就緒狀態(tài)。

  • 如果是客戶端,則進行 MatchmakingSDK 的初始化,并執(zhí)行玩家登錄的操作。_userId 將用于客戶端登錄時作為用戶的唯一標識。

  • 腳本中記得需要添加 UOS 相關(guān)的 namespace 的引用。

using System;
using Unity.UOS.Auth;
using Unity.UOS.Matchmaking;
using Unity.UOS.Multiverse;
using Unity.UOS.Multiverse.Exceptions;
using UnityEngine;
using Unity.Netcode;

public class BootstrapManager : MonoBehaviour
{
private readonly string _userId = Guid.NewGuid().ToString();

private async void Start()
{
#if !UNITY_EDITOR && UNITY_SERVER
// build 為 linux dedicated server 時,執(zhí)行server端 multiverse sdk 初始化的代碼邏輯
Debug.Log("Server is starting");
try
{
// MultiverseSDK 的初始化
await MultiverseSDK.Initialize();
}
catch (MultiverseSDKException ex)
{
Debug.LogErrorFormat("Failed to initialize sdk. {0}", ex);
throw;
}
// 啟動 server
NetworkManager.Singleton.StartServer();

try
{
// 標記 server 為 ready 狀態(tài)
await MultiverseSDK.Instance.ReadyAsync();
Debug.Log("server is ready");
}
catch (MultiverseSDKException ex)
{
Debug.LogErrorFormat("Failed to call mv sdk. {0}", ex);
throw;
}
#else
// build client端時,則初始化 matchmaking sdk 和執(zhí)行玩家登陸操作
Debug.Log("match making sdk initialize");
MatchmakingSDK.Initialize();
Debug.Log("match making sdk initialized");
await AuthTokenManager.ExternalLogin(_userId);
#endif
}
}

由于在客戶端使用 AuthTokenManager.ExternalLogin 來驗證玩家的登錄操作,所以場景中我們創(chuàng)建一個空對象,可命名為 AuthTokenManager,并掛載 AuthTokenManager.cs 腳本組件。

打開網(wǎng)易新聞 查看精彩圖片

3.3 構(gòu)建并上傳 Multiverse 鏡像

Multiverse 是基于 Docker 鏡像來管理服務(wù)器程序的,系統(tǒng)需要將用戶上傳上來的服務(wù)器程序制作成 Multiverse 可用的鏡像程序,并基于鏡像來管理后續(xù)所有的配置。

UOS Launcher 提供了一鍵構(gòu)建并上傳 Multiverse 鏡像的功能。當我們使用 Unity 構(gòu)建 Dedicated Server - Linux 且托管到 Multiverse 時,推薦使用 Launcher 來簡化鏡像構(gòu)建與上傳的流程。

步驟如下:

  • 在 Multiverse 右側(cè)列表中,點擊「構(gòu)建鏡像」按鈕,此時則可以通過 Multiverse SDK,在 Editor 中自動上傳 Dedicated Game Server 鏡像到 UOS Dev Portal。

  • 填寫配置:在彈出窗口中填寫配置:Target Directory 為鏡像存放的本地目錄,Image Tag 為鏡像標簽。

  • 構(gòu)建鏡像并上傳:點擊 Build Image 按鈕,等待鏡像構(gòu)建。構(gòu)建完成后將自動上傳到關(guān)聯(lián)的 UOS APP 的 Multiverse 鏡像中。

打開 UOS 網(wǎng)頁端,在「啟動配置」「鏡像」這里,可以看到剛才構(gòu)建的服務(wù)端程序,已經(jīng)自動上傳了。

在 Build Settings 窗口這里,可以看到在構(gòu)建鏡像時,Platform 已經(jīng)自動被切換至 Linux 平臺了。當然,大家也可以手動切換至 Linux 平臺。

3.4 創(chuàng)建啟動配置

我們以 Multiverse 的按需模式為例,需要在「Multiverse -> 啟動配置」頁面創(chuàng)建一個啟動配置,啟動配置是開啟服務(wù)的配置,包含開啟服務(wù)所需要 CPU、內(nèi)存限制,啟動參數(shù),服務(wù)器端口,以及運行程序入口等。

「啟動配置」頁面,點擊「立即創(chuàng)建」

填寫配置名稱,示例為 mvdemo-websocket ,大家也可自定義,點擊「創(chuàng)建」。

點擊「添加已有鏡像

在彈出窗口中,為當前的啟動配置添加鏡像時,首先選擇需要使用的鏡像,在這里我們選擇 image1-websocket 。

然后設(shè)置鏡像啟動參數(shù):

  • CPU核數(shù):游戲服務(wù)器根據(jù)需要,可自行設(shè)置 CPU 核數(shù)。Unity 程序運行時我們推薦設(shè)置為 0.5 核以上。

  • 啟動超時時長:如果 Multiverse 在這個時長內(nèi)未收到 GameServer 調(diào)用 MultiverseSdk.Ready() 的信號, 就會標記該 Allocation 為 failed 狀態(tài)。

  • 入口程序啟動命令:填 server.x86_64,該參數(shù)指的是服務(wù)器可執(zhí)行文件在 zip 包中的相對路徑。

  • 服務(wù)器端口:使用 WebSocket 時,選擇 TCP 協(xié)議。游戲服務(wù)器端口默認將填寫端口 9998,我們修改端口為 7777,端口號只要和 Unity 編輯器中自定義的端口號保持一致即可。

填寫完后,點擊「添加鏡像配置」,將該鏡像添加至啟動配置上。

3.5 測試并應(yīng)用鏡像配置

鏡像配置添加成功后,建議點擊「立即測試配置」,以確保程序在剛剛的配置下可以正常運行。

測試成功后可選擇「下一步」,并點擊「應(yīng)用鏡像配置」,點擊完成,進入 mvdemo-websocket 的詳情頁面。

在詳情頁面,可以進行添加鏡像配置、更新啟動參數(shù)、測試配置、刪除配置等一系列操作。

注意:此處不建議應(yīng)用測試失敗的鏡像配置,如鏡像配置測試運行失敗,請查看日志信息,并嘗試修改啟動參數(shù)來修復(fù)其中的錯誤。測試服務(wù)可手動關(guān)閉,若不關(guān)閉測試,系統(tǒng)將在一小時后清除運行的測試實例。

應(yīng)用鏡像配置會將當前鏡像啟動參數(shù)所使用的配置版本替換為該鏡像配置。一個啟動配置只會有唯一一個應(yīng)用狀態(tài)的鏡像配置。

3.6 配置 Matchmaking

Matchmaking 是 UOS 提供的用于多人聯(lián)機游戲的可定制配對服務(wù)。通過 Matchmaking,您可以創(chuàng)建自定義的匹配規(guī)則,來定義您的多人聯(lián)機游戲中的游戲?qū)值耐婕医M成和陣營劃分,以及在對局匹配的過程中應(yīng)用靈活的調(diào)整策略來對匹配規(guī)則進行動態(tài)更新。

當前綁定的 UOS App 已經(jīng)啟用了「Matchmaking」功能了。需要注意的是,Matchmaking 作為 Multiverse 的支持“玩家匹配"的功能,依賴于 Multiverse 的「房間管理」功能。因此,對于沒有啟用「房間管理」功能的應(yīng)用,在啟用「Matchmaking」后,會自動啟動「房間管理」功能。

啟用「Matchmaking」功能后,可以進入「Matchmaking」的頁面,點擊「立即創(chuàng)建」按鈕就可以開始創(chuàng)建 Match 配置。

大家自行填寫啟動配置 mvdemo-websocket 對應(yīng)的 Match 配置的名稱,并且可以對「超時時長」配置項進行調(diào)整。這里我們先使用默認的超時時長 60 秒。

然后會進入模板選擇頁面,我們基于市面上常見的游戲玩法及其匹配機制為用戶事先準備了一系列的 Match 配置的模板。

在本教程中,我們選擇「自由休閑類」「快速啟動」模板為例進行講解。選擇了模板后,點擊「創(chuàng)建」

在 Match 配置的詳情頁中,大家可以對配置模板所提供的匹配信息進行調(diào)整,也可以更換新的模板,或者是對相關(guān)配置項進行調(diào)整。

teamDefinitions 聲明了一局游戲的隊伍規(guī)格和屬性,示例模板中定義的該匹配規(guī)則的含義是:隊伍名為 quick-game 的一局對局,最少玩家數(shù)為 1,最大玩家數(shù)為 4,如果達到了 minPlayers, 則可認為該隊伍滿足了匹配條件。

3.7 開啟地域

到這里,大家已經(jīng)通過教程創(chuàng)建了一個已應(yīng)用鏡像、并可以正常運行的啟動配置了,接下來點擊側(cè)邊欄的「房間/服務(wù)器」??梢钥吹?,此時尚未啟用任何地域,也未分配服務(wù)器,點擊「啟用」。

并填寫最大服務(wù)數(shù),可直接點擊「確認」,則會以默認值 10 為最大服務(wù)數(shù)完成開啟。

在啟用地域「上?!?/strong>后,意味著您接下來的服務(wù)器可以創(chuàng)建在上海地區(qū),不同地域可以滿足不同的部署需求。

此時已經(jīng)可以先在 Unity 編輯器中,點擊運行進行測試。會看到客戶端日志輸出信息,說明客戶端完成了初始化 matchmaking sdk 和執(zhí)行了玩家登陸的操作。

4. 使用 Matchmaking 實現(xiàn)對局匹配

接下來看看使用 Matchmaking 是如何實現(xiàn)對局匹配功能的!

4.1 創(chuàng)建 Ticket 實現(xiàn)輪詢匹配

繼續(xù)在腳本 BootstrapManager.cs 中添加 OnStartMatchButton 方法,來實現(xiàn)當點擊「開始匹配」的按鈕時要響應(yīng)的事件。

  • 定義變量 startButton,表示開始匹配的 UI 按鈕。當玩家點擊開始匹配的 UI 按鈕時,代碼會進行一系列操作,包括更新按鈕文字和狀態(tài)。UI 上文字將顯示匹配中......,并且此時按鈕變?yōu)椴豢牲c擊的狀態(tài)。

  • 當玩家想要開啟一局匹配時,就需要在客戶端發(fā)起一個 Ticket。在這里,客戶端會根據(jù) Matchmaking 的匹配配置 Id,調(diào)用 CreateTicketAsync 方法來創(chuàng)建一個 Ticket ,發(fā)起匹配請求。需要傳入兩個參數(shù):Matchmaking 的匹配配置 Id,以及玩家列表 List。

  • 稍后會給大家詳細解釋開啟協(xié)程去輪詢 GetTicket 等待匹配結(jié)果的方法 WaitForMatchResult,我們同時會使用 try...catch... 來處理捕獲到的異常情況。

//需要新引入的 namespace--------------------------------------
using UnityEngine.UI;
using TMPro;
using Unity.UOS.Matchmaking.Model;
using System.Collections.Generic;
using Unity.UOS.Matchmaking.Exception;
//需要新引入的 namespace--------------------------------------

namespace Unity.UOS.Multiverse.Samples.NetCode
{
public class BootstrapManager : MonoBehaviour
{
// 填寫Matchmaking的配置列表中的配置ID
public string matchmakingConfigId;
// 填寫Matchmaking配置中的玩家匹配請求超時時長
public int matchmakingTimeoutSeconds = 120;
private readonly string _userId = Guid.NewGuid().ToString();
public Button startButton; //開始匹配的UI按鈕
private Coroutine matchCoroutine;

// 點擊開始匹配按鈕的響應(yīng)事件
public async void OnStartMatchButton()
{
// Change text to "匹配中......"
startButton.transform.GetChild(0).GetComponent ().text = "匹配中......";
// Change button to non-clickable state
startButton.interactable = false;

var player = new Player()
{
id = _userId
};

try
{
//創(chuàng)建一個 Ticket 開始匹配,傳入?yún)?shù):Matchmaking匹配配置Id,玩家列表
var ticketId =
await MatchmakingSDK.Instance.CreateTicketAsync(matchmakingConfigId, new List { player });
// 開啟一個 coroutine 去輪詢 GetTicket 等待匹配結(jié)果
matchCoroutine ??= StartCoroutine(WaitForMatchResult(ticketId));
}
catch (MatchmakingClientException ex)
{
Debug.LogErrorFormat("Failed to create ticket, clientEx: {0}", ex);
throw;
}
catch (MatchmakingServerException ex)
{
Debug.LogErrorFormat("Failed to create ticket, serverEx: {0}", ex);
throw;
}
}
}
}

此時也是需要引入系統(tǒng)自帶的 TextMeshPro Package 資源包的應(yīng)用程序集,添加引用后,點擊「Apply

打開網(wǎng)易新聞 查看精彩圖片

接著,我們要開啟一個 coroutine 去輪詢 GetTicket 等待匹配結(jié)果了,封裝協(xié)程方法 WaitForMatchResult 來實現(xiàn)該功能。

  • 將根據(jù)配置的玩家匹配請求超時時長,也就是參數(shù) matchmakingTimeoutSeconds 設(shè)置的時長,每間隔 1 秒調(diào)用一次自定義封裝的 GetTicketAsync 方法,來輪詢 GetTicket 等待匹配結(jié)果。

  • 添加異步方法 GetTicketAsync ,在方法內(nèi)實現(xiàn)查詢指定 tickerId 的 Ticket 信息。

  • 每次查詢 Ticket 信息時,要等待任務(wù)完成,如果檢查到任務(wù)出錯,就拋出異常并跳出循環(huán)。

  • 然后獲取任務(wù)的結(jié)果,為了方便調(diào)試,我們可以打印一下當前 Ticket 的狀態(tài)。如果檢查到 Ticket 的狀態(tài)為匹配完成或者錯誤,則跳出循環(huán)、退出輪詢。

//需要新引入的 namespace--------------------------------------
using System.Collections;
using System.Threading.Tasks;
//-----------------------------------------------------------

namespace Unity.UOS.Multiverse.Samples.NetCode
{
private IEnumerator WaitForMatchResult(string ticketId)
{
// 輪詢 GetTicket 等待匹配結(jié)果
Ticket ticket = null;
for (var i = 0; i < matchmakingTimeoutSeconds; i++)
{
var task = GetTicketAsync(ticketId);//查詢指定 ticket id 的詳細信息
yield return new WaitUntil(() => task.IsCompleted);

if (task.IsFaulted)
{
if (task.Exception != null) throw task.Exception;
break;
}

ticket = task.Result;
Debug.Log($"Current ticket status: {ticket.status}");

if (ticket.status is MatchmakingSDK.TicketStatusMatched or MatchmakingSDK.TicketStatusError)
{
break;
}

// 延時1秒
yield return new WaitForSeconds(1);
}
}
// 查詢指定 ticket id 的詳細信息
private async Task GetTicketAsync( string ticketId)
{
try
{
return await MatchmakingSDK.Instance.GetTicketAsync(ticketId);
}
catch (MatchmakingClientException ex)
{
Debug.LogErrorFormat("Failed to get ticket, clientEx: {0}", ex);
throw;
}
catch (MatchmakingServerException ex)
{
Debug.LogErrorFormat("Failed to get ticket, serverEx: {0}", ex);
throw;
}
}
 }

如果輪詢 Ticket 的過程中,捕獲到異常,需要做一些清除資源的操作。所以,封裝一個方法 Cleanup,來處理遇到異常時停止協(xié)同程序的執(zhí)行。在代碼中添加調(diào)用 Cleanup 方法的地方:

private void Cleanup()
{
if (matchCoroutine == null) return;
StopCoroutine(matchCoroutine);
matchCoroutine = null;
}

代碼中添加調(diào)用 Cleanup 方法的地方:

  • 在 OnStartMatchButton 方法的 Client /Server 異常時,添加調(diào)用 Cleanup 方法;

  • 在 GetTicketAsync 方法的 Client/Server 異常時,也添加調(diào)用 Cleanup 方法;

  • 在 WaitForMatchResult 方法的輪詢匹配時,如果異步任務(wù)出錯(task.IsFaulted),則調(diào)用 Cleanup 方法停止協(xié)程函數(shù)的調(diào)用。

// 點擊開始匹配按鈕的響應(yīng)事件
public async void OnStartMatchButton()
{
//此處省略其它代碼行......
try
{
//此處省略其它代碼行......
}
catch (MatchmakingClientException ex)
{
Debug.LogErrorFormat("Failed to create ticket, clientEx: {0}", ex);
Cleanup();
throw;
}
catch (MatchmakingServerException ex)
{
Debug.LogErrorFormat("Failed to create ticket, serverEx: {0}", ex);
Cleanup();
throw;
      }
}

// 查詢指定 ticket id 的詳細信息
private async Task GetTicketAsync( string ticketId)
{
try
{
return await MatchmakingSDK.Instance.GetTicketAsync(ticketId);
}
catch (MatchmakingClientException ex)
{
Debug.LogErrorFormat("Failed to get ticket, clientEx: {0}", ex);
Cleanup();
throw;
}
catch (MatchmakingServerException ex)
{
Debug.LogErrorFormat("Failed to get ticket, serverEx: {0}", ex);
Cleanup();
throw;
      }
}

private IEnumerator WaitForMatchResult(string ticketId)
{
// 輪詢 GetTicket 等待匹配結(jié)果
Ticket ticket = null;
      for (var i = 0; i < matchmakingTimeoutSeconds; i++)
{
var task = GetTicketAsync(ticketId);//查詢指定 ticket id 的詳細信息
yield return new WaitUntil(() => task.IsCompleted);

if (task.IsFaulted)
{
Cleanup();
if (task.Exception != null) throw task.Exception;
break;
}

          //此處省略其它代碼行......
      }
}

添加完代碼,接著找到場景中的開始匹配的 UI 按鈕對象 Button_StartMatch,要確保此按鈕已經(jīng)在 Inspector 面板上注冊綁定了:BootstrapManager 對象上的腳本 BootstrapManager.cs 中的 OnStartMatchButton 方法。

打開網(wǎng)易新聞 查看精彩圖片

回到 UOS 網(wǎng)頁端,找到「Matchmaking」「配置列表」,復(fù)制 mvdemo-websocket 下方的 Matchmaking 配置 ID 號。

打開網(wǎng)易新聞 查看精彩圖片

回到 Unity 編輯器,在 Inspector 面板上的 BootBootstrapManager 組件這里,有幾個參數(shù)需要我們設(shè)置:

  • 將剛才 UOS 網(wǎng)頁端復(fù)制的 Matchmaking 配置 ID ,粘貼到 matchmakingConfigId 參數(shù)這里。

  • matchmakingTimeoutSeconds 參數(shù),在這里也是需要和 UOS 網(wǎng)頁端配置的玩家匹配請求超時時長(60 秒)保持一致。如果在 60 秒結(jié)束之后 Ticket 仍未匹配成功,將被標記為匹配失敗。

  • StartButton:指定為場景中的按鈕 Button_StartMatch。

繼續(xù)點擊運行項目后,查看控制臺輸出的日志信息,可以看到當前 Tickcet 狀態(tài)的變化:由 created → awaitingAssignment → matched

Ticket 共有以下狀態(tài):

  • 匹配已創(chuàng)建 (created):Ticket 已成功創(chuàng)建,并處于正在匹配玩家的狀態(tài)。

  • 匹配成功,正在創(chuàng)建房間 (awaitingAssignment): Ticket 已匹配成功,系統(tǒng)正在為該局游戲創(chuàng)建房間。

  • 匹配成功,且創(chuàng)建房間完成 (matched): 匹配成功并已能夠拿到 IP 和 PORT 等對局信息,可連接到戰(zhàn)斗服開始游戲。

  • 匹配失敗 (error): 匹配超時(匹配時長超出玩家匹配請求超時時長)或是 Ticket 不符合規(guī)則被標記非法 Ticket。

Ticket 狀態(tài)轉(zhuǎn)換關(guān)系如下圖:

4.2 匹配成功后,獲取匹配到的服務(wù)器的 IP 地址和端口號

接下來處理匹配成功后的邏輯!

在這里我們先使用 WebSocket 協(xié)議進行測試,代碼中如果缺少引用會報錯的,我們先提前引入 WebSocket Transport for Netcode 資源包的應(yīng)用程序集」。添加引用后,點擊「Apply」按鈕。

在腳本中定義方法 HandleMatchedTicket 來處理匹配到的 Ticket。

  • 當 Ticket 的狀態(tài)為匹配成功時,會先解析匹配到的房間端口信息(ticket.assignment.gamePorts),得到當前匹配到的服務(wù)器的端口號 port。

  • 然后根據(jù)所使用的網(wǎng)絡(luò)傳輸協(xié)議 NetworkTransport 類型(如 UOS 提供的 KCP 或 WebSocket 協(xié)議)進行相應(yīng)配置,以便客戶端能連接到匹配的服務(wù)器。

  • 當使用 WebSocket 協(xié)議時,先獲取到 WebSocketTransport 組件,然后再將匹配到的服務(wù)器的 IP 地址和端口,填充到 WebSocketTransport 組件上的參數(shù) ConnectAddress 和 Port 。

//需要新引入的 namespace-----------------------------------
using Netcode.Transports.WebSocket;
//--------------------------------------------------------

private void HandleMatchedTicket(Ticket ticket)
{
//ticket匹配到的房間端口,形如 port-name / port
//比如當Multiverse啟動配置只填了一個端口時:websocket / 7654; 配置了多個端口時:websocket / 7654,kcp / 7655
//這里的代碼示例是以只填了一個端口為例
var ports = ticket.assignment.gamePorts.Split(",");
if (ports.Length != 1)
{
throw new Exception("Unexpected number of ports");
}

var portInfo = ports[0].Split("/");
if (portInfo.Length != 2)
{
throw new Exception("Unexpected number of port info");
}

var port = ushort.Parse(portInfo[1]);

switch (NetworkManager.Singleton.NetworkConfig.NetworkTransport)
{
case WebSocketTransport:
{
//如果使用 websocket 協(xié)議,獲取 WebSocketTransport 組件
var wt = NetworkManager.Singleton.GetComponent ();
if (wt == null)
{
throw new Exception("Unexpected null WebSocketTransport");
}

//填充匹配到的服務(wù)器的ip地址和端口到 WebSocketTransport 組件
Debug.Log("Using WebSocket transport");
wt.ConnectAddress = ticket.assignment.ip;
wt.Port = port;

break;
}
default:
throw new Exception("No suitable transport found");
}
}

然后我們找到之前封裝過的 WaitForMatchResult 方法,添加判斷:如果匹配成功時,調(diào)用處理匹配結(jié)果的方法 HandleMatchedTicket。如果捕獲到異常時,就清除相關(guān)的資源。

private IEnumerator WaitForMatchResult(string ticketId)
{
// 輪詢 GetTicket 等待匹配結(jié)果
Ticket ticket = null;
for (var i = 0; i < matchmakingTimeoutSeconds; i++)
{
        //此處省略其它代碼行......
}

if (ticket is { status: MatchmakingSDK.TicketStatusMatched })
{
Debug.Log("匹配成功");
try
{
// 處理匹配結(jié)果
HandleMatchedTicket(ticket);
}
catch (Exception)
{
Cleanup();
throw;
}
}
//此處省略其它代碼行......
}

再次運行游戲后進行測試,查看日志信息會輸出:"Using WebSocket transport",說明使用了 WebSocket 協(xié)議。同時可以查看下 WebSocketTransport 組件的 Connect Address 和 Port 參數(shù),已經(jīng)發(fā)生了變化。

打開網(wǎng)易新聞 查看精彩圖片

刷新 UOS 網(wǎng)頁端的服務(wù)器列表,可以看到自動創(chuàng)建的服務(wù)器信息。點擊服務(wù)器 UUID 值查看服務(wù)器詳情,可以獲取到服務(wù)器的 IP 與端口(使用 Multiverse 映射后的端口)。

  • 配置中的服務(wù)器端口為內(nèi)部監(jiān)聽端口,連接時會使用 Multiverse 映射后的端口。

  • 如下圖,配置時填的服務(wù)器監(jiān)聽端口為 7777,但是連接時將使用映射后的端口 7005 進行連接。

4.3 匹配成功后,客戶端連接服務(wù)器

繼續(xù)在 HandleMatchedTicket 方法中添加代碼:

  • 處理下當匹配成功后,開啟客戶端連接到 Multiverse 服務(wù)端,并將開始匹配的 UI 按鈕隱藏,在場景中會出現(xiàn)一個 Sphere 小球?qū)ο蠛鸵粋€可以控制隨機小球位置的按鈕。

  • 在這里,我們定義一個變量 randomTeleport 表示隨機小球位置的 UI 按鈕。

public Button randomTeleport;//隨機位置的UI按鈕

private void HandleMatchedTicket(Ticket ticket)
{
//此處省略其它代碼行......
switch (NetworkManager.Singleton.NetworkConfig.NetworkTransport)
{
//此處省略其它代碼行......
}

// 開啟客戶端,連接到server
NetworkManager.Singleton.StartClient();

//隱藏匹配的按鈕
startButton.gameObject.SetActive(false);
//激活隨機位置的按鈕
if (NetworkManager.Singleton.IsClient && NetworkManager.Singleton.LocalClient != null)
{
randomTeleport.gameObject.SetActive(true);
}
}

BootstrapManager.cs 組件上的參數(shù) Random Teleport 選擇提前創(chuàng)建好的按鈕對象:Button_RandomTeleport。

打開網(wǎng)易新聞 查看精彩圖片

此時再次運行游戲后,看到在 Game 窗口中出現(xiàn)了一個 Sphere 對象,說明客戶端連接上了服務(wù)器。

打開網(wǎng)易新聞 查看精彩圖片

4.4 匹配失敗后,則重新匹配

如果匹配失敗,則重新開始剛才的匹配過程。在 WaitForMatchResult 方法中,添加重新開始匹配的代碼,同時會調(diào)用 Cleanup 方法來停止當前的匹配操作。

private IEnumerator WaitForMatchResult(string ticketId)
{
Ticket ticket = null;
for (var i = 0; i < matchmakingTimeoutSeconds; i++)
{
//此處省略其它代碼行......
}
if (ticket is { status: MatchmakingSDK.TicketStatusMatched })
{
//此處省略其它代碼行......
}
else
{
if (ticket != null) Debug.Log($"匹配失敗: {ticket.assignment.msg}");
// Restore button to matchable state
startButton.transform.GetChild(0).GetComponent ().text = "開始匹配";
startButton.interactable = true;
// Match failed, throw an exception
Cleanup();
throw new Exception("Match failed");
}
}
4.5 匹配成功后,在客戶端隨機小球?qū)ο蟮奈恢?/strong>

繼續(xù)打開腳本 BootstrapManager.cs,添加新的方法 OnRandomTeleportButton,判斷下如果本地客戶端的玩家對象包含 BootstrapPlayer 組件,則會從客戶端向服務(wù)器端發(fā)送一個遠程過程調(diào)用(Server RPC),目的是將玩家傳送到服務(wù)器端的一個隨機位置。

//當點擊隨機位置的按鈕響應(yīng)的事件
public void OnRandomTeleportButton()
{
if (NetworkManager.Singleton.LocalClient.PlayerObject.TryGetComponent(out BootstrapPlayer bootstrapPlayer))
{
// Invoke a `ServerRpc` from client-side to teleport player to a random position on the server-side
bootstrapPlayer.RandomTeleportServerRpc();
}
}

有需要的話,可自行查看下 Netcode 示例 Demo 提供的隨機位置的腳本 BootstrapPlayer.cs。這段代碼定義了一個服務(wù)器遠程過程調(diào)用(Server RPC)方法 RandomTeleportServerRpc,用于將調(diào)用該方法的游戲?qū)ο?、傳送?XY 平面上的一個隨機位置。

public class BootstrapPlayer : NetworkBehaviour
{
[ServerRpc]
public void RandomTeleportServerRpc()
{
var oldPosition = transform.position;
transform.position = GetRandomPositionOnXYPlane();
var newPosition = transform.position;
print($"{nameof(RandomTeleportServerRpc)}() -> {nameof(OwnerClientId)}: {OwnerClientId} --- {nameof(oldPosition)}: {oldPosition} --- {nameof(newPosition)}: {newPosition}");
}

private static Vector3 GetRandomPositionOnXYPlane()
{
return new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), 0f);
}
}

查看場景中隨機位置的 UI 按鈕 Button_RandomTeleport,確保它已經(jīng)提前注冊綁定了方法 OnRandomTeleportButton。

打開網(wǎng)易新聞 查看精彩圖片

然后大家可以再次點擊運行測試,匹配成功以后,就可以隨機小球的位置了。

4.6 啟用 Websocket Proxy

如果想要在微信小游戲中使用 WebSocket 協(xié)議進行通信,需要「聯(lián)系我們」,為你的項目開通「啟用 Websocket Proxy」功能。

在 HandleMatchedTicket 方法中添加判斷的代碼,若開啟了 WebSocket 代理選項,系統(tǒng)會自動在 WebSocketTransport 組件的 “Proxy” 這一相關(guān)配置字段中填入 WebSocket 代理服務(wù)器的地址。這一過程無需開發(fā)人員手動輸入,系統(tǒng)將自動完成配置。

如此一來,在進行 WebSocket 通信時,便能正確連接到指定的代理服務(wù)器,進而實現(xiàn)預(yù)期的網(wǎng)絡(luò)通信和功能邏輯。

private void HandleMatchedTicket(Ticket ticket)
{
    //此處省略其它代碼行......

switch (NetworkManager.Singleton.NetworkConfig.NetworkTransport)
{
case WebSocketTransport:
{
//如果使用 websocket 協(xié)議,獲取 WebSocketTransport 組件
var wt = NetworkManager.Singleton.GetComponent ();
if (wt == null)
{
throw new Exception("Unexpected null WebSocketTransport");
}

//填充匹配到的服務(wù)器的ip地址和端口到 WebSocketTransport 組件
Debug.Log("Using WebSocket transport");
wt.ConnectAddress = ticket.assignment.ip;
wt.Port = port;

if (!string.IsNullOrEmpty(ticket.assignment.wsProxy))
{
// 如果聯(lián)系我們配置了開啟websocket代理, 則填充代理地址到 WebSocketTransport 組件
Debug.Log("Using WebSocket proxy");
wt.Proxy = ticket.assignment.wsProxy;
wt.SecureConnection = true;
}
break;
}
}
}

找到場景中的 BootstrapManager 對象上的 WebSocket Transport 組件,勾選「Secure Connection」選項。

如果你的項目已經(jīng)開啟 Websocket 代理, 運行項目后,會看到 WebSocketTransport 組件的 Proxy 參數(shù)上,已經(jīng)自動填充了代理地址。

4.7 增加關(guān)閉的按鈕

可以在場景中增加一個 Close 的關(guān)閉,隨時來關(guān)閉場景重新運行匹配功能。大家自行根據(jù)自己的需求,選擇是否添加這個按鈕。

創(chuàng)建一個新的按鈕,重命名為 Button_Close,可自行設(shè)置按鈕上的貼圖和顏色。

將按鈕上的 Text 文字設(shè)置為 X。

腳本中添加 OnClickCloseButton 方法,主要功能是點擊關(guān)閉按鈕時,進行資源清理、關(guān)閉網(wǎng)絡(luò)管理器并銷毀其對應(yīng)的游戲?qū)ο?,最后異步加載索引為 0 的場景,以實現(xiàn)場景的切換和資源的釋放。

//需要新引入的 namespace------------------------------------
using UnityEngine.SceneManagement;
//---------------------------------------------------------

public void OnClickCloseButton()
{
Cleanup();
if (NetworkManager.Singleton)
{
NetworkManager.Singleton.Shutdown();
Destroy(NetworkManager.Singleton.gameObject);
}
SceneManager.LoadSceneAsync(0, LoadSceneMode.Single);
}

找到場景中的關(guān)閉的 UI 按鈕對象 Button_Close,要確保此按鈕已經(jīng)在 Inspector 面板上注冊綁定了:BootstrapManager 對象上的腳本 BootstrapManager.cs 中的 OnStartMatchButton 方法。

打開網(wǎng)易新聞 查看精彩圖片

可以運行場景,再次進行測試效果。

5. 使用 KCP 協(xié)議配置并啟動項目

為助力開發(fā)者在微信小游戲平臺實現(xiàn)更優(yōu)網(wǎng)絡(luò)通信體驗,我們特別提供 KCP 協(xié)議。針對微信獨特的 UDP 通信環(huán)境,我們進行了深度適配工作,成功讓 KCP 協(xié)議無縫融入其中,保障其在微信小游戲平臺穩(wěn)定且高效地運行。

5.1 選擇 KCP 協(xié)議

我們再來測試下使用 KCP 協(xié)議來運行時所需要的相關(guān)配置!

再次找到場景中的對象 BootstrapManager,給其添加 Kcp 2k Transport 協(xié)議腳本組件 ,同時將 NetworkManager 組件上的 NetworkTransport 參數(shù)選擇的協(xié)議修改為:Kcp 2k Transport 協(xié)議

打開網(wǎng)易新聞 查看精彩圖片

5.2 構(gòu)建并上傳 Multiverse 鏡像

更換協(xié)議之后,我們按照之前的步驟,重新構(gòu)建新的服務(wù)器鏡像來測試。

鏡像標簽 Image Tag 設(shè)置為:image2-kcp,然后點擊「Build Image」按鈕。

打開網(wǎng)易新聞 查看精彩圖片

在 UOS 網(wǎng)頁端的「啟動配置」「鏡像」這里,可以看到剛才構(gòu)建的服務(wù)端程序 image2-kcp,已經(jīng)自動上傳了。

打開網(wǎng)易新聞 查看精彩圖片

5.3 創(chuàng)建啟動配置

多套啟動配置可以方便管理同一服務(wù)器程序?qū)τ诓煌?wù)所需的啟動參數(shù),CPU 消耗等不同配置的需求。

同理參考使用 WebSocket Transport 協(xié)議時的操作步驟,再創(chuàng)建一個使用 KCP 協(xié)議的啟動配置。在「啟動配置」頁面,點擊「創(chuàng)建啟動配置

打開網(wǎng)易新聞 查看精彩圖片

填寫配置名稱,示例為 mvdemo2-kcp,然后點擊「創(chuàng)建」。

打開網(wǎng)易新聞 查看精彩圖片

點擊「添加已有鏡像

打開網(wǎng)易新聞 查看精彩圖片

接著為啟動配置 mvdemo2-kcp 添加鏡像配置:

  • 選擇鏡像:image2-kcp;

  • CPU核數(shù):Unity 程序運行時我們還是推薦設(shè)置為 0.5 核以上;

  • 入口程序啟動命令:填 server.x86_64;

  • 服務(wù)器端口:使用 KCP 協(xié)議時,服務(wù)器協(xié)議選擇 UDP 協(xié)議。游戲服務(wù)器端口我們還是和 Unity 編輯器中自定義的端口號保持一致(7777)即可。

填寫完后,點擊「添加鏡像配置」,將該鏡像添加至啟動配置上。

5.4 測試并應(yīng)用鏡像配置

接著點擊「立即測試配置」,然后點擊「下一步」「立即應(yīng)用配置」即可。

5.5 配置 Matchmaking

進入「Matchmaking」「配置列表」頁面,點擊「創(chuàng)建 Match」按鈕。

這里「Match 配置名稱」就先填寫 :mvdemo2-kcp 了,「玩家匹配請求超時時長」也設(shè)置為默認 60 秒。

Match 模板還是選擇「自由休閑類」「快速啟動」模板為例進行講解。選擇了模板后,點擊「創(chuàng)建」。

在 Match 配置的詳情頁中,這里的匹配信息和之前一樣,不再重復(fù)解釋了。

5.6 獲取匹配到的服務(wù)器的 IP 地址和端口

此時也是需要先設(shè)置下,引入 Kcp Transport for Netcode 資源包的應(yīng)用程序集。當添加引用后,點擊「Apply」。

找到腳本中的 HandleMatchedTicket 方法,繼續(xù)判斷:如果使用的是 KCP 協(xié)議的話,同理也是通過 Ticket 拿到匹配到的服務(wù)器的 IP 地址和端口號,然后填充到 Kcp Transport 組件上的參數(shù) Host 和 Port。

//需要新引入的 namespace------------------------------------
using Netcode.Transports.KCP;
//--------------------------------------------------------

private void HandleMatchedTicket(Ticket ticket)
{
    //此處省略其它代碼行......

switch (NetworkManager.Singleton.NetworkConfig.NetworkTransport)
{
case Kcp2kTransport:
{
//如果使用 kcp 協(xié)議,獲取 Kcp2kTransport 組件
var kt = NetworkManager.Singleton.GetComponent ();
if (kt == null)
{
throw new Exception("Unexpected null Kcp2kTransport");
}

Debug.Log("Using KCP transport");

//填充匹配到的服務(wù)器的ip地址和端口到 Kcp2kTransport 組件
kt.Port = port;
kt.Host = ticket.assignment.ip;
break;
}
case WebSocketTransport:
{
//此處省略其它代碼行......
break;
}
default:
throw new Exception("No suitable transport found");
}
//此處省略其它代碼行......
}

進入 UOS 網(wǎng)頁端的「Matchmaking」「配置列表」頁面,找到新創(chuàng)建的 Matchmaking 配置:mvdemo2-kcp,復(fù)制 mvdemo2-kcp 下方的 Matchmaking 配置 ID 號。

打開網(wǎng)易新聞 查看精彩圖片

將剛才 UOS 網(wǎng)頁端復(fù)制的 Matchmaking 配置 ID ,粘貼到 matchmakingConfigId 參數(shù)這里。

matchmakingTimeoutSeconds 參數(shù):還是和 Matchmaking 配置中的玩家匹配請求超時時長保持一致寫 60 秒。

打開網(wǎng)易新聞 查看精彩圖片

再次運行游戲后進行測試,查看日志信息會輸出:"Using KCP transport",說明使用了 KCP 協(xié)議。

同時看到 Kcp 2k Transport 組件的 Host 和 Port 參數(shù),也已經(jīng)自動填充了匹配到的服務(wù)器的 IP 地址和 Multiverse映射后的端口號。

打開網(wǎng)易新聞 查看精彩圖片

再次刷新 UOS 網(wǎng)頁端的服務(wù)器列表,點擊服務(wù)器 UUID 值查看服務(wù)器詳情,可以看到 Unity 編輯器 Inspector 面板上的服務(wù)器的 IP 與端口,和網(wǎng)頁端的是一致的。

打開網(wǎng)易新聞 查看精彩圖片

此時,Game 窗口的畫面如下,可以正常隨機位置。

打開網(wǎng)易新聞 查看精彩圖片

6. 構(gòu)建 MiniGame 項目,設(shè)置 WXSDK 打包參數(shù)

接下來我們將該 Demo 發(fā)布成微信小游戲,接著剛才的 KCP 協(xié)議繼續(xù)操作。使用 Unity 引擎發(fā)布成微信小游戲的話,需要安裝 WebGL 平臺支持模塊包和 WXSDK,然后再構(gòu)建小游戲項目工程。

6.1 安裝 WebGL 平臺支持模塊包,將項目切換至 WebGL 平臺

再次打開 Unity Hub ,找到左側(cè)的「安裝」,選擇自己使用的 Unity 編輯器版本,比如 2022.3.53f1c1,點擊「添加模塊」按鈕:

打開網(wǎng)易新聞 查看精彩圖片

在彈出窗口中「勾選 WebGL Build Support 模塊,然后點擊「安裝」。

打開網(wǎng)易新聞 查看精彩圖片

在 UOS Launcher 面板中勾選 Weixin Minigame 選項,點擊彈窗中的按鈕「Switch to WebGL」。

打開網(wǎng)易新聞 查看精彩圖片

然后打開「File」「Build Settings」窗口,可以看到勾選 Weixin Minigame 選項后,Platform 已經(jīng)自動幫你切換至「WebGL」平臺了。

切換平臺后,需要重新檢查下你的 UOS Launcher 面板,查看 UOS App 是否綁定。貼圖的壓縮模式 Texture Compression:選擇「ASTC。

打開網(wǎng)易新聞 查看精彩圖片

6.2 安裝 WXSDK

在 Unity Editor 菜單欄,點擊「Window」「Package Manager」,點擊左上角的「+」,選擇「Add package from git URL...」,輸入下方的 WXSDK 的倉庫 Git 資源地址,點擊「Add」即可。

WXSDK 的倉庫 Git 地址:

https://gitee.com/wechat-minigame/minigame-tuanjie-transform-sdk.git

打開網(wǎng)易新聞 查看精彩圖片

等待 WXSDK 安裝好以后,可以在Package Manager窗口中看到。

打開網(wǎng)易新聞 查看精彩圖片

6.3 設(shè)置 WXSDK 的參數(shù),構(gòu)建小游戲項目工程

安裝好 WXSDK 后,點擊菜單欄的按鈕「微信小游戲」「轉(zhuǎn)換小游戲」按鈕。

打開網(wǎng)易新聞 查看精彩圖片

然后設(shè)置微信小游戲轉(zhuǎn)換工具面板的參數(shù),如下面的截圖所示:

打開網(wǎng)易新聞 查看精彩圖片

游戲AppID:可以直接從微信小游戲開發(fā)工具里點擊 register 去申請正式 AppID。注冊過賬號以后,可以在「開發(fā)管理」「開發(fā)設(shè)置」這里看到自己的 AppID 賬號,復(fù)制一下 AppID 號填入 WXSDK 的參數(shù)中。

注冊鏈接https://mp.weixin.qq.com/wxopen/waregister?action=step1

第一次注冊好的 AppID 賬號,需要進行設(shè)置下是用于「小程序開發(fā)」還是「小游戲開發(fā)」。

設(shè)置修改:登錄小程序頁面后,選擇「首頁」「小程序發(fā)布流程」「小程序類目」,然后點擊填寫添加服務(wù)類目,選擇「游戲」類目,自行選擇一種游戲類型即可,在這里我設(shè)置的是「休閑游戲」類型。

打開網(wǎng)易新聞 查看精彩圖片
打開網(wǎng)易新聞 查看精彩圖片

  • 小游戲項目名:自定義填寫導出的微信小游戲項目名稱,這里先寫 MultiverseAndNetcode_MiniGame。

  • 游戲方向:選擇根據(jù)游戲畫面選擇豎屏還是橫屏,這里選擇橫屏 Landsape。

  • 導出路徑:這里大家自行來設(shè)置生成微信小游戲工程的位置,后續(xù)步驟會需要打開這里的小游戲目錄。

  • 首包資源加載方式:由于當前項目只用到了 Multiverse 和 Matchmaking 服務(wù),沒有使用到 CDN 服務(wù),所以先將首包的資源加載方式設(shè)置為「小游戲包內(nèi)」加載。

  • 壓縮首包資源:由于場景中 UI 用到了背景圖資源,卻未使用 CDN 上傳資源,直接打包的話會提示首包資源太大。所以我們這里可以勾選下「壓縮首包資源」選項。

  • WebGL2.0(beta):這里參數(shù)打勾。

  • 顯示優(yōu)化建議彈窗:這里先不打勾,后面在微信開發(fā)者工具中運行測試項目時,就暫時不讓它自動彈出優(yōu)化提示的窗口。如果需要時,可以再勾選。

  • 最后,點擊「生成并轉(zhuǎn)換」的按鈕,開始打包項目,等到轉(zhuǎn)換成功后,會看到小游戲?qū)С雎窂较碌奈募鐖D所示:

7. 在微信開發(fā)者工具運行測試

7.1 下載微信開發(fā)者工具

如果你還沒有下載過微信開發(fā)者工具的話,可以前往微信官方網(wǎng)站進行下載微信開發(fā)者工具, 并安裝到本地電腦上。

https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

7.2 在微信開發(fā)者工具中導入小游戲項目

打開安裝好的微信開發(fā)者工具,選擇「小游戲」類型,導入你的項目。「目錄」這里選擇之前在 WXSDK 的參數(shù)面板中設(shè)置的導出路徑:

MultiverseAndNetcode_minigame/minigame 路徑文件夾。

注意不要選錯了目錄哦!路徑一定要選擇到 minigame 路徑文件夾?。?!

當選擇完目錄以后,會自動加載填入你在 Unity 編輯器內(nèi)設(shè)置的導出的小游戲項目名稱的,不需要自己填寫。然后選擇你的 AppID,最后點擊「確定」按鈕即可。

接著在 Unity 編輯器里和微信開發(fā)者工具中,我們都點擊「開始匹配」按鈕,匹配成功后,會看到都出現(xiàn)了 Sphere 游戲?qū)ο?,點擊隨機位置,可以看到這兩個對象在場景中的 Transform 組件的數(shù)據(jù)信息都是同步的。

7.3 設(shè)置域名

如果此時,想要在手機上掃描二維碼進行真機預(yù)覽小游戲效果的話,還需要進行相關(guān)域名的設(shè)置。如果大家想知道自己缺少的域名,可以在微信開發(fā)者工具里強制開啟白名單調(diào)試:

  • 打開「設(shè)置->項目設(shè)置」面板,選擇「本地設(shè)置」,取消勾選”不校驗合法域名、網(wǎng)絡(luò)視圖(業(yè)務(wù)域名)、TLS 版本以及 HTTPS 證書“(默認是勾選狀態(tài),表示默認不進行校驗域名)。

可以進入 UOS 官網(wǎng),在網(wǎng)頁上的 QA 的「4 .小程序/小游戲需要用到的域名白名單」這里,找到項目中使用到的 服務(wù)對應(yīng)的域名,然后復(fù)制域名。 https://uos.unity.cn/doc/others/qa#4

  • Matchmaking 服務(wù)對應(yīng)的域名——https://m.unity.cn

  • Passp...