# Game Overrides 与 CCD 集成

> Use Remote Config and badges to integrate Game Overrides with Cloud Content Delivery so you can target content to specific audiences.

通过 [Game Overrides](/game-overrides.md)，您可以针对特定受众定向投放内容，例如，通过运行 A/B 测试以及根据地理位置和特定时间段定制内容。通过标示来标识预期的内容后，Remote Config 会在不影响实时玩家的情况下测试该内容，同时还会将标示的内容分发给特定玩家。

要集成 Game Overrides，请完成以下步骤：

1. [安装 Remote Config 包](#install-the-remote-config-package)
2. [关联您的 Project ID](#link-your-project-id)
3. [创建一个覆盖](#create-an-override)以使用 CCD 标示来定向投放内容
4. [将 Remote Config 集成到您的游戏代码中](#integrate-remote-config-into-your-game-code)
5. [从 CCD 获取相应的资源](#retrieve-the-appropriate-assets-from-ccd)

## 安装 Remote Config 包##install-the-remote-config-package

如需了解如何安装包，请参阅 [Package Manager（包管理器）](https://docs.unity3d.com/Manual/Packages.html)。

> **Note:**
>
> 这些步骤可能因您使用的 Unity 编辑器版本而异。

1. 在 Unity 编辑器中，选择 **Window（窗口）**> **Package Manager（包管理器）**。
2. 在 Packages（包）列表视图中，选择 **Remote Config**。
3. 在包的具体详细信息视图中，选择并安装需要导入到项目中的 Remote Config 包版本。

## 关联您的 Project ID##link-your-project-id

如果尚未将您的 Unity 项目关联到 Project ID，必须执行此操作。

要在 Unity 编辑器中关联您的项目，请执行以下操作：

1. 打开 Services（服务）窗口（选择 **Window（窗口）**> **General（常规）**> **Services（服务）**）。
2. 选择 **General settings（常规设置）**。
3. 在 Project Settings（项目设置）菜单的 Services（服务）部分，从下拉选单中选择您的组织。必须登录到 Unity Hub 才能查看关联组织的列表。
4. 选择 **Use an existing Unity project ID（使用现有 Unity Project ID）** 将项目关联到先前在开发者控制面板 (Developer Dashboard) 上创建的 ID，或选择 **Create project ID（创建 Project ID）** 将项目关联到新 ID。


**Frame:**
![](/api/media?file=/ccd/media/images/ccd-campaigns-link-1.png)

## 创建覆盖##create-an-override

您可以在 [Unity Dashboard（Unity 后台）](https://dashboard.unity3d.com/)上通过 Game Overrides 服务或 Cloud Content Delivery 服务来创建覆盖。请参阅[创建覆盖](/game-overrides/get-started.md)。

1. 通过以下方式找到 Game Overrides 服务：在导航栏中选择 **LiveOps**，然后在二级导航栏中选择 **Game Overrides** > **Overrides（覆盖）**。\
   或者，也可以在 Cloud Content Delivery 服务本身的存储桶的 Targeting（定向投放）选项卡中选择 **Create Override（创建覆盖）** 来导航到 Overview（概览）页面。

2. 选择 **Create Override（创建覆盖）**。

3. 为您的覆盖命名，然后选择 **Next（下一步）**。

4. 指定 [JEXL 条件](https://docs.unity3d.com/Packages/com.unity.remote-config@latest/index.html?subfolder=/manual/CampaignsAndSettings.html%23condition)来选择要定向投放到的玩家，然后指定要定向投放到的这些玩家的百分比。选择 **Next（下一步）**。

5. 选择 **+ Choose content type（+ 选择内容类型）**，然后选择 **Cloud Content Delivery** > **Badge（标示）**。选择 **Done（完成）**。

   > **Note:**
   >
   > 要使用常规 Remote Config 配置（而不是定向投放 CCD 标示）来创建覆盖，请选择 **Config Overrides（配置覆盖）**。请参阅 [Remote Config](https://docs.unity3d.com/Packages/com.unity.remote-config@latest/index.html?subfolder=/manual/GameOverridesAndSettings.html)。

6. 选择 **Choose a bucket（选择存储桶）**，然后选择要使用的存储桶。

7. 选择 **Choose your badge（选择标示）**，然后选择要使用的标示。选择 **Choose（选择）**，然后选择 **Next（下一步）**。

8. 选择运行覆盖的 **Start Date（开始日期）** 和 **End Date（结束日期）**。选择 **Finish（完成）**。

9. 选择 **Enable（启用）** 以激活您的覆盖。

## 将 Remote Config 集成到您的游戏代码中##integrate-remote-config-into-your-game-code

`RemoteConfig` API 包含在 `Unity.Services` 命名空间中，您必须将其加入您的游戏脚本中。如需了解更多有关其类和方法的信息，请参阅 [Remote Config 脚本 API](https://docs.unity3d.com/Packages/com.unity.remote-config@latest/index.html?subfolder=/api/index.html) 文档。

Remote Config 服务有一个可访问的 [`RemoteConfigService`](https://docs.unity3d.com/Packages/com.unity.remote-config-runtime@latest/index.html?subfolder=/api/Unity.Services.RemoteConfig.RemoteConfigService.html) 实例用于在运行时获取并应用您的配置。在以下示例中，您将使用这个实例来获取由覆盖定义的 CCD 配置的键/值对。此键/值对中的键是静态字符串 `CCD_CONFIG_KEY`。然后，当获取成功时，您将调用 `ApplyCcdConfig` 函数。`ApplyCcdConfig` 接受一个 [`ConfigResponse`](https://docs.unity3d.com/Packages/com.unity.remote-config-runtime@latest/index.html?subfolder=/api/Unity.RemoteConfig.ConfigResponse.html) 结构，这个结构表示对获取请求的响应。

*C#*

```cs
public struct userAttributes {}
public struct appAttributes {}
 
async Task InitializeRemoteConfigAsync()
{
  // initialize handlers for unity game services
  await UnityServices.InitializeAsync();
 
  // remote config requires authentication for managing environment information
  if (!AuthenticationService.Instance.IsSignedIn)
  {
    await AuthenticationService.Instance.SignInAnonymouslyAsync();
  }
}
 
async void Start()
{
    if (Utilities.CheckForInternetConnection())
    {
        await InitializeRemoteConfigAsync();
    }
 
    RemoteConfigService.Instance.appConfig = RemoteConfigService.Instance.GetConfig("ccd");
    RemoteConfigService.Instance.FetchCompleted += ApplyCcdConfig;
    RemoteConfigService.Instance.FetchConfigs("ccd", new userAttributes(), new appAttributes());
}
 
void ApplyCcdConfig(ConfigResponse configResponse)
{
    switch (configResponse.requestOrigin)
    {
        case ConfigOrigin.Default:
            Debug.Log("Default values will be returned");
            break;
        case ConfigOrigin.Cached:
            Debug.Log("Cached values loaded");
            break;
        case ConfigOrigin.Remote:
            Debug.Log("Remote Values changed");
 
            if (RemoteConfigService.Instance.appConfig.HasKey("CCD_CONFIG_KEY"))
            {
                // get the correct key-value pair for CCD (the key will always be "CCD_CONFIG_KEY")
                string jsonValue = RemoteConfigService.Instance.appConfig.GetJson("CCD_CONFIG_KEY");
                // you will use jsonValue in the next step
            }
 
            break;
    }
}
```

## 从 CCD 获取相应的资源##retrieve-the-appropriate-assets-from-ccd

成功获取 CCD 配置后，您可以提取存储桶信息和标示名称，进而从 CCD 获取相应的资源。

首先定义一个 `CcdConfig` 类。

*C#*

```cs
public class CcdConfig
{
   public string bucketId;
   public string bucketName;
   public string badgeName;
}
```

在 `ApplyCcdConfig` 函数中，您可以从获取的 CCD 配置中提取这些值，并使用这些值从 CCD 获取相应的资源。

*C#*

```cs
// extract the config values that you have defined when creating your Override
CcdConfig ccdConfig = JsonUtility.FromJson<CcdConfig>(jsonValue);
 
// the bucketId and badgeName are required to fetch the appropriate assets
var bucketId = ccdConfig.bucketId;
var badgeName = ccdConfig.badgeName;
 
// fetch the entry from CCD
// Note that for a dynamic file path, a config override can be used in order to fetch
// the correct file path for your asset
JObject entry = CcdManager.Instance.GetEntry(bucketId, badgeName, "ENTRY_PATH");
// fetch the entry's content from CCD
var content = CcdManager.Instance.GetContent(bucketId, entry["entryid"].ToString());
 
// use your asset(s) as needed
// YOUR CODE HERE
```

`CCDManager` 类使用 CCD 的 [Client API](https://services.docs.unity.com/content-delivery-client) 来获取资源。您必须将 `CCDManager` 附加到每个场景中的游戏对象。

> **Note:**
>
> 如果您的资源很大，请考虑采用异步方式获取资源。

`CCDManager` 类可能类似于以下代码示例：

*C#*

```cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using Unity.RemoteConfig;
 
using System.Net;
using System;
using System.IO;
 
public class CcdManager : MonoBehaviour
{
 
   private static string projectId = "YOUR_PROJECT_ID_HERE";
   private static string ccdBaseUrl = $"https://{projectId}.client-api.unity3dusercontent.com/client_api/v1/";
 
   private static CcdManager m_Instance;
   public static CcdManager Instance { get { return m_Instance; } }
 
   private void Awake()
   {
     if (m_Instance != null && m_Instance != this)
       {
           Destroy(this.gameObject);
           return;
       }
 
       m_Instance = this;
       DontDestroyOnLoad(this.gameObject);
   }
 
   public JObject GetEntry(string bucketId, string badgename, string entrypath) {
     var entryUrl = $"{ccdBaseUrl}/buckets/{bucketId}/release_by_badge/{badgename}/entry_by_path/?path={entrypath}";
 
     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(entryUrl);
     HttpWebResponse response = (HttpWebResponse)request.GetResponse();
 
     StreamReader reader = new StreamReader(response.GetResponseStream());
     string jsonResponse = reader.ReadToEnd();
     JObject ccdEntry =  JObject.Parse(jsonResponse);
 
     return ccdEntry;
   }
 
   public string GetContent(string bucketId, string entryid) {
     var contentUrl = $"{ccdBaseUrl}/buckets/{bucketId}/entries/{entryid}/content/";
 
     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(String.Format(contentUrl));
     HttpWebResponse response = (HttpWebResponse)request.GetResponse();
 
     StreamReader reader = new StreamReader(response.GetResponseStream());
     string contentValue = reader.ReadToEnd();
 
     return contentValue;
   }
}
```

现在，您已成功获取由覆盖定义的资源。
