もうゲームのバックエンド開発は必要ない!?AccelByte Gaming Servicesのチュートリアルを触ってみた #2 ~ログイン編~

2023.10.30

ゲームソリューション部の えがわ です。
AccelByte Gaming Services (AGS)のデモが使用できるようになったのでチュートリアルを触ってみました。

前回まで

前回までは初期設定としてオフラインでのゲームプレイ、SDKのインストールを行いました。

今回

今回はModule 2: Unity - Getting Onlineまで進めます。

AccelByte Gaming Services を最大限に活用するには、まずプレイヤーを認証する必要があります。このモジュールでは、他のオンライン機能の実装に移る前に、可能な限りシンプルに認証を実施する方法について説明します。

こちらのセクションではデバイスの認証を行い、プレイヤーをオンライン状態にします。

事前準備

tutorialmodulesブランチへ切り替えます。

$ git checkout tutorialmodules

また、他のセクションとの依存関係のため以下のjsonファイルを更新します。
・Assets/Resources/Modules/TutorialModuleConfig.json

{
    "enableModulesOverride": true,
    "forceEnabledModules": ["AuthEssentials"],
    "forceDisabledOtherModules" : true
}

ログイン実装

Assets/Resources/Modules/AuthEssentials/Scripts/UI/LoginHandler_Starter.cs
Assets/Resources/Modules/AuthEssentials/Prefabs/LoginMenuCanvas_Starter.prefab
上記のファイルが存在しない場合はブランチの切り替えに失敗している可能性があります。

まずはLoginHandler_Starter.csを確認していきましょう。
現段階ではログイン状態の表示切替のみ実装されています。

Assets/Resources/Modules/AuthEssentials/Scripts/UI/LoginHandler_Starter.cs

[SerializeField] private GameObject loginStatePanel;
[SerializeField] private GameObject loginLoadingPanel;
[SerializeField] private GameObject loginFailedPanel;

public enum LoginView
{
    LoginState,
    LoginLoading,
    LoginFailed
}

private LoginView CurrentView
{
    get => CurrentView;
    set
    {
        switch (value)
        {
            case LoginView.LoginState:
                loginStatePanel.SetActive(true);
                loginLoadingPanel.SetActive(false);
                loginFailedPanel.SetActive(false);
                break;
            case LoginView.LoginLoading:
                loginStatePanel.SetActive(false);
                loginLoadingPanel.SetActive(true);
                loginFailedPanel.SetActive(false);
                break;
        
            case LoginView.LoginFailed:
                loginStatePanel.SetActive(false);
                loginLoadingPanel.SetActive(false);
                loginFailedPanel.SetActive(true);
                break;
        }
    }
}

※2023/10/23 CurrentViewのセッターは英語版のチュートリアルのみ以下に変更になっています。
冗長性が解消されており、動作も変わらないので置き換えてしまいましょう。

private LoginView CurrentView
{
    get => CurrentView;
    set
    {
        loginStatePanel.SetActive(value == LoginView.LoginState);
        loginLoadingPanel.SetActive(value == LoginView.LoginLoading);
        loginFailedPanel.SetActive(value == LoginView.LoginFailed);
    }
}

CurrentViewに列挙型の定数を設定するだけで表示を切り替えられるようになっています。

それではチュートリアルの指示に従ってログイン機能を実装していきます。
LoginHandler_Starter.csにはソース内には「ここにペーストしてね!」といった指示もあるので迷わずに実装していけます。

ローディング演出の表示

まずLogin関数を実装します。
CurrentViewのセッターによって、loginLoadingPanelのみがActiveとなるため、ローディング演出を表示できます。

private void Login()
{
    CurrentView = LoginView.LoginLoading;
}

OnEnableはオブジェクトがアクティブになった際に実行される関数です。
ここではボタンが押下されたときのイベントを付与し、ログイン画面の表示を初期状態に設定しています。

private void OnEnable()
{
    // UI initialization
    quitGameButton.onClick.AddListener(OnQuitGameButtonClicked);
    loginWithDeviceIdButton.onClick.AddListener(OnLoginWithDeviceIdButtonClicked);
    retryLoginButton.onClick.AddListener(OnRetryLoginButtonClicked);

    CurrentView = LoginView.LoginState;
}

ボタンに付与する関数を実装します。

private void OnLoginWithDeviceIdButtonClicked()
{
    Login();
}

private void OnRetryLoginButtonClicked()
{
    Login();
}

ここまで実装できたらAuthEssentialsAssetConfigを変更します。

設定箇所 ファイル
Default Menu UI Prefab LoginMenuCanvas_Starter
Default Module Script AuthEssentialsWrapper_Starter

ここで一度再生してみましょう。

ローディング演出の表示まで行えました。

ログイン機能の実装

AccelByte は多くのログイン方法をサポートしているため、どのログイン方法をアクティブにするかを選択する必要があります。AccelByte ゲーム サービスを開始するための最も簡単な認証方法の 1 つは、デバイス ID ログインです。これは、デバイスを識別するための一意の文字列です。 デバイス ID ログインを使用すると、プレーヤーは登録データや資格情報データを入力せずにゲームにログインできます。プレーヤーが初めてログインすると、AccelByte Gaming Services によって自動的にプレーヤー アカウントが作成されます。これにより、マッチング、友達管理、ストアなどのさまざまなサービスにアクセスできるようになります。

今回はデバイスIDでのログインを追加します。

※ここの操作は管理者ポータルから行います

User Management -> Login Methods -> Add New -> Device
Refirect URIに http://127.0.0.1 を設定して作成しましょう。

作成後はActivateするのを忘れずに!

SDKを使用してログインを実行

※ここからの操作はUnityに戻ります

ゲームとSDKをやり取りさせるためのWrapperを実装していきます。

Assets/Resources/Modules/AuthEssentials/Scripts/AuthEssentialsWrapper_Starter.cs

using System.Collections;
using System.Collections.Generic;
using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
using UnityEngine;

public class AuthEssentialsWrapper_Starter : MonoBehaviour
{
    private ApiClient apiClient;
    private User user;

    private PlatformType _platformType;
    private string _platformToken;

    void Start()
    {
        // MultiRegistryを通じてAccelByte SDKの機能を取得し、さらにGetApiでユーザーに関連する機能も取得
        apiClient = MultiRegistry.GetApiClient();
        user = apiClient.GetApi<User, UserApi>();
    }

    public void Login(LoginType loginMethod, ResultCallback<TokenData, OAuthError> resultCallback)
    {
        switch (loginMethod)
        {
            case LoginType.DeviceId:
                _platformType = PlatformType.Device;
                _platformToken = SystemInfo.deviceUniqueIdentifier;
                break;
        }

        Debug.Log("[AuthEssentialsWrapper] Trying to login with device id: " + _platformToken);

        // SDKにログイン要求
        user.LoginWithOtherPlatform(_platformType, _platformToken, result => OnLoginCompleted(result, resultCallback));
    }

    private void OnLoginCompleted(Result<TokenData, OAuthError> result, ResultCallback<TokenData, OAuthError> customCallback = null)
    {
        if (!result.IsError)
        {
            Debug.Log("Login user successful.");
        }
        else
        {
            Debug.Log($"Login user failed. Message: {result.Error.error}");
        }

        // ログイン処理が終了した場合のコールバック実行
        customCallback?.Invoke(result);
    }
}

Wrapperの実装は完了したので、呼び出していきます。 本寄稿の最初に修正したLoginHandler_Starterを更新します。

Assets/Resources/Modules/AuthEssentials/Scripts/UI/LoginHandler_Starter.cs

using System.Collections;
using System.Collections.Generic;
using AccelByte.Core;
using AccelByte.Models;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class LoginHandler_Starter : MenuCanvas
{
    //Declare each view panels
    [SerializeField] private GameObject loginStatePanel;
    [SerializeField] private GameObject loginLoadingPanel;
    [SerializeField] private GameObject loginFailedPanel;
    
    // Declare UI button here
    [SerializeField] private Button loginWithDeviceIdButton;
    [SerializeField] private Button retryLoginButton;
    [SerializeField] private Button quitGameButton;
    [SerializeField] private TMP_Text failedMessageText;

    private AuthEssentialsWrapper_Starter _authWrapper;
    private LoginType _lastLoginMethod;


    #region LoginView enum
    public enum LoginView
    {
        LoginState,
        LoginLoading,
        LoginFailed
    }

    private LoginView CurrentView
    {
        get => CurrentView;
        set
        {
            loginStatePanel.SetActive(value == LoginView.LoginState);
            loginLoadingPanel.SetActive(value == LoginView.LoginLoading);
            loginFailedPanel.SetActive(value == LoginView.LoginFailed);
        }
    }

    #endregion

    private void Start()
    {
        _authWrapper = TutorialModuleManager.Instance.GetModuleClass<AuthEssentialsWrapper_Starter>();
    }

    private void OnEnable()
    {
        // UI initialization
        quitGameButton.onClick.AddListener(OnQuitGameButtonClicked);
        loginWithDeviceIdButton.onClick.AddListener(OnLoginWithDeviceIdButtonClicked);
        retryLoginButton.onClick.AddListener(OnRetryLoginButtonClicked);

        CurrentView = LoginView.LoginState;
    }

    private void Login(LoginType loginMethod)
    {
        // 2.ローディング演出表示
        CurrentView = LoginView.LoginLoading;

        // 3.Wrapperのログイン処理を実行、終了後はOnLoginCompletedを呼び出す
        _lastLoginMethod = loginMethod;
        _authWrapper.Login(loginMethod, OnLoginCompleted);
    }

    private void OnLoginWithDeviceIdButtonClicked()
    {
        // 1.ログイン処理開始
        Login(LoginType.DeviceId);
    }

    private void OnRetryLoginButtonClicked()
    {
        Login(_lastLoginMethod);
    }

    private void OnQuitGameButtonClicked()
    {
        Application.Quit();
    }

    private void OnLoginCompleted(Result<TokenData, OAuthError> result)
    {
        // 4.コールバック実行
        if (!result.IsError)
        {
            // 4.1.メニュー画面へ遷移
            MenuManager.Instance.ChangeToMenu(AssetEnum.MainMenuCanvas);
        }
        else
        {
            // 4.2.ログイン失敗画面へ遷移
            failedMessageText.text = "Login Failed: " + result.Error.error;
            CurrentView = LoginView.LoginFailed;
        }
    }

    public override GameObject GetFirstButton()
    {
        return loginWithDeviceIdButton.gameObject;
    }

    public override AssetEnum GetAssetEnum()
    {
        return AssetEnum.LoginMenuCanvas_Starter;
    }
}

ではエディターで再生してみましょう!

無事にログインが完了しました!(レイアウト崩れちゃってますが...)
ダッシュボードでアナリティクスも確認できます。

次回からはオンラインゲームの肝、マッチングの実装を行っていきます!