マルチプレイヤー ゲームでは、プレイヤーがチームとなって競争または協力して目標を達成します。 各チームのプレイヤー数は、ゲームプレイに根本的な影響を与える可能性があり、多くのデベロッパーはプレイヤーについて特定の比率を選択し、楽しめるエクスペリエンスを作成しています。
チームのバランスは、設計された比率に従ってプレイヤーをチームに振り分けます。 ほとんどのマルチプレイヤー ゲームではチームのバランスが均等になるように調整するため、いずれかのチームが有利になることはありません。 一部のゲームでは、4 人のプレイヤーで 1 人の圧倒的なプレイヤーと戦うなど、意図的にバランスの悪いシナリオを作成しています。 設定に関係なく、複数あるプレイヤーのチーム向けに面白いエクスペリエンスを作成するには、チームのバランスが非常に重要です。
このガイドを完了すると、ランタイム時、および新しいプレイヤーがゲームに参加するたびに、チームに所属するプレイヤーのバランスを動的に調整する方法を習得できます。 完全なスクリプトは、参照用としてこのガイドの末尾に記載されています。
使用する Verse 言語機能
option:この仕掛けは、オプションを使用して現在プレイヤーが所属しているチームよりプレイヤー数の少ないチームがあるかどうかを判断します。
使用する Verse API
Subscribable (サブスクライブ可能):
PlayerAddedEvent()をサブスクライブして、新しいプレイヤーが進行中のゲームに参加したときにチームのバランスを動的に再調整します。Teams (チーム):Team クラスにより、チームのプレイヤーを追加、削除、および取得します。 このチュートリアルでは、Team クラスを使用してプレイヤーおよびチームの割り当てを直接操作します。
Playspace (プレイ空間):プレイ空間は、ゲームに参加および退出するプレイヤーに関連するサブスクライブ可能なイベントを追跡します。 また、プレイヤーとチームのリストの取得や、特定のプレイヤーのチーム検索も行います。 このチュートリアルでは、複数のプレイ空間のイベントをサブスクライブし、プレイ空間メソッドを使用してプレイヤーやチームを取得することにより、それらを直接操作できます。
レベルを設定する
この例では、次の仕掛けを使用します。
プレイヤー スポーン パッドの仕掛け × 4:この仕掛けにより、ゲームの開始時にプレイヤーがスポーンする場所を定義します。
次の手順に従ってレベルを設定します。
プレイヤー スポーン パッドをレベルに追加します。
[Outliner (アウトライナー)] でスポーン パッドを選択して、その[Details] パネルを開きます。
[Details] パネルの [User Options (ユーザー オプション)] で次のように設定します。
[Player Team (プレイヤー チーム)] で [Team Index (チーム インデックス)] を選択し、値を「1」に設定します
[Visible in Game (ゲーム中に表示)] を有効にします
画像をクリックすると拡大します。
スポーン パッドを複製し、最初のスポーン パッドの隣に配置します。
両方のスポーン パッドを複製し、最初のスポーン パッドのグループから離れた場所に配置します。 この場所に Team 2 のプレイヤーがスポーンします。
複製したスポーン パッドを選択し、[Details] パネルの [User Options] で、両方の [Team Index] の値を「2」に変更します。
[Outliner] で、[Island Settings (島設定)] の仕掛けを選択し、[Details] パネルを開きます。 [User Options - Game Rules (ユーザー オプション - ゲーム ルール)] で次のように設定します。
[Teams] で [Team Index] を選択し、値を「2」に設定します。 この例では 2 つのチームを使用していますが、チームはいくつでも設定することができます。
[Team Size (チーム サイズ)] を [Dynamic (動的)] に設定します。 これは、Verse コードによってチームのバランスを調整できることを意味します。
[Join in Progress (途中参加)] を [Spawn (スポーン)] に設定し、新しいプレイヤーが進行中のゲームに参加できるようにします。
画像をクリックすると拡大します。
Verse Explorer で、team_multiplayer_balancing という名前の新しい Verse の仕掛けを作成し、この仕掛けをレベルにドラッグします (Verse で新しい仕掛けを作成する方法については、「Verse を使用して独自の仕掛けを作成する」を参照してください)。
レベルは次のセットアップのようになります。
チームを均等に分ける
ゲームの開始時にチームのバランスを調整する
次の手順では、ゲームの開始時および新しいプレイヤーがゲームに参加したとき、プレイヤーをチームに均等に振り分ける方法について説明します。
Verse Explorer を開き、[team_multiplayer_balancing.verse] をダブルクリックして Visual Studio Code でスクリプトを開きます。
team_multiplayer_balancingクラス定義で、「Teams」という名前の変数team配列を追加します。これにはプレイヤーが所属している各チームへの参照が格納されます。Verseteam_multiplayer_balance := class(creative_device): # Holds the teams found with GetTeams() var Teams : []team = array{}OnBegin()関数で、前に [Island Settings] で設定したチームと一致するようにTeams配列を更新します。fort_team_collectionAPI のGetTeams()関数を呼び出して、プレイ空間のすべてのチームを取得します。VerseOnBegin<override>()<suspends>:void= Print("Verse Device Started!") set Teams = Self.GetPlayspace().GetTeamCollection().GetTeams()GetPlayers()関数を呼び出してゲーム内のすべてのプレイヤーを検索し、「AllPlayers」という名前のプレイヤー配列に保存します。VerseOnBegin<override>()<suspends>:void= Print("Verse Device Started!") set Teams = Self.GetPlayspace().GetTeamCollection().GetTeams() AllPlayers := GetPlayspace().GetPlayers()すべてのプレイヤーのリストをイテレートし、同じプレイヤー数のチームを作成します。 プレイヤーが現在所属しているチームを他のチームと比較し、そのプレイヤーに最適かどうかを判断します。 この場合、
GetTeam[]を使用してゲームの開始時にプレイヤーが自動的に割り当てられるチームを使用できます (複数のチームがあるゲーム モードでは、プレイヤーはチームに所属する必要があるため)。GetTeam[]にはエージェント型のパラメータが必要ですが、プレイヤーはエージェントのサブクラスのため、型変換なしでプレイヤーを渡せることに注意してください。VerseAllPlayers := GetPlayspace().GetPlayers() for (TeamPlayer : AllPlayers, CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]): # Assign Players to a new team if teams are unbalancedチームは内部クラスのため初期化することができず、既存のチーム オブジェクトへの参照としてのみ使用できます。
すべてのチームのバランスが整うまで、プレイヤー数が最も少ないチームにプレイヤーを割り当てます。 そのためには、
forループを使用して各プレイヤーを確認し、次に各チームを確認する必要があります。 1 つでプレイヤーをイテレートし、もう 1 つでチームをイテレートするために 2 つの for ループを使用することができますが、この例ではチームの for ループを独自のメソッドに抽出します。 各プレイヤーへの参照を取得し、次に「CurrentTeam」という名前の定数でそれらのプレイヤーが所属する各チームへの参照を取得して、プレイヤーの for ループを設定します。新しい変数整数
TeamSizeを作成し、「0」に初期化します。次にこのプレイヤーが現在所属しているチームのサイズと等しくなるように設定します。GetAgents[]は失敗する可能性がある式のため、このセットを if 文に入れる必要があります。Versefor (TeamPlayer : AllPlayers, CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]): # Assign Players to a new team if teams are unbalanced var TeamSize : int = 0 if(set TeamSize = GetPlayspace().GetTeamCollection().GetAgents[CurrentTeam].Length): Print("Size of this player's starting team is {TeamSize}")FindSmallestTeam()という名前のメソッドを作成します。 これにより、引数としてTeamSizeが渡されると任意のチーム (?team) が返され、プレイヤー数の最も少ないチームを検索して返す処理が実行されます。FindSmallestTeam()内で、「SmallestTeam」という名前の新しいチームのオプションを初期化します。 ここでオプションを使用するのは、FindSmallestTeam()を呼び出す時点でプレイヤーがすでに最も小さいチームに所属している可能性があるためです。デフォルトで
SmallestTeamオプションは false に設定されているため、さらに小さいチームが見つからない場合はfalseのままです。FindSmallestTeam()がfalseを返すと、そのプレイヤーがすでに最も小さいチームに所属していることが明確になります。 変数 intCurrentTeamSizeもTeamSizeの値に初期化する必要があります。CurrentTeamSizeを変数にして、プレイヤー数が少ない他のチームのサイズに更新できるようにする必要があります。VerseFindSmallestTeam(CurrentTeamSize : int) : ?team= var SmallestTeam : ?team = false var TeamSize : int = CurrentTeamSizeTeamSizeではSmallestTeamのサイズを追跡するため、すべてのチームのサイズと比較する必要があります。 各チームをイテレートして、サイズをローカルの intCandidateTeamSizeで取得します。CandidateTeamSizeがTeamSizeよりも小さい場合、SmallestTeamをこのチームに設定し、TeamSizeをそのチームのサイズに設定します。条件
TeamSize > CandidateTeamSizeは for ループの括弧内で確認されるため、フィルタ条件になります。 フィルタ条件を使用すると、フィルタ条件が満たされた場合にのみ、ループ内のコードが実行されることが保証されます。 これにより、プレイヤー数の最も少ないチームが見つかった場合、必ずそのチームがSmallestTeamに設定されるようになります。 プレイヤー数の少ないチームが見つからない場合は、SmallestTeamが false のままになります。最後に、すべてのチームの確認が完了すると、
SmallestTeamが返されます。Versefor(Team : Teams, CandidateTeamSize := GetPlayspace().GetTeamCollection().GetAgents[Team].Length, TeamSize > CandidateTeamSize): set SmallestTeam = option{Team} set TeamSize = CandidateTeamSize Print("Found a team with less players: {CandidateTeamSize}") return SmallestTeamOnBegin()で、forループ内に「SmallestTeam」という名前の新しいチームのオプションを作成し、引数としてTeamSizeが渡されるときにFindSmallestTeam()の値に初期化します。VerseSmallestTeam : ?team = FindSmallestTeam(TeamSize)次に、
SmallestTeamオプション変数の値へのアクセスを試みます。 値が false の場合、プレイヤーはすでに最も小さいチームに所属しているため、それ以上の割り当ては必要ありません。 その他の場合は、新しいチームにプレイヤーを割り当てる必要があります。 多くのメソッドでは、オプションを引数として直接渡すことができないため、値をローカル変数TeamToAssignに抽出する必要があります。AddToTeam[player, team]を使用して、このチームへのプレイヤーの割り当てを試みることもできますが、このメソッドではプレイヤーをすでに所属しているチームに割り当てようとすると失敗することに注意してください。 ただし、forループは単に次のプレイヤーをイテレートし、失敗したプレイヤーは元のチームに留まるため、悪い影響は発生しません。Verseif (TeamToAssign := SmallestTeam?, GetPlayspace().GetTeamCollection().AddToTeam[TeamPlayer, TeamToAssign]): Print("Attempting to assign player to a new team")
OnBegin()は、次のようなコード ブロックになります。VerseOnBegin<override>()<suspends> : void = Print("Verse Device Started!") set Teams = Self.GetPlayspace().GetTeamCollection().GetTeams() Print("Beginning to Assign Players") Playspace := GetPlayspace() AllPlayers := Playspace.GetPlayers() for (TeamPlayer : AllPlayers, CurrentTeam := Playspace.GetTeamCollection().GetTeam[TeamPlayer]): var TeamSize : int = 0 if(set TeamSize = Playspace.GetTeamCollection().GetAgents[CurrentTeam].Length): Print("Size of this player's starting team is {TeamSize}")
ゲームの途中から参加するプレイヤーを処理する
進行中のゲームで、チームのバランスを自動調整できるようにするため、新しいプレイヤーが参加するときに発生するイベントをサブスクライブする必要があります。 それまでに書いたコードをすべて繰り返さなくて済むように、共通のメソッドにリファクタリングすることができます。
「
BalanceTeams()」という名前のメソッドを作成し、GetTeams()を使用してTeams変数を設定した後、すべてのコードを移動します。 これはOnBegin()メソッドで呼び出されるため、ゲーム開始時のチームはバランスが取れています。BalanceTeams()は次のようになります。VerseBalanceTeams() : void = AllPlayers := GetPlayspace().GetPlayers() for (TeamPlayer : AllPlayers, CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]): var TeamSize : int = 0 if(set TeamSize = GetPlayspace().GetTeamCollection().GetAgents[CurrentTeam].Length): Print("Size of this player's starting team is {TeamSize}") SmallestTeam : ?team = FindSmallestTeam(TeamSize) if (TeamToAssign := SmallestTeam?, GetPlayspace().GetTeamCollection().AddToTeam[TeamPlayer, TeamToAssign]): Print("Attempting to assign player to a new team")「
OnPlayerAdded()」という名前の別のメソッドを作成します。これにはBalanceTeams()への呼び出しが含まれています。player変数を使用することはありませんが、このメソッドを使用してPlayerAddedEvent()をサブスクライブするため、メソッド定義にはこれが必要です。 サブスクライブ可能なイベントの詳細については、「仕掛けのインタラクションをコード化する」ページを参照してください。VerseOnPlayerAdded(InPlayer : player) : void = Print("A new Player joined, assigning them to a team!") BalanceTeams()OnBegin()で、OnPlayerAddedを使用してPlayerAddedEvent()をサブスクライブします。 これで、プレイヤーがゲームに参加するとOnPlayerAddedでBalanceTeams()が呼び出され、自動的にチームのバランスが調整されます。VerseOnBegin<override>()<suspends> : void = GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded) Print("Beginning to balance teams") BalanceTeams()Visual Studio Code でスクリプトを保存し、[Build Verse Code (Verseコードをビルド)] をクリックしてスクリプトをコンパイルします。
UEFN ツールバーの [Launch Session (セッションを開始)] をクリックしてレベルをプレイテストします。
レベルをプレイテストすると、各チームのサイズと、スクリプトで検出されたサイズの小さなチームが出力ログに出力されます。 プレイヤーはすべてのチームに均等に振り分けられる必要があり、ゲームに新しいプレイヤーが参加する場合も、この均等なバランスを保つ必要があります。
完全なスクリプト
次のコードは、チームに所属するプレイヤーのバランスを自動的に調整する仕掛けの完全なスクリプトです。
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
team_multiplayer_balance := class(creative_device):
# Holds the teams found with GetTeams()
var Teams : []team = array{}
OnBegin<override>()<suspends> : void =
Print("Verse Device Started!")
応用編
このガイドを完了し、Verse を使用してチームに所属するプレイヤーのバランスを自動的に調整する仕掛けの作成方法を習得しました。
このガイドで学習した知識を活用し、1 人のプレイヤー対 4 人のプレイヤーのような非対称のゲーム モード向けに意図的にバランスの悪いチームを作成してみましょう。