与其他 Unity 服务集成

要充分发挥 Cloud Code 的潜力,可以通过 C# Service SDK 或 REST API 将其与其他 Unity Gaming Services(Unity 游戏服务)连接起来。

C# SDK 更简单,并提供更一致的体验。但是,如果要使用的 UGS 服务还没有 Cloud Code C# SDK,可以通过其 REST API 与其连接。

使用 UGS SDK

要从 NuGet 访问 Cloud Code C# 服务 SDK,请搜索 Com.Unity.Services.CloudCode.Apis。安装该包后,您可以在 C# 模块中使用该包来简化与其他 Unity 服务的连接方式。

注意:Cloud Code C# SDK 的文档尚未完成。现在,您可以暂时参考 JavaScript 文档,其具有类似的结构。此外,请利用集成开发环境的自动补全和智能感知功能。

如需查看 Cloud Code C# SDK 的完整列表,请参阅可用库页面。

GameApiClient

GameApiClient 类是每个服务的 Cloud Code C# SDK 的包装器。从 Cloud Code 模块调用 UGS 服务时,可以使用此类提供的简化接口。

要使用 GameAPIClient 接口,请在 ICloudCodeSetup 配置器中将 GameApiClient 注册为单例

C#

public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(GameApiClient.Create());
        }
    }

与 UGS 集成

您可以在归属于 CloudCodeFunction 的任何函数中使用 GameApiClient 类。要使用 GameApiClient 类,请将 IGameApiClient 接口作为参数传入函数。

以下示例演示了如何在 Cloud Save 中保存和读取数据:

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.CloudSave.Model;

namespace ExampleModule;

public class CloudSaveSdkSample
{
    private static ILogger<CloudSaveSdkSample> _logger;

    public CloudSaveSdkSample(ILogger<CloudSaveSdkSample> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("SaveData")]
    public async Task SaveData(IExecutionContext context, IGameApiClient gameApiClient, string key, object value)
    {
        try
        {
            await gameApiClient.CloudSaveData.SetItemAsync(context, context.AccessToken, context.ProjectId,
                context.PlayerId, new SetItemBody(key, value));
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to save data. Error: {Error}", ex.Message);
            throw new Exception($"Failed to save data for playerId {context.PlayerId}. Error: {ex.Message}");
        }
    }

    [CloudCodeFunction("GetData")]
    public async Task<object> GetData(IExecutionContext context, IGameApiClient gameApiClient, string key)
    {
        try
        {
            var result = await gameApiClient.CloudSaveData.GetItemsAsync(context, context.AccessToken,
                context.ProjectId, context.PlayerId, new List<string> { key });
            return result.Data.Results.First().Value;
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to get data. Error: {Error}", ex.Message);
            throw new Exception($"Failed to get data for playerId {context.PlayerId}. Error: {ex.Message}");
        }
    }

    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(GameApiClient.Create());
        }
    }
}

通过 REST API 与 UGS 进行连接

如果要使用尚无 C# SDK 的服务,也可以通过 REST API 直接与服务进行连接。

身份验证

根据您的用例,您可以使用 AccessTokenServiceToken 对 API 调用进行身份验证。如果使用 ServiceToken 对 UGS Client API 进行身份验证,请确保要调用的服务支持服务令牌。如需查看支持服务令牌的服务和用例的列表,请参阅服务令牌和访问令牌支持文档。

**注意:**如果要调用的服务提供了 Cloud Code C# SDK,建议使用该 SDK,而不是直接调用服务 API。如需了解更多信息,请参阅可用库列表。

调用 API

通过依赖项注入可以将 API 接口创建为单例,并将它们注入到模块中。

除了 UGS API 之外,还可以将外部 API 实现为单例。如需查看如何实现外部 API 的示例,请参阅有关如何与外部服务集成的文档。

例如,您可以为 User Generated Content API 定义一个接口来创建和获取内容:

**注意:**需要先在项目中启用 User Generated Content 服务,然后才能使用该服务。

C#

using System.Net;
using Newtonsoft.Json;
using RestSharp;
using RestSharp.Authenticators;
using Unity.Services.CloudCode.Core;

namespace GeneratedContentSample;
public interface IGeneratedContentApi
{
    public Task<GeneratedContentGetApiResponse?> GetGeneratedContentForPlayer(IExecutionContext context);
    public Task<GeneratedContentPostApiResponse?> CreateContent(IExecutionContext context);
}

public class GeneratedContentPostApiResponse
{
    public string? uploadThumbnailUrl { get; set; }
    public string? uploadContentUrl { get; set; }
    public string? version { get; set; }
    public Content content { get; set; }
}

public class GeneratedContentGetApiResponse
{
    public int? offset { get; set; }
    public int? limit { get; set; }
    public int? total { get; set; }
    public Content[] results { get; set; }
}

public class Content
{
    public string id { get; set; }
    public string? name { get; set; }
    public string? customId { get; set; }
    public string? description { get; set; }
    public string visibility { get; set; }
    public string moderationStatus { get; set; }
    public string? version { get; set; }
    public string createdAt { get; set; }
    public string updatedAt { get; set; }
    public string deletedAt { get; set; }
    public string projectId { get; set; }
    public string environmentId { get; set; }
    public string creatorAccountId { get; set; }
    public string? thumbnailUrl { get; set; }
    public string? downloadUrl { get; set; }
    public string? portalUrl { get; set; }
    public string? metadata { get; set; }
    public float? averageRating { get; set; }
    public bool isUserSubscribed { get; set; }
    public string assetUploadStatus { get; set; }
    public string thumbnailUploadStatus { get; set; }
    public string? webhookEventName { get; set; }
}


public class GeneratedContentApi : IGeneratedContentApi
{
    private readonly RestClient _httpClient;

    public GeneratedContentApi()
    {
        _httpClient = new RestClient("https://ugc.services.api.unity.com/v1/");
    }

    public async Task<GeneratedContentGetApiResponse?> GetGeneratedContentForPlayer(IExecutionContext context)
    {
        var request = new RestRequest($"content/search")
        {
            Authenticator = new JwtAuthenticator(context.AccessToken)
        };

        // Pass through the analytics user id and Unity installation id to the service.
        if (context.AnalyticsUserId != null)
            request = request.AddHeader("analytics-user-id", context.AnalyticsUserId);
        if (context.UnityInstallationId != null)
            request = request.AddHeader("unity-installation-id", context.UnityInstallationId);


        RestResponse response = await _httpClient.ExecuteAsync(request);
        if (response.StatusCode != HttpStatusCode.OK)
            throw new Exception("Failed to get User Generated Content: " + response.Content);

        return JsonConvert.DeserializeObject<GeneratedContentGetApiResponse?>(response.Content!);
    }

    public async Task<GeneratedContentPostApiResponse?> CreateContent(IExecutionContext context)
    {
        var request = new RestRequest($"projects/{context.ProjectId}/environments/{context.EnvironmentId}/content", Method.Post)
        {
            Authenticator = new JwtAuthenticator(context.AccessToken)
        }.AddJsonBody(new
        {
            name = "a festive treat",
            description = "banasco is a traditional british toast",
        });

        // Pass through the analytics user id and Unity installation id to the service.
        if (context.AnalyticsUserId != null)
            request = request.AddHeader("analytics-user-id", context.AnalyticsUserId);
        if (context.UnityInstallationId != null)
            request = request.AddHeader("unity-installation-id", context.UnityInstallationId);


        RestResponse response = await _httpClient.ExecuteAsync(request);
        if (response.StatusCode != HttpStatusCode.OK)
            throw new Exception("Failed to create user generated content: " + response.Content);

        return JsonConvert.DeserializeObject<GeneratedContentPostApiResponse>(response.Content!);

    }

}

然后可以将 GeneratedContentApi 添加为单例:

C#

public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton<IGeneratedContentApi>(new GeneratedContentApi());
        }
    }

然后可以将 IGeneratedContentApi 接口作为函数参数传入模块函数,并访问 UGC API。

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Core;

namespace GeneratedContentSample;

public class GeneratedContent
{
    private readonly ILogger<GeneratedContent> _logger;
    public GeneratedContent(ILogger<GeneratedContent> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("AddContent")]
    public async Task<GeneratedContentPostApiResponse?> AddUserGeneratedContent(IExecutionContext ctx, IGeneratedContentApi contentClient)
    {
        try
        {
            return await contentClient.CreateContent(ctx);
        }
        catch (Exception e)
        {
            _logger.LogError("Failed to add content. Error: {Error}", e.Message);
            throw new Exception($"Failed to add content for player {ctx.PlayerId}. Error: {e.Message}");
        }
    }

    [CloudCodeFunction("GetContent")]
    public async Task<GeneratedContentGetApiResponse?> GetContent(IExecutionContext ctx, IGeneratedContentApi contentClient)
    {
        try
        {
            return await contentClient.GetGeneratedContentForPlayer(ctx);
        }
        catch (Exception e)
        {
            _logger.LogError("Failed to get content. Error: {Error}",  e.Message);
            throw new Exception($"Failed to get content. Error: {e.Message}");
        }
    }

    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton<IGeneratedContentApi>(new GeneratedContentApi());
        }
    }
}