はじめに
はじめまして、株式会社グレンジでクライアントエンジニアをしている田島と申します🙇♂️
本投稿は グレンジ Advent Calendar 2024 の22日目の記事です
この記事の対象となる読者
- 開発しているゲームにGameAnalyticsを導入したい人
- GameAnalyticsを使ってABテストを実施したい人
ABテストとは
アプリを新規インストールしたユーザーを2つ以上のグループに割り振りグループごとにアプリの内容を分岐させる手法のことで、期間の違いによる影響を受けることなく複数のバージョンの数字の違いを見ることができます
GameAnalyticsにおいてはリモートコンフィグ機能を使用して一つのkeyに対して複数種類のvalueをGameAnalyticsサーバーから返すことで割り振られたグループをクライアント側で判別することができるようになっています
公式ドキュメント
前提条件
- Unity 2022 3.47f1
- com.gameanalytics.sdk: 7.10.0
- com.google.external-dependency-manager: 1.2.182
- プラットフォーム: Android
- GameAnalyticsを導入するUnityプロジェクトが作成済みであること
手順
- GameAnalyticsプロジェクトの作成(ブラウザ)
- GameAnalytics SDKのインポート
- GameAnalytics Settingsの設定
- GameAnalytics SDKの初期化
- 疎通確認
- ABテストの実装
- ABテストの作成
- ABテストの分岐の確認
- まとめ
- サンプルコード
GameAnalyticsプロジェクトの作成(ブラウザ)
それぞれの項目が済んでいる方は必要に応じてスキップしてください
GameAnalyticsアカウントの作成
GameAnalyticsのトップ画面 から右上の Sign up
を選択し、メールアドレスもしくはGoogleアカウントで登録します
職種や利用する理由について聞かれるので回答してください
パスワードを設定します
組織に関する情報を入力し、組織を作成します
プロジェクトの作成
アカウントの登録まで完了すると↓以下のような組織の管理画面に遷移します
プロジェクトを作成するために組織の下にスタジオを作る必要があるため、真ん中の + Create Studio
ボタンを押下しスタジオ作成ダイアログを開きます
スタジオが所属する組織、スタジオの名前を入力しスタジオを作成します
組織の管理画面の真ん中に + Create Game
というボタンが表示されるようになるのでこれを押下しプロジェクト作成ダイアログを開きます
ゲームが所属する組織、スタジオ、タイトル、SDKを導入する環境、プラットフォームを入力しプロジェクトを作成します
ここではプラットフォームとしてAndroidを選択します
iOSもリリースする場合、iOS用のGameAnalyticsプロジェクトを作成する必要があります
プロジェクトの作成を完了すると、組織の管理画面に作成したプロジェクトが表示されるようになります
プロジェクト作成後、最大1分ほどプロジェクトのページにアクセスできません
GameAnalytics SDKのインポート
Unityエディタでの作業に移ります
SDKのインポートはUnity Package Manager(以下upm)を使用する方法と .unitypackage
ファイルを使用する方法がありますが、ここではupmを使用する方法を取ります
External Dependency Managerのインポート
upmでのインポートではGoogleのExternal Dependency Managerが必要となるため ダウンロードページ からターボールファイル(.tgz)をダウンロードし Packages
ディレクトリの下に配置します
manifest.jsonの更新
Packages/manifest.json
に
"com.gameanalytics.sdk": "7.10.0",
"com.google.external-dependency-manager": "file:com.google.external-dependency-manager-1.2.182.tgz"
を記載します
GameAnalytics SDKの最新バージョンについてはどうやらGameAnalyticsのドキュメント内には記載がないようなので OpenUPM を参照します
また、Packages/manifest.json
にScoped Registryを追加します
"scopedRegistries": [
{
"name": "Game Package Registry by Google",
"url": "https://unityregistry-pa.googleapis.com/",
"scopes": [
"com.google"
]
},
{
"name": "OpenUPM",
"url": "https://package.openupm.com/",
"scopes": [
"com.gameanalytics"
]
}
]
Unityエディタに戻るとリロードが走り、 Packages
以下に GameAnalytics
ディレクトリが追加されているかと思います
GameAnalytics Settingsの設定
GameAnalyticsの設定ファイルを更新していきます
Window > GameAnalytics > Select Settings
を押すと Assets/Resources/GameAnalytics/Settings.asset
というファイルが作成されます
すでにある場合は該当のファイルが選択されます
Inspector上にメールアドレスとパスワードを入力するフォームが表示されるかと思うのでGameAnalyticsに登録したアカウントのメールアドレス、パスワードを入力し Login
を押します
GameAnalyticsで作成したプロジェクトと同じプラットフォームをプルダウンから選択し、 Add platform
を押します
組織、スタジオ、プロジェクト名をプルダウンから選択するとプロジェクトに紐づく Game Key
および Secret Key
が自動で入力されます
Buildの欄はデフォルトのままで問題ないですが、アプリのバージョンに対応させるとわかりやすいと思います
入力しただけだと .asset
ファイルが更新されないので Cmd(Ctrl) + S
で保存します
GameAnalytics SDKの初期化
GameAnalytics SDKを初期化するプログラムはシンプルで
GameAnalytics.Initialize();
を呼び出すだけです
サンプルコードでは簡便のため、Awakeから初期化を呼び出していますがプロジェクトの初期化シーケンスの適切なタイミングで呼び出すことが望ましいです
疎通確認
このままABテストの実装に移ってもよいのですがABテスト作成時にGameAnalytics SettingsのBuild番号を指定する項目があり、アクティブユーザーが存在しているBuild番号しか選択肢として表示されないためここで一度実機による疎通確認を行います
アプリをビルドしプレイしたのちGameAnalyticsのプロジェクトのダッシュボードに移動します
左のタブから RealTime
を選択し遷移したページで 👁️ Live Events
を選択します
疎通が完了していれば↓以下のようにイベントが送信されています
ABテストの実装
内部の処理としては先述の通り、リモートコンフィグから値を取得しその値によってアプリ内部の処理を分岐させる、というものです
GameAnalyticsの初期化が完了するまではテストグループを判定できないため初期化が完了した際のコールバックでフラグを立てます
private bool _initializedFlag;
private void Awake()
{
GameAnalytics.onInitialize += OnInitialize;
...
}
private void OnInitialize(object sender, bool eventArgs)
{
_initializedFlag = true;
}
また、リモートコンフィグが取得可能になるまでは同様にテストグループを判定できないためリモートコンフィグのステータスが更新された際のコールバックでフラグを立てます
private bool _remoteConfigIsReady;
private void Awake()
{
GameAnalytics.OnRemoteConfigsUpdatedEvent += OnUpdateRemoteConfigs;
...
}
private void OnUpdateRemoteConfigs()
{
_remoteConfigIsReady = GameAnalytics.IsRemoteConfigsReady();
}
_initializedFlag
および _remoteConfigIsReady
が true
になるまで待機し true
になったらリモートコンフィグから値を取得します
private IEnumerator GetTestGroup()
{
yield return new WaitUntil(() => _initializedFlag && _remoteConfigIsReady);
var testGroupRemoteConfigValue = GameAnalytics.GetRemoteConfigsValueAsString(
"test_group",
"group_a"
);
PlayerPrefs.SetString("test_group", testGroupRemoteConfigValue);
PlayerPrefs.Save();
}
サンプルコードでは簡便のため PlayerPrefs
に値を保存していますがご自身のプロジェクトにユーザーデータを保存する機構などがあればそちらで保存することを推奨します
プロジェクトのサーバーが建てられているのであればそちらで保存するのがよいでしょう
取得したリモートコンフィグの値をフラグとしてラップして返すようにすると扱いやすくなるかと思います
例:
public class UserData
{
private string _testGroup;
// NOTE: グループBだったら難易度を上げたver
public bool HardModeFlag => _testGroup == "group_b";
}
サンプルコードではABテストのグループによって画面に表示するテキストを分岐させるようにしています
private IEnumerator GetTestGroup()
{
yield return new WaitUntil(() => _initializedFlag && _remoteConfigIsReady);
var testGroupRemoteConfigValue = GameAnalytics.GetRemoteConfigsValueAsString(
"test_group",
"group_a"
);
PlayerPrefs.SetString("test_group", testGroupRemoteConfigValue);
PlayerPrefs.Save();
var testGroup = PlayerPrefs.GetString("test_group", "group_a");
_testGroupText.text = testGroup;
}
ABテストの作成
GameAnalyticsのプロジェクトのダッシュボードに移動します
左のタブから A/B Testing
を選択し遷移したページで Create experiment
を選択します
↓以下の項目を入力します
- ABテスト名
- ビルド番号
- ユーザー上限
- ABテストに参加する割合
- リモートコンフィグのkey
- それぞれのグループがリモートコンフィグから取得する値
- 開始時間
- 比較する指標
入力後は↓以下のようになります
ABテストは新規インストールユーザーのみが対象となり、例えばABテストに参加する割合(Enrollment rate)を10にした場合、新規インストールユーザーの10%がABテストに参加することになります
Start test
を押してABテストを開始します
ABテストの分岐の確認
アプリをビルドし、再インストールを繰り返してアプリの内容が分岐することを確認します
まとめ
最後までご覧いただきありがとうございます
SDKの導入は確認するまでにラグがあったりでしんどいことが多いイメージですがGameAnalyticsはほぼリアルタイムで確認できてその点はとてもありがたいです
この記事が1人でも多くの人の助けになれば幸いです
指摘、質問等ございましたら遠慮なくコメントしていただけますと幸いです🙇♂️
サンプルコード
StartScene.cs
using System;
using System.Collections;
using GameAnalyticsSDK;
using TMPro;
using UnityEngine;
public class StartScene : MonoBehaviour
{
private const string DefaultTestGroupValue = "Default";
private const string TestGroupRemoteConfigKey = "test_group";
private const string TestGroupRemoteConfigDefaultValue = "group_a";
private const string TestGroupPreferenceKey = "test_group";
[SerializeField] private TextMeshProUGUI _testGroupText;
private bool _initializedFlag;
private bool _remoteConfigIsReady;
private void Awake()
{
GameAnalytics.onInitialize += OnInitialize;
GameAnalytics.OnRemoteConfigsUpdatedEvent += OnUpdateRemoteConfigs;
GameAnalytics.Initialize();
}
private void Start()
{
var testGroup = PlayerPrefs.GetString(TestGroupPreferenceKey, "");
if (!string.IsNullOrEmpty(testGroup))
{
_testGroupText.text = testGroup;
return;
}
StartCoroutine(GetTestGroupCoroutine());
}
private void OnInitialize(object sender, bool eventArgs)
{
_initializedFlag = true;
}
private void OnUpdateRemoteConfigs()
{
_remoteConfigIsReady = GameAnalytics.IsRemoteConfigsReady();
}
private IEnumerator GetTestGroupCoroutine()
{
#if UNITY_EDITOR
_remoteConfigIsReady = true;
#endif
yield return new WaitUntil(() => _initializedFlag && _remoteConfigIsReady);
var testId = GameAnalytics.GetABTestingId();
if (string.IsNullOrEmpty(testId))
{
SaveTestGroup(DefaultTestGroupValue);
SetTestGroupText(DefaultTestGroupValue);
yield break;
}
var testGroupRemoteConfigValue = GameAnalytics.GetRemoteConfigsValueAsString(
TestGroupRemoteConfigKey,
TestGroupRemoteConfigDefaultValue
);
SaveTestGroup(testGroupRemoteConfigValue);
SetTestGroupText(testGroupRemoteConfigValue);
}
private void SaveTestGroup(string testGroup)
{
PlayerPrefs.SetString(TestGroupPreferenceKey, testGroup);
PlayerPrefs.Save();
}
private void SetTestGroupText(string text)
{
_testGroupText.text = text;
}
}