This document is about: QUANTUM 1
SWITCH TO

プレビルドUIの使用例

このページでは、ゲームにコアトーナメントツールを実装する方法を説明した簡単なチュートリアルをご紹介します。
トーナメントSDKUnityプラグイン内にあるプレビルドのUIスクリーンを使用しています。

デモシーンを実装する

UnityプラグインパッケージからTournamentSDK_Demoフォルダーを実装します。
このフォルダにはトーナメントメタデータの可視化に便利な、関連するUIスクリプト、プレハブ、シーンが含まれています。

import demo scene image

正常にインポートしたら、TournamentSDK_Demo/Scenes/Demo.unityにあるデモシーンを開きます。
シーンを実行します。
ゲームの クライアントID を入力してトーナメントSDKクライアントを開始します。
Fill in your ニックネーム を入力して 無名のプロバイダ を使用してログインします。
(無名のログインプロバイダがダッシュボードで 有効 になっていることを確認してください。)
この段階で、最近のトーナメントをブラウズ氏、連動することができます。

マニュアルクライアントの初期化

この例では、ユーザーがニックネームを選択し、Photonが正常にサーバーに接続したの後でのみトーナメントSDKを初期化します。
シーンオブジェクトにBackboneManagerスクリプトを追加します。
開始時に初期化する ボックスの チェックを外 します。

Add backbone manager image
Set backbone manager image

また、同じシーンオブジェクトにResourceCacheスクリプトを追加します。

Add resource cache image

Create new script called BackboneIntegration.

Add backbone integration image

スクリプトを開いてクライアント初期化フローを実装します。

C#

using Gimmebreak.Backbone.User;
using System.Collections;
using UnityEngine;

public class BackboneIntegration : MonoBehaviour {

    private WaitForSeconds waitOneSecond = new WaitForSeconds(1);

    private IEnumerator Start()
    {
        // プレイヤーのニックネームが設定されるまで待ちます。 (最初の画面でおこないます)
        while (string.IsNullOrEmpty(PhotonNetwork.player.NickName))
        {
            yield return this.waitOneSecond;
        }
        // クライアントの初期化を試み続けます
        while (!BackboneManager.IsInitialized)
        {
            yield return BackboneManager.Initialize();
            yield return this.waitOneSecond;
        }
        // ニックネームに基づく任意のユーザーID(最小64文字)を作成します。
        string arbitraryId = "1000000000000000000000000000000000000000000000000000000000000001" + PhotonNetwork.player.NickName;
        // IDが一致しない場合。ユーザーをログアウトします。
        if (BackboneManager.IsUserLoggedIn &&
            BackboneManager.Client.User.GetLoginId(LoginProvider.Platform.Anonym) != arbitraryId)
        {
            Debug.LogFormat("Backbone user({0}) logged out.", BackboneManager.Client.User.UserId);
            yield return BackboneManager.Client.Logout();
        }
        // ログインユーザー
        if (!BackboneManager.IsUserLoggedIn)
        {
            yield return BackboneManager.Client.Login(LoginProvider.Anonym(true, PhotonNetwork.player.NickName, arbitraryId));
            if (BackboneManager.IsUserLoggedIn)
            {
                Debug.LogFormat("Backbone user({0}) logged in.", BackboneManager.Client.User.UserId);
            }
            else
            {
                Debug.LogFormat("Backbone user failed to log in.");
            }
        }
    }
}

次のトーナメントを表示する

ユーザーが正常にログインしてから、トーナメントリストの更新を行います。

C#

// トーナメントリストを更新します
BackboneManager.Client.LoadTournamentList()
    // 終了コールバックを追加します
    .FinishCallback(() => { /* List is loaded */ })
    // 'this' MonoBehaviourを実行します。
    .Run(this);

BackboneManager.Client.Tournaments.UpcomingTournamentプロパティを使用してユーザーに対し次のトーナメントを取得します。

Upcoming tournament image

C#

var tournament = BackboneManager.Client.Tournaments.UpcomingTournament;
this.tournamentName.text = tournament.TournamentName;
this.tournamentDate.text = tournament.Time.ToLocalTime().ToString("'<b>'dd. MMM'</b>' HH:mm");
this.tournamentTicket.text = string.Format("{0}/{1} | {2}",
                                           tournament.CurrentInvites,
                                           tournament.MaxInvites,
                                           GetSignupStatus());

トーナメントハブスクリーンをコピーする

この例ではトーナメントSDKで提供されている デフォルトのUIスクリーン を使用します。
実装すべき一番重要なスクリーンはTournamentListScreenTournamentHubScreenです。

UIヒエラルキーにTournamentHubScreenをコピーします。
このスクリーンはユーザーにすべてのトーナメントの詳細を表示し、トーナメンプレイハブとして動作します。

Import tournament hub image

TournamentHubScreenを初期化するには、UIオブジェクトを有効にする前に正しい トーナメントID を設定する必要があります。
TournamentHubScreen内のGUITournamentHubScreenへのリファレンスを作成し Initialize(long tournamentId)を呼び出します。

C#

// これはUIパネルを制御し、表示するUIコンテナスクリプト内です。 (トーナメントSDKの一部ではありません)

// インポートしたトーナメントハブスクリーンスクリプトへのリファレンス
[SerializeField]
private GUITournamentHubScreen tournamentHubScreen;
//...
// 特定のトーナメントID用のトーナメントハブを表示します
public static void ShowScreen(long tournamentId)
{
    // トーナメントが存在するか確認します
    var tournament = BackboneManager.Client.Tournaments.GetTournamentById(tournamentId);
    if (tournament != null)
    {
        initializedTournamentId = tournamentId;
        // 正しいトーナメントIDのトーナメントハブスクリーンを初期化します
        Instance.tournamentHubScreen.Initialize(tournamentId);
        // UIオブジェクトを有効化します
        ShowScreen();
    }
}

前のステップで作成した次のトーナメントウィジェットから、メソッドを呼び出せるようになりました。

C#

// UIパネルを制御し幼児するUIコンテナスクリプトの中です(トーナメントSDKの一部ではありません)


// 次のトーナメントのためにトーナメントハブを開きます
public void OpenTournamentHub()
{
    if (BackboneManager.IsUserLoggedIn)
    {
        // 次のトーナメントが存在するか確認します
        var upcomingTournament = BackboneManager.Client.Tournaments.UpcomingTournament;
        if (upcomingTournament != null)
        {
            // メイン画面を隠します
            LobbyMain.HideScreen();
            // トーナメントハブを表示します
            // 注意: UITournamentHubはトーナメントSDKの一部ではなくラッパーです。
            // TournamentHubScreenまわり
            UITournamentHub.ShowScreen(upcomingTournament.Id);
            LobbyAudio.Instance.OnClick();
        }
    }
}

注意: TournamentListScreenが表示するのが次のトーナメントのみであるため、簡略化のためこの例では使用していません。

トーナメントマッチハンドラーを実装する

トーナメントハブとゲームルーム・ロビー作成API間のインターフェースを作成しましょう。
TournamentMatchHandlerという新しいスクリプトを作成します。
スクリプトを開いてTournamentMatchCallbackHandlerから派生させます。
簡潔な一連のメソッドを提供してロビー・ルームのステートをトーナメントハブに伝達します。

C#

public bool IsConnectedToGameServerNetwork()
{
    //クライアントが正常にネットワークバックエンドに接続したか確認します
    //ユーザーが接続済で既定のマッチに参加する準備ができていればtrueを返します。
}

public bool IsUserConnectedToMatch(long userId)
{
    //特定のユーザーがすでにロビー・ルームに接続しているかどうか確認します。
    //ユーザーが接続していればtrueを返します。
}

public void OnJoinTournamentMatch(Tournament tournament, TournamentMatch match, ITournamentMatchController controller)
{
    //トーナメント、マッチ、コントローラオブジェクトをパスするトーナメントハブからのコールバック
    //正しいロビー・ルームに参加するにはマッチデータを使用します。
    //トーナメントハブにロビー・ルームでの変更を通知するにはコントローラを使用します。
}

public bool IsUserReadyForMatch(long userId)
{
    //特定のユーザーが準備でいているかどうか確認します(例:正しいスロットに移動しました)
    //ユーザーの開始準備ができていればtrueを返します。
}

public bool IsGameSessionInProgress()
{
    //既定のトーナメントマッチのゲームセッションが既に進行中かどうか確認します。
    //ゲームセッションが進行中であればtrueを返します。
}

public void OnLeaveTournamentMatch()
{
    //ユーザーにロビー・ルームを退出するように通知するトーナメントハブからのコールバック
}

public void StartGameSession(IEnumerable<TournamentMatch.User> checkedInUsers)
{
    //ゲームセッションの即時開始をリクエストするトーナメントハブからのコールバック。また、
    //現在のマッチに正常にチェックインしたユーザーをパスします。
    //トーナメントゲームセッションを作成し、ゲームを開始します。
    //IsGameSessionInProgressがtrueを返すまで複数回呼び出される可能性があります。
}

上記のメソッドは以下の順番で実行します:

  1. OnJoinTournamentMatch()
  2. IsConnectedToGameServerNetwork()
  3. IsUserConnectedToMatch()
  4. IsGameSessionInProgress()
  5. IsUserReadyForMatch()
  6. StartGameSession()

OnJoinTournamentMatch

トーナメント、マッチ、コントローラオブジェクトをパスするトーナメントハブからのコールバック
マッチデータを使用して正しいロビー・ルームに参加します。
コントローラを使用してトーナメントハブにロビー・ルームでの変更を通知します。

C#

public override void OnJoinTournamentMatch(Tournament tournament, TournamentMatch match, ITournamentMatchController controller)
{
    // ユーザーはトーナメントマッチへの参加、適切なルームの作成と参加をリクエストされます。
    // match.SecretをルームのIDとして使用できます
    this.tournament = tournament;
    this.tournamentMatch = match;
    this.tournamentMatchController = controller;
    this.sessionStarted = false;
    this.creatingSession = false;
    // ユーザーIDとチームIDをQuantumプレイヤーに転送します
    PlayerData.Instance.BackboneUserId = BackboneManager.Client.User.UserId;
    PlayerData.Instance.BackboneTeamId = match.GetMatchUserById(BackboneManager.Client.User.UserId).TeamId;
    // Photonルームに参加します
    StartCoroutine(JoinRoomRoutine());
}

private IEnumerator JoinRoomRoutine()
{
    while (this.tournamentMatch != null)
    {
        // トーナメントに特定のリージョンを必要とする場合、必要なリージョンの
        // 情報を提供するトーナメントカスタムプロパティを使用できます。
        // string cloudRegion = this.tournament.CustomProperties.Properties["cloud-region"];
        // ...
        // PhotonNetwork.ConnectToRegion(region, gameVersion);
        // ...
        // 適切なリージョンに接続するまで待ちます
        // ...
        // ルームへの接続を続行します。

        // トーナメントマッチが終了したら退出します。
        if (this.tournamentMatch.Status == TournamentMatchStatus.MatchFinished ||
            this.tournamentMatch.Status == TournamentMatchStatus.Closed)
        {
            // 接続したルームが終了したマッチ用のものであるか確認します
            if (PhotonNetwork.inRoom &&
                PhotonNetwork.room.Name == this.tournamentMatch.Secret)
            {
                PhotonNetwork.LeaveRoom(false);
            }
        }
        // トーナメントマッチルームに接続を試みます
        else if (PhotonNetwork.connectedAndReady &&
                 !PhotonNetwork.inRoom &&
                 !this.connectingToRoom)
        {
            this.connectingToRoom = true;
            // プレイヤープロパティをユーザーIDで設定して、ルーム内でユーザーを認識できるようにします。
            SetPlayerProperty("BBUID", BackboneManager.Client.User.UserId);
            RoomOptions roomOptions = new RoomOptions();
            roomOptions.IsVisible = false;
            // トーナメントのフェーズ設定に基づいてルームの最大プレイヤー数を設定します
            roomOptions.MaxPlayers = (byte)(this.tournament.GetTournamentPhaseById(this.tournamentMatch.PhaseId).MaxTeamsPerMatch * this.tournament.PartySize);
            // トーナメントマッチシークレットをルームのIDとしてPhotonルームに参加するか、作成します。
            PhotonNetwork.JoinOrCreateRoom(this.tournamentMatch.Secret, roomOptions, TypedLobby.Default);
        }
        // 違うルームにいる場合は、退出します。
        else if (PhotonNetwork.inRoom &&
                 PhotonNetwork.room.Name != this.tournamentMatch.Secret)
        {
            PhotonNetwork.LeaveRoom(false);
        }

        yield return this.waitOneSec;
    }
}

このメソッドはTournamentMatchHandlerを初期化し、適切なロビー・ルームへの接続を開始します。
これが呼び出されるのは、ユーザーが次のマッチへの準備でいていると確認を行ったあとのみです。

IsConnectedToGameServerNetwork

クライアントが正常にネットワークバックエンドに接続したか確認するトーナメントハブからのコールバック
ユーザーが接続しており、与えられたマッチに参加する準備ができていればtrueを返します。

C#

public override bool IsConnectedToGameServerNetwork()
{
    // ユーザーがPhotonに接続し、ルームに参加する準備が整ったか確認します。
     return PhotonNetwork.connectedAndReady;
}

IsUserConnectedToMatch

特定のユーザーがすでにロビー・ルーム接続したかどうか確認するトーナメントハブからのコールバック。
ユーザーが接続していればtrue。
プレイヤープロパティが、Photonルームに参加する前に ユーザーID で設定されていることにご注意ください。
このプレイヤープロパティは接続してたPhotonプレイヤーを認識するのに使用します。

C#

public override bool IsUserConnectedToMatch(long userId)
{
    // トーナメントマッチユーザーがルームに接続したか確認します。
    // ユーザーがルームに参加する前にPhotonプレイヤープロパティBBUIDがユーザーIDで設定されています。
    var photonPlayer = GetPhotonPlayerByBackboneUserId(userId);
    return photonPlayer != null;
}

private PhotonPlayer GetPhotonPlayerByBackboneUserId(long userId)
{
    if (PhotonNetwork.inRoom)
    {
        for (int i = 0; i < PhotonNetwork.playerList.Length; i++)
        {
            long playerUserId;
            if (TryGetPlayerProperty(PhotonNetwork.playerList[i], "BBUID", out playerUserId) &&
                userId == playerUserId)
            {
                return PhotonNetwork.playerList[i];
            }
        }
    }
    return null;
}

接続済のトーナメントマッチにいるべきすべてのユーザーに対してメソッドが呼び出されます。

IsGameSessionInProgress

既定のトーナメントマッチでゲームセッションが既に進行しているか確認するトーナメントハブからのコールバック。
ゲームセッションが進行していればtrue。

C#

public override bool IsGameSessionInProgress()
{
    // トーナメントマッチセッションが開始したかどうか判定します。
    return sessionStarted;
}

この例ではBackboneManager.Client.CreateGameSession()への正常な呼び出しを行った後、sessionStartedをtrueに設定しています。

IsUserReadyForMatch

特定のユーザーが準備完了しているか確認するトーナメントハブからのコールバック(例:ただしいスロットに移動)。
ユーザーの開始準備が整っていればtrueを返します。
マッチにまだチェックインしていないローカルユーザーは、trueを返した後にのみチェックインされます。

C#

public override bool IsUserReadyForMatch(long userId)
{
    // マッチ開始に必要なすべてをユーザーが読み込み・設定したら
    // trueを返します(ユーザーインプットが必要な場合は期限付きとなります)。
    return true;
}

この例では、ルームへの接続後ユーザーに設定する必要のあるものはありません。ですので、デフォルトでtrueを返します。

StartGameSession

ゲームセッションの即時開始をリクエストするトーナメントハブからのコールバック
現在のマッチに正常にチェックインしたユーザーをパスします。
トーナメントゲームセッションを作成しゲームを開始します。
これはIsGameSessionInProgressがtrueを返すまで数回呼び出される可能性があります。

C#

public override void StartGameSession(IEnumerable<TournamentMatch.User> checkedInUsers)
{
    // チェックイン済のユーザーでトーナメントゲームセッションを開始します。
    // このコールバックはIsGameSessionInProgressがtrueを返すまで
     // 数回呼び出される可能性があることに注意してください。

    // セッションが開始したか確認します
    if (sessionStarted)
    {
        return;
    }

    // Photonがまだルームに接続していて準備ができているあk確認します
        if (!PhotonNetwork.connectedAndReady ||
        !PhotonNetwork.inRoom)
    {
        return;
    }

    // セッションがリクエストされていないかどうか確認します
    if (!this.creatingSession)
    {
        this.creatingSession = true;
        // トーナメントゲームセッションを作成します
        BackboneManager.Client.CreateGameSession(
            checkedInUsers,
            this.tournamentMatch.Id,
            0)
            .ResultCallback((gameSession) =>
                {
                    this.creatingSession = false;
                    // ゲームセッションが作成されたかどうか確認します
                    if (gameSession != null)
                    {
                        // セッションが開始したことを表します
                        this.sessionStarted = true;
                        // ルームプロパティを設定します
                        var ht = new ExitGames.Client.Photon.Hashtable();
                        ht.Add("SESSIONID", gameSession.Id);
                        ht.Add("TOURNAMENTID", this.tournament.Id);
                        ht.Add("TOURNAMENTMATCHID", this.tournamentMatch.Id);
                        PhotonNetwork.room.SetCustomProperties(ht);
                        // この段階でシーンの読み込みを始め
                        // ゲームセッションを開始できます。
                    }
                })
            .Run(this);
    }
}

トーナメントマッチハンドラーをアタッチする

シーンオブジェクトに作成したTournamentMatchHandlerを追加します。 (例: TournamentHubScreenのとなり)
TournamentMatchHandlerのオブジェクトリファレンスを MatchHandler フィールドのGUITournamentActiveMatchスクリプトに追加します。
このスクリプトはTournamentHubScreen/Canvas/ActiveMatchContainerオブジェクトにあります。

Add match handler image

結果サブミッションを実装する

ゲームセッションが終了したら最終順位を設定する必要があります。
ゲームセッションの開始前に新しいゲームセッションオブジェクトを作成するかBackboneManager.Client.CreateGameSession()で取得したものを使用します。
カスタムステータスをゲームセッション結果と一緒に送信します。

C#

List<GameSession.User> users = new List<GameSession.User>();
Dictionary<long, int> kills = new Dictionary<long, int>();
Dictionary<long, int> deaths = new Dictionary<long, int>();
// ゲームプレイヤー(ロボット)を通してイテレートし、順位とステートを集めます
for (int i = 0; i < sortedRobots.Count; i++)
{
    // Quantumランタイムプレイヤーを取得します
    var runtimePlayer = QuantumGame.Instance.Frames.Current.GetPlayerData(((Robot*)sortedRobots[i])->Player);
    if (runtimePlayer != null)
    {
        // プレイヤーのBackboneUserIdとマッチのチームIDを取得します
        long userId = (long)runtimePlayer.BackboneUserId;
        byte teamId = runtimePlayer.BackboneTeamId;
        // 新しいゲームセッションユーザーを作成し最終順位を割り当てます。
        // (1-X、一人が優勝)
        users.Add(new Gimmebreak.Backbone.GameSessions.GameSession.User(userId, teamId) { Place = (i + 1) });
        // プレイヤーのキルステートを取得します
        kills.Add(userId, ((Robot*)sortedRobots[i])->Score.Kills);
        // プレイヤーのデスステートを取得します
        deaths.Add(userId, ((Robot*)sortedRobots[i])->Score.Deaths);
    }
}
// 送信するゲームセッション王ジェクトを作成します。
Gimmebreak.Backbone.GameSessions.GameSession gameSession = new Gimmebreak.Backbone.GameSessions.GameSession(gameSessionId, 0, users, tournamentMatchId);
// プレイ日付とセッション時間を作成します。
gameSession.PlayDate = ServerTime.UtcNow;
gameSession.PlayTime = gameTime;
// ゲームセッションステートを追加します
gameSession.Users.ForEach(user =>
                          {
                              gameSession.AddStat(1, user.UserId, kills[user.UserId]);
                              gameSession.AddStat(2, user.UserId, deaths[user.UserId]);
                          });

ゲームセッションオブジェクトを入力したらBackboneManager.Client.SubmitGameSession(gameSession);で送信します。

C#

 private IEnumerator ProcessResult(long tournamentId, GameSession finishedGameSession)
 {       
     var tournament = BackboneManager.Client.Tournaments.GetTournamentById(tournamentId);
     var tournamentMatch = tournament.UserActiveMatch;
     //report game session
     yield return BackboneManager.Client.SubmitGameSession(finishedGameSession);
     //refresh tournament data
     yield return BackboneManager.Client.LoadTournament(tournamentId);
     //check if tournament match was not finished (if not another game should be played)
     bool initializeNextGame = tournamentMatch != null &&
         tournamentMatch.Status != TournamentMatchStatus.MatchFinished &&
         tournamentMatch.Status != TournamentMatchStatus.Closed;
 }

結果送信の後トーナメントデータを再読み込みしてUserActiveMatchがクローズしているか終了しているか確認します。
ユーザーアクティブマッチが終了していない場合は、他のゲームセッションが続きます。(例:三番勝負)
新しいゲームセッションをBackboneManager.Client.CreateGameSession() で作成し、サイクルを繰り返します。

トーナメントコアループを仕上げる

最後の結果送信の後でUserActiveMatchが終了している、またはクローズしている場合、ユーザーはUserActiveMatchに戻ります。
There he can see current stats and progress chage after finished match.
最新のステータスを確認して変更を
ユーザーは明示的に「次のマッチへの準備完了」承認のアクションを経ず即座に他のマッチに移動されるわけではありません。
ユーザーが次のマッチへ準備完了の承認をおこなうと、システムが別のUserActiveMatchを割り当てトーナメントが狩猟するまでサイクルを繰り返します。

Back to top