新年將至,Unity 中國祝所有小伙伴和家人們新年快樂!2024 年,。新的一年里,愿所有小伙伴在吉運常伴,創意無限,不斷取得豐碩成果!點擊下圖,領取蛇年限定紅包封面~
本文分享 UOS 實用教程,幫助大家在假期輕松提升技能。
之前,我們已經為大家分享了一篇教程《》,詳細介紹了如何使用 Unity Online Services(UOS)提供的 C# 云函數服務,在 Unity 開發環境中快速實現和部署聯網游戲的服務器端邏輯。在那篇教程中,我們展示了如何使用云數據庫服務 CRUD Storage 來存儲諸如玩家信息和抽卡記錄等數據。
今天,我們將再次以“抽卡”功能為例,但深入探討的技術內容有所不同。
在本篇教程中,我們將借助 UOS 提供的Passport-Login服務 來完成用戶的登錄驗證流程。隨后,我們將利用云函數 Func-Stateless來部署抽卡功能的服務器端邏輯代碼。最后,這些抽卡數據將通過CRUD-Save服務 實現云端存儲,確保數據的持久性和安全性。
本教程中涉及 UOS 服務包括:
云函數服務 Func Stateless (C#):用于部署抽卡服務端邏輯代碼
云存檔服務 CRUD Save:用于存儲抽卡記錄等數據
玩家通行證服務 Passport Login:實現用戶登錄及實名認證
云函數服務 Func Stateless
在保障游戲或應用安全性的重要考量下, 確保關鍵邏輯和數據的權威性處理僅在服務器端執行顯得尤為重要 。這是因為 客戶端的代碼包體容易被破解或篡改 ,從而引發不公平競爭、數據泄露等安全問題。UOS Func Stateless (C#) 云函數以其高效、靈活、安全且成本低的特點,為游戲開發者提供了一個理想的服務端邏輯解決方案,助力他們在快速迭代的市場環境中保持競爭優勢。
Func Stateles s 支持在本地開發環境中直接調試云函數 ,無需部署到云端即可驗證邏輯的正確性,降低了調試難度,加快了開發迭代速度。
Func Stateless 能夠 自動將服務端邏輯代碼打包并部署到云端 ,簡化了部 署流程,減少了人為錯誤,讓開發者更專注于業務邏輯的實現。
云存檔服務 CRUD Save
借助 UOS Save 所提供的全面而專業的 玩家數據存儲、檢索及 管理服務 ,開發者能夠極為便捷地為廣大游戲玩家打造出一個跨越不同平臺與設備的、安全且高度可用的游戲存檔系統。
這一服務不僅確保了玩家能夠在任何時間、任何地點無縫地繼續他們的游戲進程,而且通過嚴格的數據安全保障措施,讓玩家的游戲數據始終處于嚴密的保護之下。同時,其高可用性的設計也保證了玩家數據的實時同步與持久存儲,為玩家帶來了更加流暢、穩定的游戲體驗。
玩家通行證服務 Passport
Passport 是 UOS 官方提供的玩家通行證服務,包括玩家登錄系統 (Login),以及與玩家相關的眾多游戲功能 (Feature)。
Passport Login 是一個可以開箱即用的玩家登錄系統,通過非常簡便的集成方式, 便可以獲得如下功能: 靈活可 配的 UI 登錄界面,包含用戶協議、登錄、實名認證; 支持手機號登錄,以及微信,QQ, Apple ID 等 主流第三方 OAuth 登錄;可以配置游戲服務器,以及管理游戲角色。
Passport Feature 是以玩家為中心,包含了在線游戲中各種常見的功能。包括:排行榜、公會、游戲禮包、防沉迷系統、經濟系統、郵件系統、成就系統、戰令系統、公告、任務系統。
教程視頻
教程學習大綱
在 UOS 官網下載示例項目工程
創建 UOS App 并啟用 Func-Stateless / CRUD-Save / Passport-Login 服務
設置云函數的安裝和配置目錄
在本地模式下調試并運行項目,通過 Passport 實現用戶登陸
通過 CRUD Save 實現用戶抽卡數據的云存檔
上傳和部署云函數,切換到遠程調用模式下測試云函數的調用
教程案例工程源文件
教程內學習用到的項目工程文件可以通過以下方式進行下載:
UOS 官網示例教程中在線下載:
https://uos.unity.cn/doc/func/stateless/csharp-tutorial#prepare
教程操作步驟
接下來讓我們來看看 Func Stateless 云函數結合 CRUD Save 云存檔在抽卡項目中的具體用法吧!
1. 在 UOS 官網下載示例項目工程
1.1 下載項目工程文件
首先前往上方提供的UOS 官網的教程鏈接地址,在「Func」的文檔左側頁面選擇「Stateless」下方的「示例教程(C#)」,在「準備工作」模塊點擊按鈕「DEMO(CLOUDSAVE PASSPORT)工程」,就可以下載抽卡 Demo 的項目工程了。
1.2 解壓縮項目工程文件
在下載完項目工程壓縮包以后,請解壓縮下圖中的項目工程文件。
1.3 加載項目工程
然后打開 Unity Hub,選擇電腦上已經安裝過的 Unity 編輯器版本,來打開剛剛下載好的項目工程,教程這里使用的是Unity2022.3.48f1c1 版本。
打開項目工程以后,在項目的 Project 窗口中,找到「Assets/Scenes/Demo.unity」場景并打開。
點擊「Import TMP Essentials」導入 TextMeshPro 的字體資源。
2.綁定 UOS App 并開啟服務
溫馨提示:當前項目中已安裝好 UOS Launcher,不需要再次安裝了。大家可以參考之前的的公眾號文章教程,來為你當前的項目綁定你創建好的 UOS App 。
2.1 綁定 UOS App
點擊 Launcher 面板的「LinkApp」按鈕,在彈出窗口中選擇「By Unity project」,在「Select organization」這里選擇一個自己的項目組織,然后在「Select project 」選項這里,我們選擇「Create a new project 」,自行設置修改項目名字「Project name」。
教程中,我們就先選擇綁定創建好的 FuncStatelessAndSaveDemo 應用了。
2.2 開啟 Func-Stateless 服務
在編輯器內 Unity Online Services 窗口的下拉服務列表中,找到「Func - Stateless」,點擊「Enable」按鈕來一鍵開啟服務, 進入 UOS 網頁端可以看到,此時默認云函數的腳本語言是基于 Dotnet 的運行環境的。
如果你是在 UOS 網頁端為當前 App 開啟 Func-Stateless 服務的話,需要自行手動在腳本語言的下拉選項框中選擇「Dotnet」環境 。
當前項目中已經安裝過 Func - Stateless 服務 SDK,無需再次安裝了。如果你自己的項目中沒安裝過的話,可以點擊「Install SDK」的按鈕進行安裝。
2.3 開啟 CRUD-Save 服務
接著繼續在UOS Launcher 的下拉服務窗口列表中,找到「CRUD-Save」,點擊「Enable」開啟服務,當然你也可以在 UOS 網站上直接開啟服務。
當前項目中也是已經安裝過 CRUD-Save 服務的 SDK了,無需再次安裝了。
2.4 開啟 Passport-Login 服務
接著繼續在 UOS Launcher 的下拉服務窗口列表中,找到「Passport Login」,點擊「Enable」開啟服務。
當前項目中也是已經安裝過 Passport Login 服務的 SDK了,無需再次安裝了。
3.設置云函數的安裝和配置目錄
3.1 導入 NuGetForUnity 工具
在 Unity Editor 菜單欄中先點擊「UOS -> Func Stateless -> Open Panel」按鈕,打開 Func Stateless Tool 面板。
然后再點擊「UOS -> Func Stateless -> Import NuGetForUnity」,來導入 UOS 版本的 NuGetForUnity 工具。
3.2 修改云函數的安裝和配置目錄
導入完成后,點擊菜單欄中「NuGet -> Preferencs」打開配置界面。修改Packages Install Path 為云函數所在目錄下的 Packages 目錄;Packages Config Path 為云函數所在目錄。
在這里我們就先使用給定的默認設置目錄了,大家有需要自行修改
登錄功能模塊的 UI 界面和腳本,我們使用的是 UOS 的 Passport 服務為用戶提供的示例模板,在之前點擊導入「PassportUI」資源的時候已經導入過了,可以直接在項目中使用。
在這里我們使用的是手機號短信驗證碼的方式登錄的, 由于 Passport 的「登錄配置」默認是開啟了真實短信驗證和實名認證的。所以,這里請 輸入真實的手機號和短信驗證碼 來完成登錄。
當輸入完短信驗證碼后,可以在 Console 控制臺看到輸出的日志信息:「完成登錄」。
日志信息是在 UIController.cs 腳本中調用輸出的,找到場景中的 UIController 對象,可看到掛載的 UIController.cs 腳本:
進入 UIController.cs 腳本查看下代碼,在 Start 方法中會調用 Passport SDK 進行 UI 初始化的方法 Init ,方法中需要傳入配置 _config 和回調函數 _callback。
_config 是 Passport SDK 進行初始化時的相關配置:來設置是否自動旋轉屏幕方向、是否通過自行調用 Login 函數啟動登錄面板,以及設置 UI 風格主題是深色還是淺色等等。
_callback 是 Passport SDK 的回調函數:Passport 中已經封裝注冊好了,在用戶拒絕協議、完成登錄、完成所有流程、用戶登出這幾個狀態下的回調事件。
所以當用戶短信驗證通過后,會自動進入LoggedIn狀態下的回調事件,輸出了日志「完成登錄」。
public class UIController : MonoBehaviour
{
// sdk 配置(Config 是 SDK 初始化時的配置)
private readonly PassportUIConfig _config = new()
{
AutoRotation = true, // 是否開啟自動旋轉,默認值為 false。
InvokeLoginManually = false, // 是否通過自行調用 Login 函數啟動登錄面板,默認值為 false。
Theme = PassportUITheme.Dark, // 風格主題配置。
UnityContainerId = "unity-container" // WebGL 場景下 Unity 實例容器 Id。
};
// sdk 回調
private async void _callback(PassportEvent e)
{
// event: 不同情況下的回調事件,詳情可以參考下面的回調類型。
switch (e)
{
case PassportEvent.RejectedTos:
Debug.Log("用戶拒絕了協議");
break;
case PassportEvent.LoggedIn:
Debug.Log("完成登錄");
break;
case PassportEvent.Completed:
Debug.Log("完成所有流程");
await SelectPersona();
break;
case PassportEvent.LoggedOut:
Debug.Log("用戶登出");
break;
}
}
private void Start()
{
// 調用 SDK
PassportUI.Init(_config, _callback);
}
}
然后會需要進行實名身份認證:
當實名驗證成功后,會看到控制臺日志輸出:「完成所有流程」。
此時會自動回調PassportEvent.Completed 狀態下的事件。
4.2 修改登錄配置
接著可以點擊 Passport Login 旁邊的「Developer portal」按鈕,進入 UOS 網頁端的 Passport 模塊來自行修改「登錄配置」中的設置。
點擊選中「模擬短信驗證」,這樣驗證碼直接輸入「 111111」即可;取消選中「實名認證」,則暫時不再驗證用戶的身份信息。
修改完配置后,可以再次回到編輯器測試下效果。點擊運行游戲,選擇「退出登錄」,然后「使用新的賬號登錄」,再重新輸入一個手機號,然后輸入短信驗證碼 111111 ,就可以成功登陸了,同時也跳過了身份驗證,直接可以進入游戲。Console 控制臺也能看到對應的日志輸出。
4.3 選擇角色
登陸完成后,會選擇一個角色進入游戲。 在腳本的 _callback 方法的Completed 狀態下,看到會調用選擇角色的方法SelectPersona。
private async void _callback(PassportEvent e)
{
// event: 不同情況下的回調事件,詳情可以參考下面的回調類型。
switch (e)
{
case PassportEvent.RejectedTos:
Debug.Log("用戶拒絕了協議");
break;
case PassportEvent.LoggedIn:
Debug.Log("完成登錄");
break;
case PassportEvent.Completed:
Debug.Log("完成所有流程");
await SelectPersona();
break;
case PassportEvent.LoggedOut:
Debug.Log("用戶登出");
break;
}
}
在方法 SelectPersona 中,會先調用PassportSDK.Identity.GetRealms()方法獲取域列表,然后從域列表中根據需要選擇一個域,在這里我們暫時先使用域列表的第一個域。
大家也可以手動填寫你想要使用的某一個域。我們進入 UOS 網頁端 Passport 的「服務器」模塊,可以看到默認已經幫我們創建了一個域了。點擊復制域的 UUID,然后賦值給腳本中的域變量 realmID 即可。
// 選擇域
//var realms = await PassportSDK.Identity.GetRealms(); // 獲取域列表
//var realmID = realms[0].RealmID; // 根據需要自行選擇域
var realmID = "543b59d1-3008-4028-8591-83167e82feb0"; // 也可以填寫固定的 RealmID 而不是動態獲取
修改完代碼后,可以再次運行,是可以正常進入游戲的。然后我們需要選擇一個角色,先通過調用GetPersonas() 方法來獲取到角色列表。
由于 Demo 示例中我們默認啟用的配置是:一個用戶單服務器下只能創建一個角色,并沒有開啟單服務器下多角色的功能。所以目前是一個手機號對應一個用戶 ID,一個用戶 ID 對應一個角色 ID。
如果角色列表中沒有任何角色,則會調用 CreatePersona 方法,在指定的域 realmID 中來創建一個角色賦值給 persona 變量。
如果角色列表中已經有角色的話,會直接選擇已創建過的角色 personas[0] 賦值給 persona 變量。最后,再通過調用 SelectPersona 方法來指定選擇的角色。
// 選擇角色
private async Task SelectPersona()
{
// 選擇域
//var realms = await PassportSDK.Identity.GetRealms(); // 獲取域列表
//var realmID = realms[0].RealmID; // 根據需要自行選擇域
var realmID = "543b59d1-3008-4028-8591-83167e82feb0"; // 也可以填寫固定的 RealmID 而不是動態獲取
// 獲取(或創建)與選擇角色
Persona persona = null;
var personas = await PassportSDK.Identity.GetPersonas(); // 獲取角色列表
if (!personas.Any())
{
// 若沒有角色,則新建角色
persona = await PassportSDK.Identity.CreatePersona("YourDisplayName", realmID);
}
else
{
// 若有角色,則選擇第一個角色
persona = personas[0];
}
// 選擇角色
await PassportSDK.Identity.SelectPersona(persona.PersonaID);
}
4.4 創建一個新的域
用戶也可自行創建一個新的域來使用。在 Passport 的【服務器】管理頁面,點擊【創建新的域】,自己設置下名稱,點擊【創建】即可。
然后可以將新創建的域的 UUID 賦值給腳本中的變量 realmID ,再次進行測試后,回到 UOS 網頁端,點擊查看域的詳情,可以看到域中已經有創建的角色信息了。
4.5 進入游戲場景,獲取用戶登錄信息
登陸成功后,會看到下面的游戲畫面,我們先來看看【進入游戲】的 UI 對象上綁定的事件。
場景中的 MainUI 對象上掛載了 MainUIController.cs 腳本,當我們點擊【進入游戲】的按鈕時,會執行 MainUIController.cs 腳本中的 StartGame 的方法。
點擊【進入游戲】按鈕,可以進入游戲場景:
同時,Console 控制臺會看到有下面的日志輸出信息。
首先來看看輸出的日志:“currentUserId, use as save name”,說明執行了 StartGame 方法 。會首先通過 PassportSDK 來獲取到當前角色所屬的用戶ID。
public async void StartGame()
{
try
{
ShowLoading(true);
// await NetworkManager.Login(username, password);
var userId = PassportSDK.CurrentPersona.UserID;
Debug.Log($"currentUserId, use as save name: {userId}");
await NetworkManager.Login(userId);
}
catch (Exception e)
{
Debug.LogException(e);
Debug.Log(e.Message);
ShowLoading(false);
MessageUI.Show(e.Message);
}
}
剛才控制臺對應輸出的日志信息還有:“loginResult: 1000000001, 106104, 467”。因為代碼中拿到 UserID 后,異步調用了NetworkManager.Login方法。
在 Login 方法中,會先通過調用云函數腳本 LoginService 中的云函數 Login 來獲取到登陸結果 loginResult。然后再通過事件調用,將用戶的昵稱(Nickname)、金幣數(Coins)、鉆石數(Diamonds)的信息,同步更新到 UI 界面上。
稍后我們會展開來詳細講解:云函數腳本 LoginService 和 ActionService 的。
public async Task Login(string username)
{
try
{
var loginService = new LoginService();
var loginResult = await loginService.Login(username);
if (!loginResult.Ok)
{
throw new Exception(loginResult.Message);
}
_saveId = loginResult.SaveId;
_user = loginResult.User;
_as = new ActionService();
Debug.Log($"loginResult: {loginResult.User.Nickname}, {loginResult.User.Coins}, {loginResult.User.Diamonds}");
// invoke ui update
onLogin.Invoke(new Account
{
Nickname = loginResult.User.Nickname,
Coins = loginResult.User.Coins,
Diamonds = loginResult.User.Diamonds
});
}
catch (Exception e)
{
onError.Invoke(e.Message, 3);
}
}
5.云函數結合 CRUD Save 實現用戶抽卡數據的云存檔
接下來看看項目中如何使用 CloudSave 實現讀檔和存檔的!
5.1查看網頁端的云存檔文件
運行游戲點擊抽卡后,用戶抽卡的相關結果數據信息,其實已經通過 UOS 的 CRUD-Save 服務實現云存檔了。
我們進入 UOS 網頁端CRUD Save的【存檔管理】頁面,可以看到當前登錄的用戶,然后點擊最右邊的【詳情】按鈕。
可以在彈出的界面中查看【存檔詳情】,點擊【下載存檔】。
打開存檔文件后,可以查看文件中存儲的抽卡的信息。
用戶也可以在網頁上點擊【對玩家隱藏存檔】或者【刪除存檔】,現在我們演示下點擊頁面上的【刪除存檔】按鈕,則會清空該存檔 ID 對應的一整條信息的。
此時如果再次運行游戲,可以看到金幣數和鉆石數將顯示為 0,英雄列表和背包列表也都為空了。
接下來我們看看云存檔的功能具體是如何實現的!
5.2初始化 CloudSaveSDK
在 MainUIController.cs 腳本的 Start 方法中,會調用InitializeAsync方法,該方法會使用 UOS Launcher 中填寫的 UOS APP 信息來實現對 CloudSaveSDK 的初始化。
private async void Start()
{
//此處省略其它代碼行......
try
{
ShowLoading(true);
await CloudSaveSDK.InitializeAsync();
ShowLoading(false);
}
catch (Exception e)
{
Debug.Log(e.Message);
ShowLoading(false);
MessageUI.Show(e.Message);
}
}
5.3登錄云函數結合 CloudSaveSDK 實現讀取已存檔的用戶信息
然后點擊菜單欄「UOS -> Func Stateless -> Open Panel」按鈕,打開「Func Stateless Tool」窗口。
在打開的工具的窗口中,可以看到當前項目中的 2 個云函數類LoginService 和 ActionService。我們先不上傳云函數,先在本地進行調試運行項目。
雙擊云函數「Login」,就可以打開云函數所在腳本了。 我們來分析下云函數 Login 的代碼,會先通過調用 CloudSaveSDK 封裝的方法ListAllAsync 來列出當前用戶的所有存檔元數據信息,存儲在變量 saveItems 中。
[CloudService]
public class LoginService
{
[CloudFunc]
public async Task Login( string username)
{
// 此處省略其它代碼行......
var saveItems = await CloudSaveSDK.Instance.Files.ListAllAsync();
}
}
然后判斷下如果 saveItems 集合長度為 0,說明當前用戶沒有存檔文件,是新用戶第一次登錄,那么我們就會調用CreateAsync 方法來創建一個存檔文件。
創建存檔文件的時候,需要傳入三個參數,存檔名稱 username、字節數組類型的存檔文件 data、存檔選項 options。
我們創建了一個新用戶對象 newUser,然后通過封裝的SerializeToByteArray方法,將 newUser 的屬性信息轉換成字節數組變量 data,作為第二個參數傳遞到 CreateAsync 方法中。
在存檔選項 options 中,設置了當前云存檔的模式為單存檔 ProgressType.Linear,如果你要在你的項目中使用多存檔模式的話,可以使用 ProgressType.Multi。
大家可以自行查看封裝的存檔選項類 CreateOptions,根據需要自行傳入更多的參數。
最后會將用戶登錄的結果返回。
if (saveItems.Count == 0)
{
var newUser = new User
{
Username = username,
Nickname = username + Helper.GetRandomEmoji(),
Diamonds = 500,
Coins = 0,
LuckPoints = 0,
DrawCounts = 0,
DrawPool = Helper.NewDrawPool(),
Heroes = new Dictionary (),
Props = new Dictionary ()
};
var data = SerializeHelper.SerializeToByteArray(newUser);
var options = new CreateOptions
{
ProgressType = ProgressType.Linear
};
var saveId = await CloudSaveSDK.Instance.Files.CreateAsync(username, data, options);
return new LoginResult
{
Ok = true,
SaveId = saveId,
User = newUser
};
}
如果當前用戶已存在存檔文件,則驗證下登錄用戶的密碼,然后調用LoadBytesAsync方法,根據用戶的存檔 ID 來獲取存檔文件內容,賦值給變量 saveData。
然后通過封裝的反序列化DeserializeFromByteArray方法將字節數組變量 saveData 轉換成 User 類型,賦值給 user 變量,最后再將用戶登錄的結果返回。
// 驗證密碼
var saveItem = saveItems[0];
// 讀取存檔
var saveData = await CloudSaveSDK.Instance.Files.LoadBytesAsync(saveItem.SaveId);
var user = SerializeHelper.DeserializeFromByteArray (saveData);
return new LoginResult
{
Ok = true,
SaveId = saveItem.SaveId,
User = user
};
5.4抽卡云函數結合 CloudSaveSDK 實現讀檔和存檔
當我們點擊抽卡按鈕時,抽到的金幣、鉆石、角色、物品等都會更新到 UI 面板上,也會同步更新到云存檔文件中的。
現在我們再次運行游戲,然后找到場景中的抽卡的 Button 按鈕,可以看到按鈕響應的是 MainUIController 腳本中的DrawCard方法。
下面的示例代碼給我們展示的是抽卡的方法(DrawCard)的客戶端的邏輯代碼。
public async void DrawCard(int count = 1)
{
ShowLoading(true);
try
{
await NetworkManager.DrawCard(count);
}
catch (Exception e)
{
Debug.Log(e.Message);
ShowLoading(false);
MessageUI.Show(e.Message);
}
}
在 NetworkManager.DrawCard 代碼中,會調用ActionService.cs 腳本中的抽卡云函數(Draw),來獲取抽卡的結果。
private ActionService _as;
public async Task DrawCard(int count)
{
try
{
var drawResult = await _as.Draw(_saveId, count);
//此處省略其它代碼行......
}
catch (Exception e)
{
onError.Invoke(e.Message, 3);
}
}
下面我們來看看抽卡云函數(Draw)的代碼吧,代碼中實現了從卡池中抽取卡,并返回抽取的結果。
大家在這里先思考一個問題:為什么需要把邏輯放在云函數里面呢?
原因是客戶端包體可能會被破解,需要把扣除資源(扣鉆石)這種邏輯放在云端,這樣才安全,否則客戶端被破解后,可以在客戶端無限抽卡了。
我們接著來看下代碼邏輯:
在方法中先通過調用 CloudSaveSDK 封裝的方法 LoadBytesAsync ,根據用戶的存檔 ID 來獲取到存檔文件內容 saveData。
然后再通過封裝的反序列化 DeserializeFromByteArray 方法,將字節數組類型的結果轉換成 User 類型賦值給了變量 user,獲取到了當前 user 的信息。
接著開始進行抽卡,中間這段代碼是抽卡的邏輯代碼,大家有需要可以自行查看代碼。
抽卡結束后會調用UpdateAsync 方法來更新存檔數據。更新存檔時需要傳入 2 個參數,分別是存檔 ID 和更新存檔選項 options。 在更新存檔文件時 UpdateOptions 有不同的方式,可以是 ByFilePath、ByFileStream 或者 ByFileBytes。 在這里,我們通過UpdateFileWay.ByFileBytes 字節文件的方式來更新,而當前用戶的信息會以 byte[] 數組的形式存在變量 data 中。
最后該方法再將用戶抽卡的結果信息返回。
[CloudFunc]
public async Task Draw( string saveId, int count)
{
Debug.Log("call to draw");
// read
var saveData = await CloudSaveSDK.Instance.Files.LoadBytesAsync(saveId);
var user = SerializeHelper.DeserializeFromByteArray (saveData);
if (count > user.Diamonds)
{
return new DrawResult
{
Ok = false,
Message = "鉆石不夠"
};
}
// start to draw
// 此處省略其它代碼行......
// write
user.Diamonds += diamonds - count;
user.Coins += coins;
user.LuckPoints += luckPoints;
user.DrawCounts += count;
user.DrawPool = pool;
user.Heroes = heroes;
user.Props = props;
var data = SerializeHelper.SerializeToByteArray(user);
var options = new UpdateOptions
{
File = new FileOptions
{
UpdateFileWay = UpdateFileWay.ByFileBytes,
FileBytes = data,
}
};
Debug.Log(saveId);
await CloudSaveSDK.Instance.Files.UpdateAsync(saveId, options);
return new DrawResult
{
Ok = true,
Items = res,
User = user
};
}
}
通過云函數 Draw 抽取卡片后,在 NetworkManager.cs 腳本的 DrawCard 方法中會更新本地緩存,同時處理抽卡結果。
抽卡成功后,會將 _user 變量賦值為更新后的用戶信息 drawResult.User。
同時將抽取到的卡片列表 drawResult.Items 轉換為一個新的集合對象 listResult,來存儲抽卡結果。
通過 onDrawCard.Invoke 方法的調用,通知注冊到 onDrawCard 事件的監聽器,在用戶界面上顯示新抽取的卡片的信息。
public async Task DrawCard(int count)
{
try
{
var drawResult = await _as.Draw(_saveId, count);
// update cache
if (!drawResult.Ok)
{
throw new Exception(drawResult.Message);
}
_user = drawResult.User;
var listResult = new List (drawResult.Items.Count);
listResult.AddRange(drawResult.Items.Select(it => new Item
{
Type = it.Type switch
{
0 => ItemType.Hero,
1 => ItemType.Prop,
_ => ItemType.Other
},
Name = it.Name,
Count = it.Count,
Level = it.Level
}));
onDrawCard.Invoke(listResult);
}
catch (Exception e)
{
onError.Invoke(e.Message, 3);
}
}
6.上傳和部署云函數,切換到遠程調用模式下測試云函數的調用
當全部驗證完代碼邏輯后,請先切換成遠程模式,上傳云函數后再進行驗證遠程調用。
6.1上傳云函數
所以,我們現在就可以點擊 Func Stateless Tool 面板的「上傳云函數」的按鈕,來等待云函數的構建和部署。
當云函數構建完成后,工具會自動將云函數部署到 Func Stateless。 可以通過點擊工具中的時間戳,快速跳轉到網頁控制臺查看云函數部署情況。
可以看到已經自動將云函數 loginservice 和 actionservice 部署到 Func Stateless 了。
點擊云函數代碼緩存路徑下的文件,會自動為你下載 zip 文件的。
可以點擊查看下,會自動將我們配置的云函數路徑(Scripts/CloudService)下的腳本都緩存到 zip 壓縮包中的。
6.2切換成遠程調用模式
云函數的調用模式分為本地調用和遠程調用,之前我們已經運行項目測試過,都屬于在本地調用云函數。
現在,讓我們在 Func Stateless Tool 工具中,將云函數的調用模式由「本地調用」切換到「遠程調用」。主要的目的還是防止客戶端包體可能會被破解,所以權威性邏輯不能在客戶端執行,而必須在服務端上執行。
遠程調用:它指的是自動將本地函數調用請求改寫成 HTTP 等網絡服務請求。
切換到遠程調用模式后,云函數代碼發生了變化。打開腳本可以看到,云函數中的代碼由原來的業務邏輯變成了遠程調用的代碼。如下圖所示:
LoginService.cs腳本中的登錄云函數 Login:
[CloudService]
public class LoginService
{
[CloudFunc]
public async Task Login( string username)
{
var jwtToken = await AuthTokenManager.GetAccessToken(Settings.AppID);
FuncContext.SetAppIdSecret(Settings.AppID, Settings.AppSecret);
FuncContext.SetToken(jwtToken);
var json = "{" + $"\"username\":{JsonConvert.SerializeObject(username)}" + "}";
var jsonResult = await HttpClient.Call($"https://{LoginServiceq3k9FwJi5A.Domain}/release/c1ac2450-730c-4f5a-a15a-7a314c6b9a9e/loginservice", "login", json);
try
{
return JsonConvert.DeserializeObject (jsonResult);
}
catch (Exception ex)
{
Debug.LogException(ex);
throw new ExecuteException(jsonResult);
}
}
}
ActionService.cs腳本中的抽卡云函數 Draw:
[CloudFunc]
public async Task Draw( string saveId, int count)
{
var jwtToken = await AuthTokenManager.GetAccessToken(Settings.AppID);
FuncContext.SetAppIdSecret(Settings.AppID, Settings.AppSecret);
FuncContext.SetToken(jwtToken);
var json = "{" + $"\"saveId\":{JsonConvert.SerializeObject(saveId)},\"count\":{JsonConvert.SerializeObject(count)}" + "}";
var jsonResult = await HttpClient.Call($"https://{ActionServiceq3k9FwJi5A.Domain}/release/c1ac2450-730c-4f5a-a15a-7a314c6b9a9e/actionservice", "draw", json);
try
{
return JsonConvert.DeserializeObject (jsonResult);
}
catch (Exception ex)
{
Debug.LogException(ex);
throw new ExecuteException(jsonResult);
}
}
6.3 查看調用的日志信息
切換成【遠程調用】模式后,再次運行游戲項目。
然后回到 UOS 網頁端的云函數頁面,可以查看云函數的調用日志,可以看到項目已經成功調用了上傳的云函數。
6.4重置云函數
Func Stateless Tool 工具還給我們提供了重置云函數功能。如果點擊【重置云函數】按鈕后,會將云函數回退到上一個版本的云函數。然后當我們修改云函數的代碼后,可以再次上傳新的云函數。
本期教程我們先講解到這里,大家趕快下載 Demo 項目試試吧!我們下一期再見哦!
Unity Online Services (UOS) 是一個專為游戲開發者設計的一站式游戲云服務平臺,提供覆蓋游戲全生命周期的開發、運營和推廣支持。
了解更多 UOS 相關信息:
官網:https://uos.unity.cn
技術交流 QQ 群:823878269
公眾號:UOS 游戲云服務
Unity 官方微信
第一時間了解Unity引擎動向,學習進階開發技能
每一個“點贊”、“在看”,都是我們前進的動力
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
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.