SDKを使用してパーティでプレイする - パーティでプレイする - (Unityモジュール)
注釈:本資料はAI技術を用いて翻訳されています。
ラッパーを展開する
このセクションでは、AccelByte Gaming Services (AGS) Software Development Kit (SDK) を使用してパーティでプレイする機能を実装する方法を学習します。Byte Wars プロジェクトには、パーティプレイの完全な実装を含む PlayingWithPartyWrapper という名前のラッパークラスがすでに存在します。このチュートリアルでは、そのラッパーのスターターバージョンを使用して、パーティでプレイする機能をゼロから実装します。
チュートリアルスターターモードを有効にする
このチュートリアルに従うには、まずスターターモードを有効にする必要があります。これを行うには、プロジェクトをビルドしてUnity Editorで開きます。
エディターで、Assets/Resources/Modules/Play/PlayingWithParty に移動し、PlayingWithPartyAssetConfig.asset を開きます。次に、Is Active チェックボックスと Is Starter Active チェックボックスをオンにして、スターターモードでモジュールを有効化します。
スターターパックの内容
このチュートリアルに従うには、PlayingWithPartyWrapper_Starter というスターターラッパークラスを使用します。このクラスは、基本的なセッション実装(ゲームセッションの作成、参加、招待)を含む SessionEssentialsWrapper クラスを継承しており、次のファイルで定義されています:
- C# ファイル:
Assets/Resources/Modules/Play/SessionEssentials/Scripts/SessionEssentialsWrapper.cs
SessionEssentialsWrapper クラス自体は、次のファイルで定義されている AccelByteWarsOnlineSession クラスを継承しています:
- C# ファイル:
Assets/Resources/Modules/Play/OnlineSessionUtils/Scripts/AccelByteWarsOnlineSession.cs
PlayingWithPartyWrapper_Starter クラスは、以下のファイルで定義されています:
- C# ファイル:
Assets/Resources/Modules/Play/PlayingWithParty/Scripts/PlayingWithPartyWrapper_Starter.cs
また、ヘルパー定数変数と構造体を含むモデルクラスが以下のファイルで定義されています:
- C# ファイル:
Assets/Resources/Modules/Play/PlayingWithParty/Scripts/PlayingWithPartyWrapperModels.cs
AccelByteWarsOnlineSession クラスは、セッションおよびパーティ関連のロジックのための基底抽象クラスです。これには、以下を含むいくつかの事前定義されたヘルパーコンポーネントが含まれています:
-
AGS SDKインターフェースを参照するためのヘルパー変数。これらの変数は初期化時に割り当てられます:
protected static User User;
protected static Lobby Lobby;
protected static Session Session;
protected virtual void Awake()
{
...
User ??= AccelByteSDK.GetClientRegistry().GetApi().GetUser();
Lobby ??= AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
Session ??= AccelByteSDK.GetClientRegistry().GetApi().GetSession();
...
} -
キャッシュされたパーティセッションとパーティステータスを取得するためのヘルパープロパティ。
public static SessionV2PartySession CachedParty { get; protected set; } = null;
public static bool IsInParty => CachedParty != null && CachedParty.members.Select(m => m.StatusV2 == SessionV2MemberStatus.JOINED).ToArray().Length > 1;
public static bool IsPartyLeader => IsInParty && GameData.CachedPlayerState.PlayerId == CachedParty.leaderId;
PlayingWithPartyWrapperModels クラスには、以下を含むいくつかのヘルパーが含まれています:
-
パーティ関連のポップアップメッセージを表示するための文字列。
public static readonly string PartyMembersGameSessionStatusesKey = "party_member_game_session_statuses";
public static readonly string PartyMatchmakingStartedMessage = "Party Matchmaking Started by Party Leader";
public static readonly string PartyMatchmakingSuccessMessage = "Party Matchmaking Found, Joining Match";
public static readonly string PartyMatchmakingFailedMessage = "Party Matchmaking failed";
public static readonly string PartyMatchmakingCanceledMessage = "Party Matchmaking is canceled by party leader";
public static readonly string PartyMatchmakingExpiredMessage = "Party Matchmaking expired";
public static readonly string PartyMatchmakingSafeguardMessage = "Matchmaking with this game mode is not supported when in a party";
public static readonly string JoinPartyGameSessionMessage = "Joining Party Leader Game Session";
public static readonly string JoinPartyGameSessionFailedMessage = "Failed to Join Party Game Session";
public static readonly string JoinPartyGameSessionCanceledMessage = "Party game session is canceled by party leader";
public static readonly string JoinPartyGameSessionWaitServerMessage = "Joined Party Game Session. Waiting for Server";
public static readonly string JoinPartyGameSessionServerErrorMessage = "Party Game Session failure. Cannot find game server.";
public static readonly string JoinPartyGameSessionSafeguardMessage = "Cannot join session. Insufficient slots to join with party";
public static readonly string PartyGameSessionLeaderSafeguardMessage = "Cannot play online session since party members are on other session";
public static readonly string PartyGameSessionMemberSafeguardMessage = "Only party leader can start online session";
パーティでゲームセッションを実装する
このセクションでは、パーティでゲームセッションを作成および参加する方法を学習します。
-
セッション入門モジュールで、ゲームセッションの作成と参加をすでに実装しました。ゲームセッションを作成する際、すべてのパーティメンバーを招待するための追加パラメータを渡すことができます。そのモジュールラッパーの
CreateMatchSession()関数で、teamsパラメータを設定するコードを追加します。このパラメータを使用すると、バックエンドはそこに含まれるすべてのプレイヤーにゲームセッション招待を送信します。これにより、パーティリーダーがゲームセッションを作成すると、メンバーも招待されます。public override void CreateGameSession(
SessionV2GameSessionCreateRequest request,
ResultCallback<SessionV2GameSession> onComplete)
{
// ..
// Add party session id for playing with party feature.
if (CachedParty != null && !string.IsNullOrEmpty(CachedParty.id))
{
request.teams = new SessionV2TeamData[]
{
new SessionV2TeamData
{
TeamId = CachedParty.id,
userIds = CachedParty.members.Where(m => m.StatusV2 == SessionV2MemberStatus.JOINED).Select(m => m.id).ToArray()
}
};
}
// ..
} -
パーティメンバーがバックエンドからゲームセッション招待を受け取ったら、プレイヤーがゲームセッションに参加するようにします。これを行うには、
PlayingWithPartyWrapper_Starterクラスを開き、以下の関数を作成します。private void OnInvitedToPartyGameSession(Result<SessionV2GameInvitationNotification> result)
{
if (!IsInParty)
{
return;
}
if (result.IsError)
{
BytewarsLogger.LogWarning($"Failed to handle party game session invitation event. Error {result.Error.Code}: {result.Error.Message}");
return;
}
BytewarsLogger.Log($"Received party game session session invitation.");
/* If the player is a party member, join the party leader's game session
* only if the player is not currently in any session, including offline single-player mode. */
if (GameData.GameModeSo?.GameMode != GameModeEnum.MainMenu &&
CachedSession == null && !IsPartyLeader)
{
Session.GetGameSessionDetailsBySessionId(result.Value.sessionId, (sessionResult) =>
{
if (sessionResult.IsError)
{
BytewarsLogger.LogWarning($"Failed to get game session details. Error {sessionResult.Error.Code}: {sessionResult.Error.Message}");
return;
}
// If the session is not from party leader, ignore it.
if (!sessionResult.Value.members.Select(m => m.id).Contains(CachedParty?.leaderId))
{
return;
}
MenuManager.Instance.PromptMenu.ShowLoadingPrompt(PlayingWithPartyModels.JoinPartyGameSessionMessage);
JoinGameSession(sessionResult.Value.id, (joinResult) =>
{
if (joinResult.IsError)
{
BytewarsLogger.LogWarning($"Failed to join party leader game session. Error {joinResult.Error.Code}: {joinResult.Error.Message}");
MenuManager.Instance.PromptMenu.HidePromptMenu();
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.JoinPartyGameSessionFailedMessage
});
return;
}
// Handle travel to server or host.
SessionV2GameSession gameSession = joinResult.Value;
switch (gameSession.configuration.type)
{
case SessionConfigurationTemplateType.DS:
if (gameSession.dsInformation == null || gameSession.dsInformation.status != SessionV2DsStatus.AVAILABLE)
{
Lobby.SessionV2DsStatusChanged += OnDSStatusChangedReceived;
MenuManager.Instance.PromptMenu.ShowLoadingPrompt(
PlayingWithPartyModels.JoinPartyGameSessionWaitServerMessage, true,
PromptMenuCanvas.DefaultCancelMessage, () =>
{
Lobby.SessionV2DsStatusChanged -= OnDSStatusChangedReceived;
LeaveGameSession(gameSession.id, null);
});
}
else
{
TravelToDS(gameSession, AccelByteWarsOnlineSessionModels.GetGameSessionGameMode(gameSession));
}
break;
case SessionConfigurationTemplateType.P2P:
TravelToP2PHost(gameSession, AccelByteWarsOnlineSessionModels.GetGameSessionGameMode(gameSession));
break;
default:
BytewarsLogger.LogWarning($"Failed to travel. Unsupported game session server configuration type.");
break;
}
});
});
}
} -
上記の関数から、ゲームセッションに参加可能なゲームサーバーがまだない場合のイベントもリッスンします。そのため、以下のコードを追加して、このイベントを処理する関数を作成しましょう。
private void OnDSStatusChangedReceived(Result<SessionV2DsStatusUpdatedNotification> result)
{
if (result.IsError)
{
BytewarsLogger.LogWarning(
$"Failed to handle dedicated server status changed event. " +
$"Error {result.Error.Code}: {result.Error.Message}");
Lobby.SessionV2DsStatusChanged -= OnDSStatusChangedReceived;
MenuManager.Instance.PromptMenu.ShowPromptMenu(
PromptMenuCanvas.DefaultErrorPromptMessage,
SessionEssentialsModels.FailedToFindServerMessage,
PromptMenuCanvas.DefaultOkMessage, null);
return;
}
SessionV2GameSession session = result.Value.session;
SessionV2DsInformation dsInfo = session.dsInformation;
// Check if the requested game mode is supported.
InGameMode requestedGameMode = AccelByteWarsOnlineSessionModels.GetGameSessionGameMode(session);
if (requestedGameMode == InGameMode.None)
{
BytewarsLogger.LogWarning(
$"Failed to handle dedicated server status changed event. " +
$"Session's game mode is not supported by the game.");
Lobby.SessionV2DsStatusChanged -= OnDSStatusChangedReceived;
MenuManager.Instance.PromptMenu.ShowPromptMenu(
PromptMenuCanvas.DefaultErrorPromptMessage,
SessionEssentialsModels.FailedToFindServerMessage,
PromptMenuCanvas.DefaultOkMessage, null);
return;
}
// Check the dedicated server status.
switch (dsInfo.StatusV2)
{
case SessionV2DsStatus.AVAILABLE:
Lobby.SessionV2DsStatusChanged -= OnDSStatusChangedReceived;
TravelToDS(session, requestedGameMode);
break;
case SessionV2DsStatus.FAILED_TO_REQUEST:
case SessionV2DsStatus.ENDED:
case SessionV2DsStatus.UNKNOWN:
Lobby.SessionV2DsStatusChanged -= OnDSStatusChangedReceived;
BytewarsLogger.LogWarning(
$"Failed to handle dedicated server status changed event. " +
$"Session failed to request for dedicated server due to unknown reason.");
MenuManager.Instance.PromptMenu.ShowPromptMenu(
PromptMenuCanvas.DefaultErrorPromptMessage,
SessionEssentialsModels.FailedToFindServerMessage,
PromptMenuCanvas.DefaultOkMessage, null);
break;
default:
BytewarsLogger.Log($"Received dedicated server status change. Status: {dsInfo.StatusV2}");
break;
}
} -
ゲームセッションの作成とは異なり、セッション参加リクエストを送信する際にパラメータを渡すことはできません。したがって、パーティリーダーが既存のゲームセッションに参加する場合、同じゲームセッションに参加させたい場合は、パーティメンバーを手動で招待する必要があります。これを行うには、
PlayingWithPartyWrapper_Starterクラスに以下の関数を作成します。private void OnPartyGameSessionJoined(Result<SessionV2GameJoinedNotification> result)
{
if (!IsInParty)
{
return;
}
if (result.IsError)
{
BytewarsLogger.LogWarning($"Failed to handle party game session joined event. Error {result.Error.Code}: {result.Error.Message}");
return;
}
BytewarsLogger.Log($"Received game session joined event.");
if (IsPartyLeader)
{
HashSet<string> partyMemberIds = CachedParty?.members.Where(m => m.StatusV2 == SessionV2MemberStatus.JOINED).Select(m => m.id).ToHashSet();
HashSet<string> sessionMemberIds = result.Value.members.Select(m => m.id).ToHashSet();
// Send manual invites to party members not yet invited.
string[] uninvitedPartyMemberIds = partyMemberIds?.Where(id => !sessionMemberIds.Contains(id)).ToArray();
if (uninvitedPartyMemberIds.Length > 0)
{
foreach (string memberId in uninvitedPartyMemberIds)
{
SendGameSessionInvite(result.Value.sessionId, memberId, null);
}
}
}
} -
ラッパーが初期化されたときに、上記の関数をそれぞれのイベントにバインドします。これは
OnEnable()関数で行うことができます。private void OnEnable()
{
...
if (Lobby != null)
{
// Bind game session events.
Lobby.SessionV2InvitedUserToGameSession += OnInvitedToPartyGameSession;
Lobby.SessionV2UserJoinedGameSession += OnPartyGameSessionJoined;
}
} -
最後に、ラッパーが非初期化されたときに、上記の関数をそれぞれのイベントからアンバインドします。これは
OnDisable()関数で行うことができます。private void OnDisable()
{
...
if (Lobby != null)
{
// Unbind game session events.
Lobby.SessionV2InvitedUserToGameSession -= OnInvitedToPartyGameSession;
Lobby.SessionV2UserJoinedGameSession -= OnPartyGameSessionJoined;
}
}
パーティでマッチメイキングを実装する
このセクションでは、パーティでマッチメイキングを実行する方法を学習します。
-
パーティでマッチメイキングを有効にするには、まずパーティシステムを実装する必要があります。詳細については、パーティ入門モジュールを再確認してください。
-
次に、マッチチケットを作成する際、オプションのマッチチケットパラメータにパーティIDを渡す必要があります。専用サーバーでのクイックマッチおよびピアツーピアでのクイックマッチモジュールを再確認して、マッチメイキング用のマッチチケットを作成する方法を学習してください。これらのモジュールのラッパーの
StartMatchmaking()関数で、マッチメイキングプロセスを開始する前に、マッチチケットのsessionIdパラメータをプレイヤーのパーティIDで設定するために、以下のコードを追加します。public override void StartMatchmaking(
string matchPool,
ResultCallback<MatchmakingV2CreateTicketResponse> onComplete)
{
...
MatchmakingV2CreateTicketRequestOptionalParams optionalParams = new() { attributes = new() };
...
// Add party session id for playing with party feature.
if (CachedParty != null && !string.IsNullOrEmpty(CachedParty.id))
{
optionalParams.sessionId = CachedParty.id;
}
...
} -
マッチチケットの
sessionIdパラメータにパーティIDを設定することで、バックエンドはパーティ全体をマッチメイキングし、各メンバーを自動的に招待します。メンバーが招待を受け取ったら、それを承認してゲームセッションに参加する必要があります。これは前のセクションですでに処理しました。 -
これでマッチメイキングのセットアップが完了したので、パーティマッチメイキングイベントを処理するためのいくつかの関数を作成しましょう。
PlayingWithPartyWrapper_Starterクラスを開き、マッチメイキングが開始されたときに呼び出される以下の関数を作成します。この関数は、パーティリーダーがマッチメイキングを開始したことを示すプロンプトをパーティメンバーに表示します。private void OnPartyMatchmakingStarted(Result<MatchmakingV2MatchmakingStartedNotification> result)
{
if (!IsInParty)
{
return;
}
if (result.IsError)
{
BytewarsLogger.LogWarning($"Failed to start party matchmaking. Error {result.Error.Code}: {result.Error.Message}");
return;
}
BytewarsLogger.Log($"Party matchmaking started.");
/* Show notification that the party matchmaking is started.
* Only show the notification if the player is a party member.*/
if (!IsPartyLeader)
{
MenuManager.Instance.PromptMenu.ShowLoadingPrompt(PlayingWithPartyModels.PartyMatchmakingStartedMessage);
}
} -
同じクラスで、マッチメイキングが正常に完了したか失敗したかを示すプロンプトメッセージをパーティメンバーに表示する以下の関数を作成します。
private void OnPartyMatchmakingFound(Result<MatchmakingV2MatchFoundNotification> result)
{
if (!IsInParty)
{
return;
}
if (result.IsError)
{
BytewarsLogger.LogWarning($"Failed to party matchmaking. Error {result.Error.Code}: {result.Error.Message}");
return;
}
else
{
BytewarsLogger.Log($"Party matchmaking found. Currently joining the match.");
}
/* Show notification that the party matchmaking is completed.
* Only show the notification if the player is a party member.*/
if (!IsPartyLeader)
{
if (result.IsError)
{
MenuManager.Instance.PromptMenu.HidePromptMenu();
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.PartyMatchmakingFailedMessage,
UseDefaultIconOnEmpty = false
});
}
else
{
MenuManager.Instance.PromptMenu.ShowLoadingPrompt(PlayingWithPartyModels.PartyMatchmakingSuccessMessage);
}
}
} -
次に、パーティリーダーがマッチメイキングをキャンセルしたことを示すプロンプトをパーティメンバーに表示する以下の関数を作成します。
private void OnPartyMatchmakingCanceled(Result<MatchmakingV2MatchmakingCanceledNotification> result)
{
if (!IsInParty)
{
return;
}
if (result.IsError)
{
BytewarsLogger.LogWarning($"Failed to cancel party matchmaking. Error {result.Error.Code}: {result.Error.Message}");
return;
}
BytewarsLogger.Log($"Party matchmaking canceled.");
/* Show notification that the party matchmaking is canceled.
* Only show the notification if the player is a party member.*/
if (!IsPartyLeader)
{
MenuManager.Instance.PromptMenu.HidePromptMenu();
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.PartyMatchmakingCanceledMessage,
UseDefaultIconOnEmpty = false
});
}
} -
次に、マッチチケットの有効期限が切れたためにマッチメイキングが失敗したことを示すプロンプトをパーティメンバーに表示する以下の関数を作成します。
private void OnPartyMatchmakingExpired(Result<MatchmakingV2TicketExpiredNotification> result)
{
if (!IsInParty)
{
return;
}
if (result.IsError)
{
BytewarsLogger.LogWarning($"Failed to handle party matchmaking expired event. Error {result.Error.Code}: {result.Error.Message}");
return;
}
BytewarsLogger.Log($"Party matchmaking expired.");
if (!IsPartyLeader)
{
MenuManager.Instance.PromptMenu.HidePromptMenu();
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.PartyMatchmakingExpiredMessage,
UseDefaultIconOnEmpty = false
});
}
} -
ラッパーが初期化されたときに、上記の関数をそれぞれのイベントにバインドします。これは
OnEnable()関数で行うことができます。private void OnEnable()
{
...
if (Lobby != null)
{
// Bind party matchmaking events.
Lobby.MatchmakingV2MatchmakingStarted += OnPartyMatchmakingStarted;
Lobby.MatchmakingV2MatchFound += OnPartyMatchmakingFound;
Lobby.MatchmakingV2MatchmakingCanceled += OnPartyMatchmakingCanceled;
Lobby.MatchmakingV2TicketExpired += OnPartyMatchmakingExpired;
}
} -
最後に、ラッパーが非初期化されたときに、上記の関数をそれぞれのイベントからアンバインドします。これは
OnDisable()関数で行うことができます。private void OnDisable()
{
...
if (Lobby != null)
{
// Unbind party matchmaking events.
Lobby.MatchmakingV2MatchmakingStarted -= OnPartyMatchmakingStarted;
Lobby.MatchmakingV2MatchFound -= OnPartyMatchmakingFound;
Lobby.MatchmakingV2MatchmakingCanceled -= OnPartyMatchmakingCanceled;
Lobby.MatchmakingV2TicketExpired -= OnPartyMatchmakingExpired;
}
}
リソース
-
このチュートリアルセクションで使用されているファイルは、Unity Byte Wars GitHubリポジトリで入手できます。
- Assets/Resources/Modules/Play/PlayingWithParty/Scripts/PlayingWithPartyWrapper_Starter.cs
- Assets/Resources/Modules/Play/PlayingWithParty/Scripts/PlayWithPartyModels.cs
- Assets/Resources/Modules/Play/SessionEssentials/Scripts/SessionEssentialsWrapper.cs
- Assets/Resources/Modules/Play/OnlineSessionUtils/Scripts/AccelByteWarsOnlineSession.cs