使用示例:向加入大厅的所有玩家宣布升级
此使用示例演示了如何使用 Triggers 向玩家所在大厅中的所有其他玩家宣布该玩家升级了。此示例使用了 Cloud Save 服务发出的 key-saved
事件。此示例通过推送消息将玩家升级消息告知与升级玩家位于同一大厅(请参阅 Lobby)的玩家。
触发器使用过滤器来评估事件有效负载,仅当玩家升级且玩家实体发出 Cloud Save 事件时才会触发 Cloud Code 模块。
注意:只能将推送消息与 Cloud Code 模块配合使用。
**注意:**UGS CLI 不支持带有过滤器的触发器。此示例使用 Triggers Admin API 来创建触发器。
先决条件
必须首先创建具有所需访问角色的服务帐户。
使用服务帐户进行身份验证
在调用 Triggers 服务之前,必须使用服务帐户进行身份验证。
- 导航到 Unity Dashboard。
- 选择 Administration(管理)> Service Accounts(服务帐户)。
- 选择 **New(新建)**按钮并输入服务帐户的名称和描述。
- 选择 Create(创建)。
添加产品角色并创建密钥:
- 选择 Manage product roles(管理产品角色)。
- 将以下角色添加到服务帐户:
- 从 LiveOps 下拉选单中,选择 **Triggers Configuration Editor(Triggers 配置编辑者)**和 Triggers Configuration Viewer(Triggers 配置查看者)。
- 从 Admin(管理)下拉选单中,选择 Unity Environments Viewer(Unity 环境查看者)。
- 从 LiveOps 下拉选单中,选择 **Triggers Configuration Editor(Triggers 配置编辑者)**和 Triggers Configuration Viewer(Triggers 配置查看者)。
- 选择 Save(保存)。
- 选择 Add Key(添加密钥)。
- 使用 base64 编码方式对 **Key ID(密钥 ID)**和 **Secret key(密钥)**进行编码。格式为“key_id:secret_key”。请记下此值。
如需了解更多信息,请参阅身份验证。
检查 key-saved
事件
当保存 Cloud Save 键时,Cloud Save 服务会发出 key-saved
事件。事件有效负载如下所示:
{
"id": "7LpyhpsIvEczGkDI1r8J6gHhHezL",
"idType": "player",
"key": "LEVEL",
"value": 1,
"valueIncluded": true,
"writeLock": "7b8920a57912509f6b5cbb183eb7fcb0",
"accessClass": "default",
"modifiedDate": "2021-03-04T09:00:00Z"
}
该事件会将事件有效负载作为参数传递给 Cloud Code。
请参阅 Cloud Save:保存键以了解更多信息。
设置 Cloud Code
定义一个模块终端,当玩家升级时,向大厅中的所有玩家发送推送消息。
创建一个包含如下内容的 AnnounceLevelUp
模块函数:
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudCode.Shared;
using Unity.Services.Lobby.Model;
namespace AnnounceLevelUp;
public class AnnounceLevelUp
{
private const string ServiceId = "cloud-code";
private readonly ILogger<AnnounceLevelUp> _logger;
public AnnounceLevelUp(ILogger<AnnounceLevelUp> logger)
{
_logger = logger;
}
[CloudCodeFunction("AnnounceLevelUp")]
public async Task Announce(IExecutionContext ctx, PushClient pushClient, IGameApiClient gameApiClient,
string id)
{
try
{
var playerList = await GetPlayersFromLobbies(ctx, gameApiClient, id);
if (playerList.Count == 0)
{
_logger.LogInformation("The player {playerId} has not joined any lobbies", id);
return;
}
await SendPushMessage(ctx, pushClient, playerList);
}
catch (ApiException e)
{
_logger.LogError("Failed to announce level up for player {playerId}. Error: {Error}", id,
e.Message);
throw new Exception($"Failed to announce level up for player {id}. Error: {e.Message}");
}
}
public async Task SendPushMessage(IExecutionContext ctx, PushClient pushClient, List<Player> lobbyPlayers)
{
foreach (var lobbyPlayer in lobbyPlayers)
{
try
{
var message = $"The player {lobbyPlayer.Id} has leveled up!";
_logger.LogInformation("Sending push notification to player {lobbyPlayer}", lobbyPlayer.Id);
await pushClient.SendPlayerMessageAsync(ctx, message, "Level up!", lobbyPlayer.Id);
}
catch (ApiException e)
{
_logger.LogError("Failed to send push notification to player {playerId}. Error: {Error}", lobbyPlayer.Id,
e.Message);
}
}
}
public async Task<List<Player>> GetPlayersFromLobbies(IExecutionContext ctx, IGameApiClient gameApiClient, string playerId)
{
var playersToNotify = new List<Player>();
try
{
var lobbyList = await gameApiClient.Lobby.GetJoinedLobbiesAsync(ctx, ctx.ServiceToken, ServiceId,playerId );
foreach (var lobby in lobbyList.Data)
{
var players = await GetLobbyData(ctx, gameApiClient, lobby);
playersToNotify.AddRange(players);
}
return playersToNotify;
}
catch (Exception e)
{
_logger.LogError("Failed to get players from lobbies. Error: {Error}", e.Message);
throw new Exception($"Failed to get players from lobbies. Error: {e.Message}");
}
}
public async Task<List<Player>> GetLobbyData(IExecutionContext ctx, IGameApiClient gameApiClient, string lobbyId)
{
try
{
var response = await gameApiClient.Lobby.GetLobbyAsync(ctx, ctx.ServiceToken, lobbyId, "cloud-code");
return response.Data.Players;
}
catch (Exception e)
{
_logger.LogError("Failed to get players from lobby: {lobbyId}. Error: {Error}", lobbyId, e.Message);
throw new Exception($"Failed to get players from lobby: {lobbyId}. Error: {e.Message}");
}
}
public class ModuleConfig : ICloudCodeSetup
{
public void Setup(ICloudCodeConfig config)
{
config.Dependencies.AddSingleton(GameApiClient.Create());
config.Dependencies.AddSingleton(PushClient.Create());
}
}
}
部署该模块。
请参阅部署 Hello World 以了解如何部署模块。
**注意:**如果您使用 UGS CLI 来部署模块,请不要忘记添加额外的服务帐户角色 Cloud Code Editor
。
配置触发器
要将 Cloud Code 资源关联到 Cloud Save key-saved
事件,请创建触发器。触发器会在触发事件时(例如,玩家每次保存键时)执行 Cloud Code 模块。
触发器配置中的过滤器将评估事件有效负载,仅当玩家升级且玩家实体发出 Cloud Save 事件时才会触发 Cloud Code 模块。
向 Triggers 服务发送 cURL 请求以创建触发器。
使用您先前创建的服务帐户凭据进行身份验证,将凭据编码为 key_id:secret_key
格式的 base64 字符串:
curl 'https://services.api.unity.com/triggers/v1/projects/<PROJECT_ID>/environments/<ENVIRONMENT_ID>/configs' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic <SERVICE_ACCOUNT_CREDENTIALS_ENCODED>' \
--data '{
"name": "announce-level-up",
"eventType": "com.unity.services.cloud-save.key-saved.v1",
"actionType": "cloud-code",
"actionUrn": "urn:ugs:cloud-code:AnnounceLevelUp/AnnounceLevelUp",
"filter": "data[\"idType\"] == \"player\" && data[\"key\"] == \"LEVEL\""
}'
此示例触发器会在玩家将值保存到 LEVEL
键时执行 Cloud Code 模块函数。
设置大厅
为了按照示例操作,您需要创建一个大厅。您可以在 Unity Cloud Dashboard 中使用 Cloud Code 辅助脚本来实现此目的:
JavaScript
const { LobbyApi } = require("@unity-services/lobby-1.2");
module.exports = async ({ params, context, logger }) => {
const lobbyApi = new LobbyApi(context);
const serviceId = "cloud-code";
try {
// Create a private lobby without any initial players.
const { data: lobby } = await lobbyApi.createLobby(serviceId, null, {
name: "sample-lobby",
maxPlayers: 4,
});
return lobby.id;
} catch (err) {
logger.error(`Error while calling out to Lobby: ${JSON.stringify(err.message)}`);
throw err;
}
};
记下大厅 ID。
向大厅添加玩家
您可以运行以下 Cloud Code 脚本,将玩家添加到从 Unity Cloud Dashboard 创建的大厅。请确保在每次测试运行时重新生成玩家 ID 令牌,以便添加新玩家。
请记下您添加到大厅的玩家的其中一个玩家 ID。您稍后可以使用玩家 ID,通过更新玩家的 Cloud Save 数据来验证结果。
此脚本接受 lobbyId
作为参数。
JavaScript
const { LobbyApi } = require("@unity-services/lobby-1.2");
module.exports = async ({ params, context, logger }) => {
const lobbyApi = new LobbyApi(context);
const serviceId = "cloud-code";
try {
await lobbyApi.joinLobbyById(params.lobbyId, serviceId, context.playerId);
} catch (err) {
logger.error("Failed to join lobby", { "error.message": err.message }, { lobbyId: params.lobbyId });
throw err;
}
};
**注意:**当您将玩家添加到大厅时,请注意大厅中允许的最大玩家数量。保留一个备用槽位,以便稍后添加玩家,在 Unity 项目中以该玩家的身份来测试示例。
验证结果
要验证结果,您可以设置一个订阅推送消息的 Unity 项目,并使用您先前创建的用于添加玩家的 Cloud Code 脚本,让经过身份验证的玩家加入大厅。
先决条件
要订阅推送消息,需要安装 Cloud Code SDK,并将 Unity Gaming Services(Unity 游戏服务)项目关联到 Unity 编辑器。
关联项目
将 Unity Gaming Services(Unity 游戏服务)项目与 Unity 编辑器关联。您可以在 Unity Cloud Dashboard 中找到您的 UGS Project ID。
在 Unity 编辑器中,选择 Edit(编辑)> Project Settings(项目设置)> Services(服务)。
关联您的项目。
如果项目没有 Unity Project ID:- 选择 Create a Unity Project ID(创建 Unity Project ID)> Organizations(组织),然后从下拉选单中选择一个组织。
- 选择 Create project ID(创建 Project ID)。
如果已有 Unity Project ID:- 选择 Use an existing Unity project ID(使用现有 Unity Project ID)。
- 从下拉选单中选择组织和项目。
- 选择 Link project ID(关联 Project ID)。
此时将显示您的 Unity Project ID,并且项目现在已关联到 Unity 服务。此外,还可以使用 UnityEditor.CloudProjectSettings.projectId
属性在 Unity 编辑器脚本中访问您的 Project ID。
SDK 安装
要安装适用于 Unity 编辑器的最新 Cloud Code 包,请执行以下操作:
- 在 Unity 编辑器中,打开 Window(窗口)> Package Manager(包管理器)。
- 在 Package Manager(包管理器)中,选择 **Unity Registry(Unity 注册表)**列表视图。
- 搜索
com.unity.services.cloudcode
或在列表中找到 Cloud Code 包。 - 选择该包,然后选择 Install(安装)。
请查看 Unity - 手册:Package Manager(包管理器)窗口,熟悉 Unity Package Manager(包管理器)界面。
您可以使用 Cloud Code SDK 版本 2.4.0+ 订阅消息。
创建 Monobehaviour
脚本
要订阅玩家级消息,请设置一个 Monobehaviour
脚本。如需了解更多信息,请参阅发送推送消息。
您可以将以下示例代码用于您的 MonoBehaviour
脚本:
C#
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Services.Authentication;
using Unity.Services.CloudCode;
using Unity.Services.CloudCode.Subscriptions;
using Unity.Services.Core;
using UnityEngine;
namespace CloudCode
{
public class CloudCodePushExample : MonoBehaviour
{
async void Start()
{
await UnityServices.InitializeAsync();
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log(AuthenticationService.Instance.PlayerId);
await SubscribeToPlayerMessages();
}
// This method creates a subscription to player messages and logs out the messages received,
// the state changes of the connection, when the player is kicked and when an error occurs.
Task SubscribeToPlayerMessages()
{
// Register callbacks, which are triggered when a player message is received
var callbacks = new SubscriptionEventCallbacks();
callbacks.MessageReceived += @event =>
{
Debug.Log(DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK"));
Debug.Log(
$"Got player subscription Message: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
};
callbacks.ConnectionStateChanged += @event =>
{
Debug.Log(
$"Got player subscription ConnectionStateChanged: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
};
callbacks.Kicked += () => { Debug.Log($"Got player subscription Kicked"); };
callbacks.Error += @event =>
{
Debug.Log(
$"Got player subscription Error: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
};
return CloudCodeService.Instance.SubscribeToPlayerMessagesAsync(callbacks);
}
}
}
首次运行 Monobehaviour
脚本时,会记录玩家 ID。请记下此值。
加入大厅
您可以使用先前创建的用于添加玩家的 Cloud Code 脚本,让经过身份验证的玩家加入大厅。将先前创建的大厅 ID 作为 lobbyId
参数传入,将运行 Unity 项目时记下的玩家 ID 作为 playerId
参数传入。
更新 Cloud Save 中的 Level
键
您可以运行以下脚本来更新 Cloud Save 键以触发 Cloud Code 模块。
它接受一个字符串参数 playerId
和一个数字参数 level
。
**注意:**如果您有一个自定义触发器使用带有事件值的过滤器,请确保将 level
参数指定为数字。如果没有定义特定类型参数,则参数将作为字符串传递。如果您的过滤器将值评估为数字,而您传入字符串,则触发器将无效。
请使用您先前添加到大厅的玩家的其中一个玩家 ID。这样可确保在 Unity 项目中经过身份验证的玩家会收到消息,因为他们在同一个大厅。
JavaScript
const { DataApi } = require("@unity-services/cloud-save-1.4");
module.exports = async ({ params, context, logger }) => {
const cloudSaveApi = new DataApi(context);
try {
await cloudSaveApi.setItem(context.projectId, params.playerId, {
key: "LEVEL",
value: params.level,
});
result = await cloudSaveApi.getItems(context.projectId, params.playerId);
return result.data;
} catch (err) {
logger.error("Error while calling out to Cloud Save", { "error.message": err.message });
throw err;
}
};
验证结果
确保 Unity 项目正在运行,Unity 项目中经过身份验证的玩家位于大厅内,并更新 Cloud Save 中的 LEVEL
密钥。
您应该会在 Unity 编辑器中看到玩家分数被超过时发送的推送消息:
Got player subscription Message:
{
"data_base64": <BASE64-ENCODED-DATA>,
"time": "2023-11-13T16:21:35.102058117Z",
"message": "The player Z96pNb4wfgdaMLqMQfWpwXEclaRZ has leveled up!",
"specversion": "1.0",
"id": <ID>,
"source": "https://cloud-code.service.api.unity.com",
"type": "com.unity.services.cloud-code.push.v1",
"projectid": <PROJECT-ID>,
"environmentid": <ENVIRONMENT-ID>,
"correlationid": <CORRELATION-ID>,
"messagetype": "Level up!",
}