《火拼24》系列教程第二章內(nèi)容來(lái)襲!在首章教程的基礎(chǔ)上,本節(jié)將深度揭秘如何運(yùn)用 UOS 的 Remote Config 服務(wù),實(shí)現(xiàn)游戲關(guān)卡難度的動(dòng)態(tài)調(diào)控——手把手教你通過(guò)遠(yuǎn)程配置靈活修改關(guān)卡參數(shù),無(wú)需迭代新版本即可實(shí)時(shí)優(yōu)化玩家體驗(yàn),讓游戲平衡性調(diào)整更高效、更智能!
教程視頻
項(xiàng)目工程獲取與學(xué)習(xí)指引
1. 初始項(xiàng)目工程下載
倉(cāng)庫(kù)地址:
https://cnb.cool/unity/uos/Rush24Tutorial/-/tree/lesson2-start
分支名稱:lesson2-start 分支
分支說(shuō)明:請(qǐng)先下載 lesson2-start 分支的項(xiàng)目工程,該分支是本節(jié)學(xué)習(xí)的起點(diǎn)。
倉(cāng)庫(kù)地址:
https://cnb.cool/unity/uos/Rush24Tutorial/-/tree/lesson2-end
分支名稱:lesson2-end 分支
分支說(shuō)明:lesson2-end 分支包含本節(jié)所有功能的完整實(shí)現(xiàn)代碼,建議在學(xué)習(xí)完成后參考或用于調(diào)試對(duì)照。
教程學(xué)習(xí)大綱
1. 使用 Remote Config 在服務(wù)端配置關(guān)卡數(shù)據(jù)
2. 封裝獲取遠(yuǎn)程配置數(shù)據(jù)的腳本工具類
3. 使用 Remote Config 的數(shù)據(jù)來(lái)動(dòng)態(tài)調(diào)控游戲關(guān)卡難度
教程操作步驟
接下來(lái)我們要開始本節(jié)課的學(xué)習(xí)啦!
1. 使用 Remote Config 在服務(wù)端配置關(guān)卡數(shù)據(jù)
1.1 下載并打開項(xiàng)目工程
請(qǐng)先通過(guò)上面提供的 git 倉(cāng)庫(kù)鏈接,下載初始狀態(tài)(lesson2-start 分支)的項(xiàng)目工程。
然后通過(guò) Unity Hub,打開剛剛下載好的項(xiàng)目工程。在教程中,我們是使用Unity 2022.3.42 f1c1版本來(lái)打開項(xiàng)目工程的,該教程同樣適用于團(tuán)結(jié)引擎,請(qǐng)大家自行選擇想要使用的版本來(lái)開始你的學(xué)習(xí)。
打開項(xiàng)目工程以后,首先確保你的項(xiàng)目已經(jīng)綁定了第一章節(jié)教程中設(shè)置過(guò)的同一個(gè)UOS App,因?yàn)槟莻€(gè) UOS App 中已經(jīng)開通過(guò) Passport 服務(wù)了。
我們會(huì)在第一節(jié)教程的功能的基礎(chǔ)上,繼續(xù)來(lái)添加如何實(shí)現(xiàn)在服務(wù)端動(dòng)態(tài)調(diào)整游戲關(guān)卡難度!
1.2什么是 Remote Config
Remote Config 是可以和任意 UOS 應(yīng)用搭配使用的遠(yuǎn)程配置模塊,支持開發(fā)者動(dòng)態(tài)更改游戲配置,以減少不必要的打包發(fā)版次數(shù)。
一些使用Remote Config的例子如下:
A/B Testing 和新功能上線;
可集成 CDN 進(jìn)行灰度測(cè)試;
提前設(shè)定好時(shí)間的節(jié)日推廣/限時(shí)活動(dòng);
白名單/黑名單,或根據(jù)玩家標(biāo)簽(Tag)提供差異化體驗(yàn)。
1.3 開通Remote Config 服務(wù)并安裝 SDK
在 UOS Launcher 的下拉服務(wù)窗口列表中,找到「Remote Config」,點(diǎn)擊「Enable」開啟服務(wù)。
然后點(diǎn)擊「Install SDK」,安裝 Remote Config SDK。
1.4 配置關(guān)卡數(shù)據(jù)
1.4.1 配置(Config)的概念
配置(Config)是面向游戲開發(fā)者的概念,是傳統(tǒng)的 Config 形式,以 key-value 的形式存儲(chǔ)在云端,游戲開發(fā)者有權(quán)對(duì) Config 進(jìn)行管理,其中更改是實(shí)時(shí)生效的。
1.4.2 配置 Remote Config
在 UOS 網(wǎng)頁(yè)端,進(jìn)入「Remote Config ->配置管理」頁(yè)面,點(diǎn)擊「配置(CONFIG)」下面的「立即創(chuàng)建」按鈕:
在彈出窗口中,輸入配置的key、類型和對(duì)應(yīng)的值:
名稱:我們輸入 AllStageLevels,表示該配置是游戲關(guān)卡的數(shù)據(jù)。對(duì)于同一 UOS App 來(lái)說(shuō),名稱(key)是不能重復(fù)的。
類型:字段類型有 int、float、long、bool、string、json 六個(gè)選項(xiàng),在這里我們使用 string 類型。
值:暫時(shí)輸入 5,6,7,8 2,3,4,5。表示第一關(guān)的四張牌的數(shù)據(jù)是 5,6,7,8,第二關(guān)的四張牌的數(shù)據(jù)是 2,3,4,5。在這里輸入關(guān)卡數(shù)據(jù)的格式是:兩道題目之間用「空格」分隔,同一道題的四個(gè)數(shù)值之間用「英文逗號(hào)」分隔。
配置完數(shù)據(jù)之后,點(diǎn)擊「添加」按鈕,就可以看到配置好的數(shù)據(jù)了。
1.4.3 覆蓋 ( Override)的概念
我們可以看到在「配置管理」頁(yè)面,還有一個(gè)覆蓋(Override)的配置,等后面用到 Override 時(shí)再詳細(xì)講解它的用法。
先來(lái)了解下覆蓋(Override)的概念:
覆蓋(Override)也是面向游戲開發(fā)者(Developer)的概念,為了在 Config 之上進(jìn)行更加靈活的特異性配置,包括應(yīng)用百分比、生效開始時(shí)間、過(guò)期時(shí)間等。只有游戲開發(fā)者可以對(duì) Override 進(jìn)行管理。
2. 封裝獲取遠(yuǎn)程配置數(shù)據(jù)的腳本工具類
配置好了數(shù)據(jù)以后,我們來(lái)看看代碼中,具體的是如何實(shí)現(xiàn)加載 RemoteConfig 中的數(shù)據(jù)的!
2.1 創(chuàng)建腳本來(lái)存放配置的 key
在下圖所示的路徑下,創(chuàng)建一個(gè)新的腳本文件夾 RemoteConfig,并在該文件夾下創(chuàng)建一個(gè)新的腳本 RemoteConfigKeys.cs。
靜態(tài)類RemoteConfigKeys,用于存放遠(yuǎn)程配置的鍵名常量。
定義一個(gè)常量字符串 AllStageLevels,表示所有關(guān)卡的配置鍵名。
namespace TwentyFour.Scripts.Features.RemoteConfig
{
// 定義一個(gè)靜態(tài)類,用于存放遠(yuǎn)程配置的鍵名常量
public static class RemoteConfigKeys
{
// 定義一個(gè)常量字符串,表示所有關(guān)卡的配置鍵名
public const string AllStageLevels = "AllStageLevels";
}
}它的值要和剛才 RemoteConfig 網(wǎng)頁(yè)段配置的 key 名稱保持一致。
2.2 創(chuàng)建獲取遠(yuǎn)程配置的輔助類
在 RemoteConfig 文件夾下,再創(chuàng)建一個(gè)新的腳本 RemoteConfigHelper.cs。
該腳本中的代碼實(shí)現(xiàn)了一個(gè)遠(yuǎn)程配置輔助工具類,用于從遠(yuǎn)程服務(wù)器獲取和管理游戲的配置數(shù)據(jù),方便游戲根據(jù)遠(yuǎn)程參數(shù)靈活調(diào)整功能和表現(xiàn)。
2.2.1 初始化 Remote Config SDK
創(chuàng)建 Init 方法,作為整個(gè)遠(yuǎn)程配置功能的初始化入口。在方法內(nèi)先調(diào)用 Initialize 來(lái)初始化 RemoteConfig SDK。在初始化過(guò)程中如果發(fā)生了錯(cuò)誤,我們會(huì)通過(guò) try...catch... 捕獲異常,并打印記錄的錯(cuò)誤信息,便于代碼調(diào)試。
using System;
using System.Threading.Tasks;
using Unity.UOS.Config;
using Logger = TwentyFour.Scripts.Utilities.Logger;
namespace TwentyFour.Scripts.Features.RemoteConfig
{
// 定義遠(yuǎn)程配置輔助類
public class RemoteConfigHelper
{
// 初始化遠(yuǎn)程配置的異步方法
public static async Task Init()
{
try
{
// 初始化遠(yuǎn)程配置SDK
RemoteConfigSDK.Initialize();
}
catch (Exception e)
{
// 捕獲異常并記錄錯(cuò)誤日志
Logger.LogError($"RemoteConfigHelper Init Error:{e}");
}
}
}
}2.2.2 獲取 Remote Config 配置過(guò)的數(shù)據(jù)
在初始化方法Init中,調(diào)用封裝的GetDefaultRemoteConfig方法,來(lái)異步獲取玩家的默認(rèn)遠(yuǎn)程配置,以便游戲運(yùn)行時(shí)動(dòng)態(tài)調(diào)整行為。
首先定義字典 _remoteConfigData ,用于本地存儲(chǔ)遠(yuǎn)程配置數(shù)據(jù),方便后續(xù)快速讀取,無(wú)需每次都訪問(wèn)服務(wù)器;
然后調(diào)用 SDK 提供的 GetPlayerSettingsAsync 函數(shù),向服務(wù)器發(fā)起一個(gè)異步網(wǎng)絡(luò)請(qǐng)求,以獲取為當(dāng)前玩家準(zhǔn)備的配置。
網(wǎng)絡(luò)請(qǐng)求完成后,如果成功了:
通過(guò) settings.data 從返回結(jié)果中獲取響應(yīng)數(shù)據(jù),存在變量 response 中;
然后將從服務(wù)器收到的配置列表(response.Settings)轉(zhuǎn)換成一個(gè)字典,并將其賦值給剛才定義過(guò)的字典 _remoteConfigData;
接下來(lái)為了調(diào)試和監(jiān)控,遍歷所有獲取到的配置項(xiàng)(response.Settings),用 StringBuilder 高效地構(gòu)建了一個(gè)完整的字符串,然后通過(guò) Logger.Log(sb) 將所有配置項(xiàng)打印到日志中,方便開發(fā)者查看拉取到的數(shù)據(jù)是否正確。
網(wǎng)絡(luò)請(qǐng)求完成后,如果失敗了:
如果網(wǎng)絡(luò)請(qǐng)求或服務(wù)器處理失敗,就在日志中記錄一條錯(cuò)誤信息,方便排查問(wèn)題。
無(wú)論成功與否,最后都將獲取到的玩家默認(rèn)的配置結(jié)果(settings)返回。
using System;
using System.Threading.Tasks;
using Unity.UOS.Config;
using Logger = TwentyFour.Scripts.Utilities.Logger;
using Cloud;
using Unity.UOS.Config.Model;
using System.Linq;
using System.Text;
namespace TwentyFour.Scripts.Features.RemoteConfig
{
// 定義遠(yuǎn)程配置輔助類
public class RemoteConfigHelper
{
// 用于存儲(chǔ)遠(yuǎn)程配置數(shù)據(jù)的靜態(tài)字典,鍵為配置名,值為Setting對(duì)象
private static Dictionary
_remoteConfigData = new Dictionary
(); // 初始化遠(yuǎn)程配置的異步方法 public static async Task Init() { try { // 初始化遠(yuǎn)程配置SDK RemoteConfigSDK.Initialize(); await GetDefaultRemoteConfig(); // 獲取默認(rèn)遠(yuǎn)程配置 } catch (Exception e) { // 捕獲異常并記錄錯(cuò)誤日志 Logger.LogError($"RemoteConfigHelper Init Error:{e}"); } } // 異步獲取默認(rèn)遠(yuǎn)程配置的方法 public static async Task > GetDefaultRemoteConfig() { // 發(fā)送請(qǐng)求獲取玩家默認(rèn)設(shè)置 var settings = await RemoteConfigSDK.GetPlayerSettingsAsync(new GetPlayerSettingsRequest()); if (settings.IsSuccess()) { // 獲取響應(yīng)數(shù)據(jù) GetPlayerSettingsResponse response = settings.data; // 將設(shè)置轉(zhuǎn)換為字典并保存 _remoteConfigData = response.Settings.ToDictionary(x => x.Key, x => x.Value); var sb = new StringBuilder(); // 遍歷所有設(shè)置,拼接日志信息 foreach (var entry in response.Settings) { sb.AppendLine($"Remote Config: {entry.Key} - {entry.Value} ,type: {entry.Value.Type}"); } // 記錄日志 Logger.Log(sb); } else { // 獲取失敗時(shí)記錄錯(cuò)誤日志 Logger.LogError($"RemoteConfigHelper GetDefaultRemoteConfig Error"); } // 返回結(jié)果 return settings; } } }
2.2.3 封裝讀取配置數(shù)據(jù)的接口
封裝方法GetString(key),用于從遠(yuǎn)程配置緩存中獲取字符串類型的配置值。后續(xù)如果配置了其它數(shù)據(jù)類型,用到的時(shí)候,會(huì)再詳細(xì)講解其它類型的數(shù)據(jù)如何讀取。
方法中傳入你想要獲取的配置項(xiàng)的名稱(key);
然后嘗試從 _remoteConfigData 字典中查找是否存在指定 key 的配置項(xiàng),如果找到了并且該配置項(xiàng)的類型是 string 類型,則返回配置項(xiàng)的值。如果找不到,則返回一個(gè)空字符串。
namespace TwentyFour.Scripts.Features.RemoteConfig
{
// 定義遠(yuǎn)程配置輔助類
public class RemoteConfigHelper
{
//......
// 獲取遠(yuǎn)程配置的字符串類型的數(shù)據(jù)的方法
public static string GetString(string key)
{
// 嘗試從字典中獲取指定key的值,并判斷類型是否為String
if (_remoteConfigData.TryGetValue(key, out var value) && value.Type == ConfigType.String)
{
// 返回字符串值
return value.Value;
}
// 未獲取到則返回空字符串
return string.Empty;
}
}
}3. 使用 Remote Config 的數(shù)據(jù)來(lái)動(dòng)態(tài)調(diào)控游戲關(guān)卡難度
3.1 調(diào)用初始化遠(yuǎn)程配置的方法
在游戲啟動(dòng)時(shí),我們將按順序完成遠(yuǎn)程配置、關(guān)卡、存檔的初始化,最后再進(jìn)入主場(chǎng)景。
因此,先找到腳本 LoadGameData.cs,在協(xié)程方法 Init() 中,實(shí)現(xiàn)在成功創(chuàng)建角色之后,先調(diào)用初始化遠(yuǎn)程配置數(shù)據(jù)的方法 InitRemoteConfig。
在 InitRemoteConfig 方法中,調(diào)用之前封裝好的 RemoteConfigHelper.cs 腳本中的 Init 方法,實(shí)現(xiàn)遠(yuǎn)程配置的初始化;
然后使用 WaitUntil 等待這個(gè)異步任務(wù)完成。
using TwentyFour.Scripts.Features.RemoteConfig;
namespace TwentyFour.Scripts.Gameplay.HomePage
{
public class LoadGameData : MonoBehaviour
{
//......
// Start is called before the first frame update
IEnumerator Init()
{
yield return StartCoroutine(InitRemoteConfig());
yield return StartCoroutine(InitStage());
yield return StartCoroutine(InitSave());
GameRouter.LoadHomeSceneFirst();
}
//......
IEnumerator InitRemoteConfig()
{
var t = RemoteConfigHelper.Init();
yield return new WaitUntil(()=>t.IsCompleted);
yield return null;
}
}
}接著運(yùn)行游戲進(jìn)行測(cè)試,可以看到 Console 控制臺(tái)打印的日志信息。說(shuō)明已經(jīng)加載到了遠(yuǎn)程配置的關(guān)卡數(shù)據(jù)了,但是發(fā)現(xiàn) Game 窗口中的關(guān)卡數(shù)據(jù)還沒(méi)有發(fā)生變化。
原因是:在 StageManager.cs 腳本的 LoadAllStagesFromRemoteConfig 方法中,看到目前使用的還是本地的 Resources 文件夾下的 levels.txt 文件中的關(guān)卡數(shù)據(jù)。
public static class StageManager
{
//......
public static void LoadAllStagesFromRemoteConfig()
{
var file = Resources.Load ( "levels").text;
HandleFileContent(file);
}
}Resources 文件夾下的 levels.txt 文件中的關(guān)卡數(shù)據(jù)如下:
3.2 使用從 RemoteConfig 加載到的關(guān)卡數(shù)據(jù)
現(xiàn)在我們修改下代碼,實(shí)現(xiàn)從遠(yuǎn)程配置(RemoteConfig )來(lái)加載所有關(guān)卡的數(shù)據(jù)。
打開StageManager.cs 腳本:
在原來(lái)的方法 HandleFileContent 中,處理的是從 levels.txt 獲取到的字符串,每一行的分隔符號(hào)是「\n」,現(xiàn)在的每一行題目的分隔符是「空格」,修改代碼 fileString.Split('\n',' ') 中的分隔符號(hào),這里可以使用可變參數(shù)類型,所以一次性可以傳入多個(gè)字符分隔符;
該方法的作用主要是:將一段特定格式的字符串,也就是剛才遠(yuǎn)程配置過(guò)的數(shù)據(jù)「5,6,7,8 2,3,4,5」,解析成關(guān)卡對(duì)象(Stage),并存儲(chǔ)到 _allStages 列表中。
namespace TwentyFour.Scripts.Gameplay.GameMode.StageMode
{
public static class StageManager
{
//......
private static void HandleFileContent(string text)
{
_allStages = new List ();
string fileString = text.Replace("\r", "");
string[] lines = fileString.Split('\n',' ');
//......
}
}
}然后在 LoadAllStagesFromRemoteConfig 方法中,調(diào)用 RemoteConfigHelper.cs 腳本中封裝的 GetString 方法,實(shí)現(xiàn)從服務(wù)器的遠(yuǎn)程配置中獲取配置好的字符串類型的關(guān)卡數(shù)據(jù),存放到變量 file 中,然后判斷以下兩種情況:
如果遠(yuǎn)程配置有數(shù)據(jù)(即字符串不為空),則使用遠(yuǎn)程配置的關(guān)卡數(shù)據(jù);
如果遠(yuǎn)程沒(méi)有獲取到數(shù)據(jù)(即字符串為空),則使用本地 Resources 文件夾中名為 levels.txt 的文本資源,作為關(guān)卡數(shù)據(jù);
兩種情況下,最終都會(huì)調(diào)用 HandleFileContent 方法來(lái)對(duì)關(guān)卡數(shù)據(jù)的字符串進(jìn)行解析的。
using TwentyFour.Scripts.Features.RemoteConfig;
namespace TwentyFour.Scripts.Gameplay.GameMode.StageMode
{
public static class StageManager
{
//......
public static void LoadAllStagesFromRemoteConfig()
{
//獲取 RemoteConfig 中配置的關(guān)卡數(shù)據(jù)
var file = RemoteConfigHelper.GetString(RemoteConfigKeys.AllStageLevels);
if (string.IsNullOrEmpty(file))
{
//使用本地 Resources 文件夾中名為 levels.txt 的文本資源,作為關(guān)卡數(shù)據(jù)
file = Resources.Load ( "levels").text;
}
//解析存放關(guān)卡數(shù)據(jù)的字符串
HandleFileContent(file);
}
}
}再次運(yùn)行測(cè)試,可以看到 Game 窗口中闖關(guān)界面關(guān)卡數(shù)只有兩關(guān)了,說(shuō)明數(shù)據(jù)已經(jīng)從配置的 RemoteConfig 中獲取到了。
接著,大家可以自行再次修改 UOS 網(wǎng)頁(yè)端 RemoteConfig 中配置的關(guān)卡數(shù)據(jù),然后登出賬號(hào)后重新登錄,就會(huì)自動(dòng)加載新的關(guān)卡數(shù)據(jù)了。
下節(jié)教程預(yù)告
教程主題——《火拼24》系列教程三:游戲闖關(guān)進(jìn)度的云端存檔
《火拼24》下一篇教程,將揭秘基于 CRUD Save 打造的游戲闖關(guān)進(jìn)度云端存檔功能。它能實(shí)現(xiàn)當(dāng)前登錄用戶闖關(guān)進(jìn)度的云端保存,讓用戶退出游戲后再次進(jìn)入仍可無(wú)縫銜接,精彩不容錯(cuò)過(guò)!
小貼士:為方便大家提前學(xué)習(xí),教程三的分支代碼已同步更新,可提前下載查閱或本地調(diào)試。
教程三:初始項(xiàng)目工程(供學(xué)習(xí)參考)
https://cnb.cool/unity/uos/Rush24Tutorial/-/tree/lesson3-start
教程三:完整示例工程參考(可直接運(yùn)行)
https://cnb.cool/unity/uos/Rush24Tutorial/-/tree/lesson3-end
記得鎖定更新,別錯(cuò)過(guò)每一步關(guān)鍵指南哦!
Unity Online Services (UOS) 是一個(gè)專為游戲開發(fā)者設(shè)計(jì)的一站式游戲云服務(wù)平臺(tái),提供覆蓋游戲全生命周期的開發(fā)、運(yùn)營(yíng)和推廣支持。
了解更多 UOS 相關(guān)信息:
官網(wǎng):https://uos.unity.cn
技術(shù)交流 QQ 群:823878269
Unity 官方微信
第一時(shí)間了解Unity引擎動(dòng)向,學(xué)習(xí)進(jìn)階開發(fā)技能
每一個(gè)“點(diǎn)贊”、“在看”,都是我們前進(jìn)的動(dòng)力
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.