SDK を使用して統計データを設定しクエリを実行する - 統計データを追跡し表示する - (Unity モジュール)
注釈:本資料はAI技術を用いて翻訳されています。
ラッパーを開く
このチュートリアルでは、AccelByte Gaming Services (AGS) Software Development Kit (SDK) を使用して AGS 統計データを実装する方法を学びます。Byte Wars には、StatsEssentialsWrapper クラスで定義されたゲームインスタンスラッパーがあります。このラッパーは、AGS 統計データ値を操作するためのラッパーとして機能します。
このチュートリアルでは、StatsEssentialsWrapper クラスのスターターバージョンである StatsEssentialsWrapper_Starter ラッパーを使用して統計データ値を操作します。
スターターパックの内容
このチュートリアルを進めるには、StatsEssentialsWrapper_Starter というスターターラッパークラスを使用します。このラッパーは以下のファイルで定義されています:
- C# ファイル:
Assets/Resources/Modules/Storage/StatsEssentials/Scripts/StatsEssentialsWrapper_Starter.cs
また、以下のファイルにヘルパー関数と変数を含むモデルクラスも定義されています:
- C# ファイル:
Assets/Resources/Modules/Storage/StatsEssentials/Scripts/StatsEssentialsModels.cs
StatsEssentialsWrapper_Starter クラスには、以下のいくつかの事前定義されたコンポーネントがあります。
-
AGS SDK インターフェースを参照するためのヘルパー変数。これらの変数は、ラッパーが初期化されるときに割り当てられます。
private Statistic statistic;
private ServerStatistic serverStatistic;
void Start()
{
statistic = AccelByteSDK.GetClientRegistry().GetApi().GetStatistic();
#if UNITY_SERVER
serverStatistic = AccelByteSDK.GetServerRegistry().GetApi().GetStatistic();
#endif
}
StatsEssentialsModels クラスには、以下のヘルパーがあります。
-
Byte Wars でサポートされている各ゲームモードの統計データコードを返すヘルパークラス。これにより、統計データ値を更新する際にハードコードする必要がなくなり、アクセスが簡素化されます。これらの統計データコードは、前のセクションで Admin Portal で設定したものと同じです。
public class GameStatsData
{
public struct GameStatsModel
{
public string StatCode;
public string DisplayName;
public GameStatsModel(string codeName, string displayName)
{
StatCode = codeName;
DisplayName = displayName;
}
}
public InGameMode GameMode { get; private set; }
public GameStatsModel HighestScoreStats { get; private set; }
public GameStatsModel TotalScoreStats { get; private set; }
public GameStatsModel MatchesPlayedStats { get; private set; }
public GameStatsModel MatchesWonStats { get; private set; }
public GameStatsModel KillCountStats { get; private set; }
public GameStatsModel DeathStats { get; private set; }
public ReadOnlyCollection<string> StatsCodes { get; }
public ReadOnlyCollection<GameStatsModel> StatsModels { get; }
public GameStatsData(InGameMode gameMode)
{
GameMode = gameMode;
string gameModeSuffx = "unknown";
switch(GameMode)
{
case InGameMode.SinglePlayer:
case InGameMode.LocalElimination:
case InGameMode.LocalTeamDeathmatch:
gameModeSuffx = "singleplayer";
break;
case InGameMode.MatchmakingElimination:
case InGameMode.CreateMatchElimination:
gameModeSuffx = "elimination";
break;
case InGameMode.MatchmakingTeamDeathmatch:
case InGameMode.CreateMatchTeamDeathmatch:
gameModeSuffx = "teamdeathmatch";
break;
}
HighestScoreStats = new($"unity-highestscore-{gameModeSuffx}", "Highest Score");
TotalScoreStats = new($"unity-totalscore-{gameModeSuffx}", "Total Score");
MatchesPlayedStats = new($"unity-matchesplayed-{gameModeSuffx}", "Matches Played");
MatchesWonStats = new($"unity-matcheswon-{gameModeSuffx}", "Matches Won");
KillCountStats = new($"unity-killcount-{gameModeSuffx}", "Kill Count");
DeathStats = new($"unity-deaths-{gameModeSuffx}", "Deaths");
StatsModels = new ReadOnlyCollection<GameStatsModel>(new[]
{
HighestScoreStats,
TotalScoreStats,
MatchesPlayedStats,
MatchesWonStats,
KillCountStats,
DeathStats
});
StatsCodes = new ReadOnlyCollection<string>(StatsModels.Select(model => model.StatCode).ToList());
}
} -
各ゲームモードの統計データコードを格納するヘルパー変数。
public readonly static GameStatsData SinglePlayerStatsData = new(InGameMode.SinglePlayer);
public readonly static GameStatsData EliminationStatsData = new(InGameMode.CreateMatchElimination);
public readonly static GameStatsData TeamDeathmatchStatsData = new(InGameMode.CreateMatchTeamDeathmatch); -
ゲームモードに基づいて統計データコードを返すヘルパー関数。
public static GameStatsData GetGameStatsDataByGameMode(InGameMode gameMode)
{
switch (gameMode)
{
case InGameMode.SinglePlayer:
case InGameMode.LocalElimination:
case InGameMode.LocalTeamDeathmatch:
return SinglePlayerStatsData;
case InGameMode.MatchmakingElimination:
case InGameMode.CreateMatchElimination:
return EliminationStatsData;
case InGameMode.MatchmakingTeamDeathmatch:
case InGameMode.CreateMatchTeamDeathmatch:
return TeamDeathmatchStatsData;
default:
return null;
}
}
ゲームクライアント用の統計データを実装する
このセクションでは、AGS SDK を使用してゲームクライアント用の統計データ機能を実装する方法を説明します。
-
StatsEssentialsWrapper_Starterクラスを開きます。まず、後で UI に表示するためのユーザー統計データを取得する以下の新しい関数を作成します。この関数は統計データコードのリストを必要とし、リクエストが完了するとコールバック関数を呼び出します。public void GetUserStatsFromClient(string[] statCodes, string[] tags, ResultCallback<PagedStatItems> resultCallback)
{
statistic.GetUserStatItems(
statCodes,
tags,
result => OnGetUserStatsFromClientCompleted(result, resultCallback)
);
} -
次に、上記の関数のコールバック関数を作成します。ここでは、統計データ値の取得に成功したかどうかのリクエストステータスを単純にログに記録します。
private void OnGetUserStatsFromClientCompleted(Result<PagedStatItems> result, ResultCallback<PagedStatItems> customCallback = null)
{
if (!result.IsError)
{
BytewarsLogger.Log("Get User's Stat Items from Client successful.");
}
else
{
BytewarsLogger.LogWarning($"Get User's Stat Items from Client failed. Message: {result.Error.Message}");
}
customCallback?.Invoke(result);
} -
次に、統計データ値を更新するための以下の新しい関数を作成します。これは、Admin Portal で以前に設定したシングルプレイヤーモードの最高スコアなど、ゲームクライアントによって更新されることを意図した統計データ値を更新するために使用します。この関数は、更新する統計データコードと値のリストを必要とし、リクエストが完了するとコールバック関数を呼び出します。
public void UpdateUserStatsFromClient(List<StatItemUpdate> statItems, ResultCallback<StatItemOperationResult[]> resultCallback = null)
{
statistic.UpdateUserStatItems(
statItems.ToArray(),
result => OnUpdateUserStatsFromClientCompleted(result, resultCallback)
);
} -
次に、上記の関数のコールバック関数を作成します。ここでは、統計データ値の更新に成功したかどうかのリクエストステータスを単純にログに記録します。
private void OnUpdateUserStatsFromClientCompleted(Result<StatItemOperationResult[]> result, ResultCallback<StatItemOperationResult[]> customCallback = null)
{
if (!result.IsError)
{
BytewarsLogger.Log("Update User's Stat Items from Client successful.");
}
else
{
BytewarsLogger.LogWarning($"Update User's Stat Items from Client failed. Message: {result.Error.Message}");
}
customCallback?.Invoke(result);
}
ゲームサーバー用の統計データを実装する
このセクションでは、AGS SDK を使用してゲームサーバー用の統計データ機能を実装する方法を説明します。
-
StatsEssentialsWrapper_Starterクラスを開きます。Admin Portal で以前に設定したエリミネーションモードとチームデスマッチモードの最高スコアなど、ゲームサーバーによって更新されることを意図した統計データ値を更新するための以下の新しい関数を作成します。この関数は、更新する統計データコードと値のリストを必要とし、リクエストが完了するとコールバック関数を呼び出します。public void UpdateManyUserStatsFromServer(List<UserStatItemUpdate> statItems, ResultCallback<StatItemOperationResult[]> resultCallback)
{
serverStatistic.UpdateManyUsersStatItems(
statItems.ToArray(),
result => OnUpdateManyUserStatsFromServerCompleted(result, resultCallback));
} -
次に、上記の関数のコールバック関数を作成します。ここでは、統計データ値の更新に成功したかどうかのリクエストステータスを単純にログに記録します。
private void OnUpdateManyUserStatsFromServerCompleted(Result<StatItemOperationResult[]> result, ResultCallback<StatItemOperationResult[]> customCallback = null)
{
if (!result.IsError)
{
BytewarsLogger.Log("Update User's Stat Items from Server successful.");
}
else
{
BytewarsLogger.LogWarning($"Update User's Stat Items from Server failed. Message: {result.Error.Message}");
}
customCallback?.Invoke(result);
}
統計データ値を更新するための実装
Byte Wars では、ゲーム終了時にプレイヤーの統計データ値を更新するように設計されています。これらの値は、ゲームモードと統計データタイプ(例: ゲームクライアント用、またはゲームサーバー用)に基づいて更新する必要があります。このセクションでは、以前に作成した関数を使用して統計データ値を更新する方法を説明します。
-
StatsEssentialsWrapper_Starterクラスを開き、以下の関数を作成します。この関数は、ゲーム内の接続されているすべてのプレイヤーの統計データを更新するためのラッパー関数です。まず、現在プレイされているゲームモードを判断します。次に、接続されているプレイヤーをループして、そのデータを統計データアイテムに格納します。最後に、この関数はすべての統計データアイテムを収集し、ゲームモードに基づいてUpdateUserStatsFromClient()またはUpdateManyUserStatsFromServer()のいずれかを呼び出します。備考以下の関数は、Byte Wars で利用可能なすべての統計データを変更する方法を示しています。ただし、このモジュールでは、プレイヤーの最高スコアの統計データのみを設定します。他の統計データ値は更新されません。
private void UpdateConnectedPlayersStatsOnGameEnds(GameManager.GameOverReason reason)
{
if (reason != GameManager.GameOverReason.MatchEnded)
{
return;
}
GameModeEnum gameMode = GameManager.Instance.GameMode;
InGameMode inGameMode = GameManager.Instance.InGameMode;
StatsEssentialsModels.GameStatsData gameStatsData = StatsEssentialsModels.GetGameStatsDataByGameMode(inGameMode);
List<PlayerState> playerStates = GameManager.Instance.ConnectedPlayerStates.Values.ToList();
// Store statistics to update.
List<UserStatItemUpdate> statItems = new();
Dictionary<int, (bool isWinner, int score, int kills, int deaths)> teamStats = new();
GameManager.Instance.GetWinner(out TeamState winnerTeam, out PlayerState winnerPlayer);
foreach (PlayerState playerState in playerStates)
{
if (!teamStats.ContainsKey(playerState.TeamIndex))
{
List<PlayerState> teamPlayers = playerStates.Where(p => p.TeamIndex == playerState.TeamIndex).ToList();
teamStats.Add(playerState.TeamIndex, new()
{
isWinner = winnerTeam != null ? playerState.TeamIndex == winnerTeam.teamIndex : false,
score = (int)teamPlayers.Sum(p => p.Score),
kills = teamPlayers.Sum(p => p.KillCount),
deaths = (GameData.GameModeSo.PlayerStartLives * teamPlayers.Count) - teamPlayers.Sum(p => p.Lives)
});
}
(bool isWinner, int score, int kills, int deaths) = teamStats[playerState.TeamIndex];
// Highest score statistic
statItems.Add(new()
{
updateStrategy = StatisticUpdateStrategy.MAX,
statCode = gameStatsData.HighestScoreStats.StatCode,
userId = playerState.PlayerId,
value = score
});
// Total score statistic
statItems.Add(new()
{
updateStrategy = StatisticUpdateStrategy.INCREMENT,
statCode = gameStatsData.TotalScoreStats.StatCode,
userId = playerState.PlayerId,
value = score
});
// Matches played statistic
statItems.Add(new()
{
updateStrategy = StatisticUpdateStrategy.INCREMENT,
statCode = gameStatsData.MatchesPlayedStats.StatCode,
userId = playerState.PlayerId,
value = 1
});
// Matches won statistic
statItems.Add(new()
{
updateStrategy = StatisticUpdateStrategy.INCREMENT,
statCode = gameStatsData.MatchesWonStats.StatCode,
userId = playerState.PlayerId,
value = isWinner ? 1 : 0
});
// Kill count statistic
statItems.Add(new()
{
updateStrategy = StatisticUpdateStrategy.INCREMENT,
statCode = gameStatsData.KillCountStats.StatCode,
userId = playerState.PlayerId,
value = kills
});
// Death statistic
statItems.Add(new()
{
updateStrategy = StatisticUpdateStrategy.INCREMENT,
statCode = gameStatsData.DeathStats.StatCode,
userId = playerState.PlayerId,
value = deaths
});
}
#if UNITY_SERVER
BytewarsLogger.Log($"[Server] Update the stats of connected players when the game ended. Game mode: {gameMode}. In game mode: {inGameMode}");
UpdateManyUserStatsFromServer(statItems, (Result<StatItemOperationResult[]> result) =>
{
if (!result.IsError)
{
BytewarsLogger.Log("[Server] Successfully updated the stats of connected players when the game ended.");
}
else
{
BytewarsLogger.LogWarning($"[Server] Failed to update the stats of connected players when the game ended. Error: {result.Error.Message}");
}
});
#else
BytewarsLogger.Log($"[Client] Update the stats of local connected players when the game ended. Game mode: {gameMode}. In game mode: {inGameMode}");
/* Local gameplay only has one valid account, which is the player who logged in to the game.
* Thus, we can only update the stats based on that player's user ID. */
List<StatItemUpdate> localPlayerStatItems = statItems
.Where(x => x.userId == GameData.CachedPlayerState.PlayerId)
.Select(x => new StatItemUpdate
{
updateStrategy = x.updateStrategy,
value = x.value,
statCode = x.statCode,
additionalData = x.additionalData
}).ToList();
UpdateUserStatsFromClient(localPlayerStatItems, (Result<StatItemOperationResult[]> result) =>
{
if (!result.IsError)
{
BytewarsLogger.Log("[Client] Successfully updated the stats of connected players when the game ended.");
}
else
{
BytewarsLogger.LogWarning($"[Client] Failed to update the stats of connected players when the game ended. Error: {result.Error.Message}");
}
});
#endif
} -
最後に、
Start()関数で、上記の関数を以下のイベントにバインドして、ゲーム終了時に呼び出されるようにします。void Start()
{
statistic = AccelByteSDK.GetClientRegistry().GetApi().GetStatistic();
#if UNITY_SERVER
serverStatistic = AccelByteSDK.GetServerRegistry().GetApi().GetStatistic();
#endif
GameManager.OnGameEnded += UpdateConnectedPlayersStatsOnGameEnds;
}
リソース
-
このチュートリアルで使用されているファイルは、Unity Byte Wars GitHub リポジトリで入手できます。