マルチプレイヤー ゲームでは、プレイヤーがチームとなって競争または協力して目標を達成します。 各チームのプレイヤー数は、ゲームプレイに根本的な影響を与える可能性があり、多くのデベロッパーはプレイヤーについて特定の比率を選択し、楽しめるエクスペリエンスを作成しています。
チームのバランスは、設計された比率に従ってプレイヤーをチームに振り分けます。 ほとんどのマルチプレイヤー ゲームではチームのバランスが均等になるように調整するため、いずれかのチームが有利になることはありません。 一部のゲームでは、4 人のプレイヤーで 1 人の圧倒的なプレイヤーと戦うなど、意図的にバランスの悪いシナリオを作成しています。 設定に関係なく、複数あるプレイヤーのチーム向けに面白いエクスペリエンスを作成するには、チームのバランスが非常に重要です。

このガイドを完了すると、ランタイム時、および新しいプレイヤーがゲームに参加するたびに、チームに所属するプレイヤーのバランスを動的に調整する方法を習得できます。完全なスクリプト が、参照用としてこのガイドの末尾に記載されています。
使用する Verse 言語の機能
-
option
:この仕掛けは、オプションを使用して現在プレイヤーが所属しているチームよりプレイヤー数の少ないチームがあるかどうかを判断します。 -
if
:if
式 を使用して、チームのサイズに基づいてプレイヤーを新しいチームへ移動させる必要があるかどうかを確認します。
使用する Verse API
-
Subscribable (サブスクライブ可能):
PlayerAddedEvent()
を サブスクライブ して、新しいプレイヤーが進行中のゲームに参加したときにチームのバランスを動的に再調整します。 -
Team (チーム): Team クラス により、チームのプレイヤーを追加、削除、および取得します。このチュートリアルでは、Team クラスを使用してプレイヤーおよびチームの割り当てを直接操作します。
-
Playspace (プレイ空間): プレイ空間 は、ゲームに参加および退出するプレイヤーに関連するサブスクライブ可能なイベントをトラックします。また、プレイヤーとチームのリストの取得や、特定のプレイヤーのチーム検索も行います。このチュートリアルでは、複数のプレイ空間のイベントをサブスクライブし、プレイ空間 メソッド を使用してプレイヤーやチームを取得することにより、それらを直接操作できます。
レベルを設定する
この例では、次の仕掛けを使用します。
プレイヤー スポーン パッドの仕掛け x 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 (ユーザー オプション - ゲーム ルール)] で次のように実行します。
-
[Team (チーム)] で [Team Index] を選択し、値を「2」に設定します。この例では 2 つのチームを使用していますが、チームはいくつでも設定することができます。
-
[Team Size (チーム サイズ)] を [Dynamic (動的)] に設定します。これは、Verse コードによってチームのバランスを調整できることを意味します。
-
[Join in Progress (途中参加)] を [Spawn (スポーン)] に設定し、新しいプレイヤーが進行中のゲームに参加できるようにします。
画像をクリックすると拡大表示されます。
-
-
「team_multiplayer_balancing」という名前の新しい Verse の仕掛けを Verse Explorer で作成し、仕掛けをレベルにドラッグします。(Verse で新しい仕掛けを作成する方法については、「Verse を使用して独自の仕掛けを作成する」を参照してください。)
レベルは次のセットアップのようになります。

チームを均等に分ける
ゲームの開始時にチームのバランスを調整する
次の手順では、ゲームの開始時および新しいプレイヤーがゲームに参加したとき、プレイヤーをチームに均等に振り分ける方法について説明します。
-
Verse Explorer を開き、team_multiplayer_balancing.verse をダブルクリックして Visual Studio Code でスクリプトを開きます。
-
team_multiplayer_balancing
クラス定義で、「Teams
」という名前の変数team
配列を追加します。これにはプレイヤーが所属している各チームへの参照が格納されます。team_multiplayer_balance := class(creative_device): # GetTeams() を使用して見つかったチームを保持します var Teams:[]team = array{}
-
OnBegin()
関数 で、前に [Island Settings] で設定したチームと一致するようにTeams
配列を更新します。fort_team_collection
API のGetTeams()
関数を呼び出し て、プレイ空間のすべてのチームを取得します。OnBegin<override>()<suspends>:void= Print("Verse Device Started!") set Teams = Self.GetPlayspace().GetTeamCollection().GetTeams()
-
GetPlayers()
関数を呼び出してゲーム内のすべてのプレイヤーを検索し、「AllPlayers
」という名前のプレイヤー配列に保存します。OnBegin<override>()<suspends>:void= Print("Verse Device Started!") set Teams = Self.GetPlayspace().GetTeamCollection().GetTeams() AllPlayers := GetPlayspace().GetPlayers()
-
すべてのプレイヤーのリストをイテレートし、同じプレイヤー数のチームを作成します。プレイヤーが現在所属しているチームを他のチームと比較し、そのプレイヤーに最適かどうかを判断します。この場合、
GetTeam[]
を使用してゲームの開始時にプレイヤーが自動的に割り当てられるチームを使用することができます (複数のチームがあるゲーム モードでは、プレイヤーはチームに所属する必要があるため)。GetTeam[]
にはエージェント型の parameter が必要ですが、プレイヤーはエージェントのサブクラスのため、型変換 なしでプレイヤーを渡せることに注意してください。AllPlayers := GetPlayspace().GetPlayers() for (TeamPlayer : AllPlayers, CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]): # チームのバランスが悪い場合に、プレイヤーを新しいチームに割り当てる
チームは内部クラスのため初期化することができず、既存のチーム オブジェクトへの参照としてのみ使用できます。
-
すべてのチームのバランスが整うまで、プレイヤー数が最も少ないチームにプレイヤーを割り当てます。そのためには、
for
ループ を使用して各プレイヤーを確認し、次に各チームを確認する必要があります。1 つでプレイヤーをイテレートし、もう 1 つでチームをイテレートするために 2 つの for ループを使用することができますが、この例ではチームの for ループを独自のメソッドに抽出します。各プレイヤーへの参照を取得し、次に「CurrentTeam
」という名前の定数で彼らが所属する各チームへの参照を取得して、プレイヤーの for ループを設定します。-
新しい変数整数
TeamSize
を作成し、「0
」に初期化します。次にこのプレイヤーが現在所属しているチームのサイズと等しくなるように設定します。GetAgents[]
は失敗する可能性がある式のため、このセットを if 文に入れる必要があります。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}")
FindSmallestTeam()
という名前のメソッドを作成します。これにより、引数としてTeamSize
が渡されると任意のチーム (?team
) が返され、プレイヤー数の最も少ないチームを検索して返す処理が実行されます。FindSmallestTeam()
内で、「SmallestTeam
」という名前の新しいチームの オプション を初期化します。 ここでオプションを使用するのは、FindSmallestTeam()
を呼び出す時点でプレイヤーがすでに最も小さいチームに所属している可能性があるためです。-
デフォルトで
SmallestTeam
オプションは false に設定されているため、さらに小さいチームが見つからない場合はfalse
のままです。FindSmallestTeam()
がfalse
を返すと、そのプレイヤーがすでに最も小さいチームに所属していることが明確になります。変数 intCurrentTeamSize
もTeamSize
の値に初期化する必要があります。CurrentTeamSize
を変数にして、プレイヤー数が少ない他のチームのサイズに更新できるようにする必要があります。FindSmallestTeam(CurrentTeamSize : int) : ?team= var SmallestTeam : ?team = false var TeamSize : int = CurrentTeamSize
-
TeamSize
ではSmallestTeam
のサイズをトラックするため、すべてのチームのサイズと比較する必要があります。各チームをイテレートして、サイズをローカルの intCandidateTeamSize
で取得します。CandidateTeamSize
がTeamSize
よりも小さい場合、SmallestTeam
をこのチームに設定し、TeamSize
をそのチームのサイズに設定します。条件
TeamSize > CandidateTeamSize
は for ループの括弧内で確認されるため、フィルタ条件になります。フィルタ条件を使用すると、フィルタ条件が満たされた場合にのみ、ループ内のコードが実行されることが保証されます。これにより、プレイヤー数の最も少ないチームが見つかった場合、必ずそのチームがSmallestTeam
に設定されるようになります。プレイヤー数の少ないチームが見つからない場合は、SmallestTeam
が false のままになります。最後に、すべてのチームの確認が完了すると、
SmallestTeam
が返されます。for(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 SmallestTeam
-
OnBegin()
で、for
ループ内に「SmallestTeam
」という名前の新しいチームの オプション を作成し、引数としてTeamSize
が渡されるときにFindSmallestTeam()
の値に初期化します。SmallestTeam : ?team = FindSmallestTeam(TeamSize)
-
次に、
SmallestTeam
オプション変数の値へのアクセスを試みます。値が false の場合、プレイヤーはすでに最も小さいチームに所属しているため、それ以上の割り当ては必要ありません。その他の場合は、新しいチームにプレイヤーを割り当てる必要があります。 多くのメソッドでは、オプションを引数として直接渡すことができないため、値をローカル変数TeamToAssign
に抽出する必要があります。AddAgentToTeam[player, team]
を使用して、このチームへのプレイヤーの割り当てを試みることもできますが、このメソッドではプレイヤーをすでに所属しているチームに割り当てようとすると失敗することに注意してください。ただし、for
ループは単に次のプレイヤーをイテレートし、失敗したプレイヤーは元のチームに留まるため、悪い影響は発生しません。if (TeamToAssign := SmallestTeam?, GetPlayspace().GetTeamCollection().AddToTeam[TeamPlayer, TeamToAssign]): Print("Attempting to assign player to a new team")
-
-
OnBegin()
should look like the code block below.OnBegin<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}") SmallestTeam : ?team = FindSmallestTeam(TeamSize) if (TeamToAssign := SmallestTeam?, Playspace.GetTeamCollection().AddToTeam[TeamPlayer, TeamToAssign]): Print("Attempting to assign player to a new team")
ゲームの途中から参加するプレイヤーを処理する
進行中のゲームで、チームのバランスを自動調整できるようにするため、新しいプレイヤーが参加するときに発生するイベントをサブスクライブする必要があります。 それまでに書いたコードをすべて繰り返さなくて済むように、共通のメソッドにリファクタリングすることができます。
-
「
BalanceTeams()
」という名前のメソッドを作成し、GetTeams()
を使用してTeams
変数を設定した後、すべてのコードを移動します。これはOnBegin()
メソッドで呼び出されるため、ゲーム開始時のチームはバランスが取れています。BalanceTeams()
は次のようになります。BalanceTeams():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()
をサブスクライブするため、メソッド定義にはこれが必要です。サブスクライブ可能なイベントについての詳細は、「Coding Device Interactions (仕掛けのインタラクションをコーディングする)」ページを参照してください。OnPlayerAdded(InPlayer : player) : void = Print("A new Player joined, assigning them to a team!") BalanceTeams()
-
OnBegin()
で、OnPlayerAdded
を使用してPlayerAddedEvent()
をサブスクライブします。これで、プレイヤーがゲームに参加するとOnPlayerAdded
でBalanceTeams()
が呼び出され、自動的にチームのバランスが調整されます。OnBegin<override>()<suspends> : void = GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded) Print("Beginning to balance teams") BalanceTeams()
-
Visual Studio Code でスクリプトを保存し、[Build Verse Scripts (Verse スクリプトをビルド)] をクリックしてスクリプトをコンパイルします。
-
UEFN ツールバーの [Launch Session (セッションを起動)] をクリックし、レベルをプレイテストします。
レベルをプレイテストすると、各チームのサイズと、スクリプトで検出されたサイズの小さなチームが出力ログに出力されます。 プレイヤーはすべてのチームに均等に振り分けられる必要があり、ゲームに新しいプレイヤーが参加する場合も、この均等なバランスを保つ必要があります。

完全なスクリプト
次のコードは、チームに所属するプレイヤーのバランスを自動的に調整する仕掛けの完全なスクリプトです。
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /Fortnite.com/Devices}
using { /Verse.org/Simulation}
team_multiplayer_balance := class(creative_device):
# GetTeams() を使用して見つかったチームを保持します
var Teams:[]team = array{}
OnBegin<override>()<suspends> : void =
Print("Verse Device Started!")
set Teams = Self.GetPlayspace().GetTeamCollection().GetTeams()
AllPlayers := GetPlayspace().GetPlayers()
#新しいプレイヤーがゲームに参加したときにチームのバランスを再調整できるようにするため、PlayerAddedEvent をサブスクライブします
Self.GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
Print("Beginning to balance teams")
BalanceTeams()
#ゲームに参加した新しいプレイヤーを処理します
OnPlayerAdded(InPlayer : player) : void =
Print("A new Player joined, assigning them to a team!")
BalanceTeams()
<#
各プレイヤーについて、そのプレイヤーが所属するチームのプレイヤーの数を見つけます。 チームのリストをイテレートし、
それをプレイヤーの数が最も少ないチームに割り当てます。
同点の場合は開始チームに割り当てます。
#>
BalanceTeams():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")
FindSmallestTeam(CurrentTeamSize : int) : ?team =
var SmallestTeam : ?team = false
var TeamSize : int = CurrentTeamSize
<#
各チーム Team について、そのチームのプレイヤー数を取得します。 SmallestTeam よりもプレイヤーが少ない場合は、
SmallestTeam を Team に設定し、TeamSize を新しい Team のプレイヤー数に更新します
#>
for(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 SmallestTeam
応用編
このガイドを完了し、Verse を使用してチームに所属するプレイヤーのバランスを自動的に調整する仕掛けの作成方法を習得しました。
このガイドで学習した知識を活用し、1 人のプレイヤー対 4 人のプレイヤーのような非対称のゲーム モード向けに意図的にバランスの悪いチームを作成してみましょう。