Game Server Services(GS2)を使ってUnityでアカウントの引き継ぎ機能を実装してみた

2024.01.22

今回は、GS2-Accountの引き継ぎ機能を利用して、ゲームを実行するデバイスの変更時などにゲームアカウント情報の引き継ぎができる機能をUnityで実装してみたので、その内容をご紹介します。

環境

  • Unity 2022.3.14f1
  • UniTask 2.5.0
  • GS2 C# SDK 2023.12.10
  • GS2 SDK for Unity 2023.12.14

アカウントの引き継ぎとは

今回紹介する機能はスマートフォン向けのソーシャルゲームなどでよく実装されている機能で、ユーザーが異なるデバイスで同じアカウントを利用したい場合に利用されます。例えば、ユーザーが最初にゲームをインストールしたスマートフォンから機種変更しても同じアカウント情報を利用したかったり、マルチプラットフォームで展開されているゲームで上の画像のようにスマートフォン版でもPC版でも同じアカウントを共有できるようにしたい場合に便利な機能です。

具体的な引き継ぎの方法としては、一時的な引き継ぎ用パスワードを作成しそれを引き継ぎ先のデバイスで入力する方法の他、GoogleやFacebook等のアカウントにゲームアカウントを紐付ける方法などがよく使われています。

こういった機能がソーシャルゲームなどのモバイル環境のゲームでよく実装される理由として、多くのモバイルゲームで匿名アカウントの仕組みが使われている点が挙げられます。匿名アカウントとは、サーバー上で最低限のプレイヤーの識別をすることのみを目的にしたアカウントです。一般的なアカウントとは異なり、IDとパスワードをシステムがランダムに決定します。プレイヤーがどういった人間であるかは識別する必要がないため、匿名アカウントの作成にユーザーは通常関与しません。

一般的なWebサービスでは最初にアカウント登録を行い、その後は作成したアカウントを利用すればどのデバイスでも同じアカウントを利用できますが、ソーシャルゲームなどでは多くの場合ゲームの初回起動時にユーザーからは見えないところで匿名アカウントが自動的に作成され、次回以降の起動時はデバイスに保存された認証パラメータを元にアカウントへ自動的にログインします。ユーザーがアカウントの存在自体を認識しない仕組みになっているため、異なるデバイスでアカウントを共有するためには上記のような引き継ぎ機能の用意が必要になってきます。

なぜ、このような匿名アカウントの仕組みを採用するモバイルゲームが多いかの理由としては、アカウント作成などの煩わしい手間をかけることなく、少しでも気軽にユーザーにゲームを触ってもらうため、という点が大きいと思われます。

GS2における引き継ぎ

匿名アカウントを管理できるGS2-Accountには、標準でアカウントデータの引き継ぎを行うための機能が用意されています。

各ユーザーアカウントには、引き継ぎ情報をリスト形式で登録することが可能です。1つの引き継ぎ情報は、以下のフォーマットで構成されています。

名前                       説明
slot 引き継ぎ情報の識別用スロット番号。1つのアカウントにつき0〜1024まで登録可能。0はパスワード引き継ぎ用、1はGoogleアカウントによる引き継ぎ用、などといった形で用途で使い分けることが想定されている。
userIdentifier 引き継ぎ用ユーザーID。ゲームのユーザーIDや、Google等のサービスのIDなどを登録。
password 引き継ぎを実行するための一時パスワードなどを登録

引き継ぎ実行の際は、あらかじめ登録したこれら3つの情報の正しい組み合わせをパラメータにして専用APIへリクエストすることで、引き継ぎ先のアカウントの認証セッション情報が取得できる仕組みになっています。

GS2-Account | Game Server Services | Docs

実装

ここからは、実際にどのように引き継ぎ機能を実装したかを紹介していきます。

今回、Unityで以下のようなUIが表示されるゲームプロジェクトを作成しました。

画面左は新規で引き継ぎ情報を登録するためのフォームで、右は登録済みの情報を入力することで引き継ぎを実行するためのフォームです。また、画面上ではログイン中のGS2ユーザーアカウントのIDが表示されるようになっています。

アカウントの引き継ぎ方法としてGoogle アカウント等の外部サービスと連携する方法もありますが、今回はシンプルな実装にするためにIDとパスワードによる引き継ぎの方式を採用しています。

なお、GS2 APIの呼び出しにはUnity用のSDKを使用しており、UniTaskを使用して非同期処理が簡潔に書けるようにしています。

ゲーム起動時の認証処理

最初に、以下のStart()が実行されるようになっています。

内容としては、PlayerPrefs(Unity標準のセーブ機能)にGS2のログインパラメータ情報が保存されているかを確認し、保存されていない場合は新規で匿名アカウントを作成し、そのIDとパスワードをPlayerPrefsへ保存した上でログイン、保存されている場合はそのパラメータを使ってログイン、という処理の流れになっています。これにより、ソーシャルゲーム等でよくある初回起動時にユーザーの見えないところで匿名アカウントを作成し、次回起動以降はそのアカウントへログインする機能を実現しています。

Gs2Login()は、匿名アカウントにログインする際に使用するメソッドで、ログイン処理の他にセッション情報の保存やユーザーID表示等のUI操作も行っています。セッション情報は、GS2の様々なマイクロサービスのAPIを利用する際に必要となるため、他コンポーネントでも共有できるようScriptableObjectに保存しています。

なお、GS2 Clientの作成には専用のstaticメソッドを外部に作成しそれを呼び出しています。

using System;
using Cysharp.Threading.Tasks;
using Gs2.Unity.Core;
using Gs2.Unity.Util;
using TMPro;
using UnityEngine;

public class Gs2AccountAuth : MonoBehaviour
{
    [SerializeField] GameObject accountMenuPanel;
    [SerializeField] UserData userData;
    [SerializeField] TMP_Text loginStatus;

    private Gs2Domain _gs2;
    private const string AccountNamespaceName = "game-0001";

    async void  Start()
    {
        _gs2 = await MyGs2Client.CreateClient();

        var gs2Account = _gs2.Account.Namespace(AccountNamespaceName);

        var gs2UserId = PlayerPrefs.GetString("Gs2UserId", "");
        var gs2Password = PlayerPrefs.GetString("Gs2Password", "");

        if (gs2UserId == "" || gs2Password == "")
        {
            // アカウントの新規作成
            var account = await (await gs2Account.CreateAsync()).ModelAsync();

            PlayerPrefs.SetString("Gs2UserId", account.UserId);
            PlayerPrefs.SetString("Gs2Password", account.Password);
            PlayerPrefs.Save();

           await Gs2Login(account.UserId, account.Password);
        }
        else
        {
            await Gs2Login(gs2UserId, gs2Password);
        }
    }

     public async UniTask Gs2Login(string userId, string password)
     {
         try
         {
             var gameSession = await _gs2.LoginAsync(
                 new Gs2AccountAuthenticator(new AccountSetting { accountNamespaceName = AccountNamespaceName }),
                 userId,
                 password
             );

             // セッション情報を他コンポーネントで共有するために保存
             userData.GameSession = gameSession;

             // UI操作
             accountMenuPanel.SetActive(true);
             loginStatus.text = $"ログイン中 - {userData.GameSession.UserId}";
         }
         catch (Exception e)
         {
             Debug.LogError(e);
         }
     }
}

引き継ぎデータの登録

画面左側の引き継ぎ登録用ボタンをクリックすると、以下のSubmit()メソッドが実行されるようになっています。

具体的な流れとしては、TakeOver(0).AddTakeOverSettingAsync()によって、フォームで入力したuserIdentifierpasswordの内容でGS2-Account内のログイン中ユーザーのスロット0に引き継ぎ情報が登録されるようリクエストを実行し、その完了後にフォームの内容リセットや処理結果テキストの表示等のUI操作を行っています。

using System;
using Cysharp.Threading.Tasks;
using Gs2.Unity.Core;
using TMPro;
using UnityEngine;
public class Gs2NewTakeOver : MonoBehaviour
{
    [SerializeField] private UserData userData;
    [SerializeField] private TMP_InputField userIdentifierInput;
    [SerializeField] private TMP_InputField passwordInput;
    [SerializeField] private TMP_Text resultText;

    private Gs2Domain _gs2;
    async void Start()
    {
        _gs2 = await MyGs2Client.CreateClient();
    }

    public async void Submit()
    {
        await _gs2.Account.Namespace(namespaceName: "game-0001")
            .Me(userData.GameSession)
            .TakeOver(0)
            .AddTakeOverSettingAsync(userIdentifierInput.text, passwordInput.text);

        // UI操作
        userIdentifierInput.text = "";
        passwordInput.text = "";

        resultText.gameObject.SetActive(true);
        resultText.text = "引き継ぎ情報の登録が完了しました。";
        await UniTask.Delay(TimeSpan.FromSeconds(3));
        resultText.gameObject.SetActive(false);
    }
}

引き継ぎの実行

画面右側の引き継ぎ実行ボタンをクリックすると、以下のSubmit()メソッドが実行されるようになっています。

具体的な処理の流れとしては、DoTakeOverAsync()によってフォームに入力したuserIdentifierpasswordの内容で引き継ぎの実行をリクエストし、正しい内容であれば引き継ぎ先アカウントの情報を使って改めてログイン処理、誤った内容であればその旨をテキストで表示する形になっています。

引き継ぎ成功時のログイン処理には、ゲーム起動時に実行されるのと同じGs2Login()を使用しており、これによって画面上のログインユーザーIDの表示も引き継ぎ先アカウントのものに書き換わるようにしています。

using System;
using Cysharp.Threading.Tasks;
using Gs2.Gs2Account.Exception;
using Gs2.Unity.Core;
using TMPro;
using UnityEngine;

public class Gs2TakeOver : MonoBehaviour
{
    [SerializeField] private TMP_InputField userIdentifierInput;
    [SerializeField] private TMP_InputField passwordInput;
    [SerializeField] private GameObject gs2AccountAuthObject;
    [SerializeField] private TMP_Text resultText;

    private Gs2Domain _gs2;
    async void Start()
    {
        _gs2 = await MyGs2Client.CreateClient();
    }

    public async void Submit()
    {
        try
        {
            var result = await _gs2.Account.Namespace(namespaceName: "game-0001")
                .DoTakeOverAsync(0, userIdentifierInput.text, passwordInput.text);
            var account = await result.ModelAsync();

            var gs2AccountAuth = gs2AccountAuthObject.GetComponent<Gs2AccountAuth>();
            await gs2AccountAuth.Gs2Login(account.UserId, account.Password);

            // UI操作
            resultText.gameObject.SetActive(true);
            resultText.text = "他アカウントへの引き継ぎが完了しました。";
            await UniTask.Delay(TimeSpan.FromSeconds(3));
            resultText.gameObject.SetActive(false);
        }
        catch (PasswordIncorrectException e)
        {
            Debug.LogError(e);

            // UI操作
            resultText.gameObject.SetActive(true);
            resultText.text = "入力内容に誤りがあります。";
            await UniTask.Delay(TimeSpan.FromSeconds(3));
            resultText.gameObject.SetActive(false);
        }
    }
}

実行結果

ここからは、上で作成した画面がどのように動作するのかを見ていきます。

ゲームを初回起動すると、自動的に作成・ログインした匿名アカウントのユーザーIDとともに引き継ぎ用の各フォームが表示されます。このアカウントの情報は保存されているので、以降何度起動しても同じユーザーIDでログインされます。

以下の画像のように新規引き継ぎ用のuserIdentifierpasswordをフォームに入力した上で、「新規引き継ぎ情報登録」ボタンをクリックします。

ゲームクライアントがGS2のAPIにリクエストを飛ばし、引き継ぎ情報の登録が完了すると、以下のようにボタンの下に処理の成功メッセージが表示されます。

この時、GS2のマネジメントコンソールでGS2-Accountに作成されたユーザーの情報を確認すると、以下のようにフォームに入力した内容の通りに引き継ぎ情報が登録されているのが確認できます。

ここでゲームをいったん終了し、PlayerPrefsに保存されているデータを消去します。その上でゲームを再び起動すると、初回起動扱いとなり新しい匿名アカウントの作成およびログインが実行されます。以下の画像では、ユーザーIDが先ほどのものと変わっていることが確認できます。

右側の引き継ぎ実行用のフォームで、先ほど登録した引き継ぎ情報を入力して「引き継ぎ実行」ボタンをクリックします。

正しいパラメータを入力していると引き継ぎが実行され、最初に作成された匿名アカウントにログインが切り替わります。以下の画像で、ユーザーIDが元のものに戻っているのが分かります。

まとめ

GS2-Accountの機能を使って、Unityでアカウントの引き継ぎ機能を実装してみました。

このように、ソーシャルゲームでよく使われる機能をGS2はあらかじめ用意しているため、少ない労力で素早く機能を作成することが可能になっています。