完全なコード
以下は、プレイヤーのバランスを非対称にしてダイナミックなプレイ エクスペリエンスを作成する 3 チームの侵入ゲームの完全なコードです。
triad_infiltration_game.verse
using { /Fortnite.com/Devices}
using { /Fortnite.com/FortPlayerUtilities}
using { /Verse.org/Simulation}
using { /Verse.org/Random}
using { /UnrealEngine.com/Temporary/Diagnostics}
triad_infiltration_log_channel := class(log_channel){}
triad_infiltration := class(creative_device):
Logger:log = log{Channel := triad_infiltration_log_channel}
# プレイヤーがチームに参加できない事態を避けるため、
# すべての Maximum(Team) 変数の合計に設定する必要があります。
# Infiltrator チームの最大プレイヤー数。
@editable
MaximumInfiltrators:int = 2
# Attacker チームの最大プレイヤー数。
@editable
MaximumAttackers:int = 4
# Defender チームの最大プレイヤー数。
@editable
MaximumDefenders:int = 4
# ゲームが開始されると、チームのスポーンにプレイヤーをテレポートさせるテレポーターの配列。
@editable
Teleporters:[]teleporter_device = array{}
# Infiltrator の可視性を制御する invisibility_manager スクリプトへの参照。
@editable
InvisibilityManager:invisibility_manager = invisibility_manager{}
# 各チームのための武器グランターの配列。
@editable
var WeaponGranters:[]item_granter_device = array{}
# 各チームのプレイヤー スポナーの配列。
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Infiltrator チームへの参照。
var MaybeInfiltrators:?team = false
# Attacker チームへの参照。
var MaybeAttackers:?team = false
# Defender チームへの参照。
var MaybeDefenders:?team = false
# ゲーム内のすべてのチームの配列。
var AllTeams:[]team = array{}
# 最大プレイヤー数に対するチームのマップ。
var TeamsAndTotals:[team]int = map{}
OnBegin<override>()<suspends>:void =
# すべてのチームを取得します。
set AllTeams = GetPlayspace().GetTeamCollection().GetTeams()
var AllPlayers:[]player = GetPlayspace().GetPlayers()
# 後で参照できるようにチームを保存します。
set MaybeInfiltrators = option{AllTeams[0]}
set MaybeAttackers = option{AllTeams[1]}
set MaybeDefenders = option{AllTeams[2]}
if:
Infiltrators := MaybeInfiltrators?
Attackers := MaybeAttackers?
Defenders := MaybeDefenders?
Logger.Print("Found all three teams")
set TeamsAndTotals[Infiltrators] = MaximumInfiltrators
set TeamsAndTotals[Attackers] = MaximumAttackers
set TeamsAndTotals[Defenders] = MaximumDefenders
Logger.Print("Set all three teams in TeamsAndTotals")
then:
#新しいプレイヤーがゲームに参加したときにチームのバランスを再調整できるようにするため、PlayerAddedEvent をサブスクライブします。
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
BalanceTeams()
Logger.Print("Teams balanced, calling invisibility script")
InvisibilityManager.StartInvisibilityManager(AllTeams, AllPlayers, Infiltrators)
Sleep(0.25)
TeleportPlayersToStartLocations()
else:
Logger.Print("Couldn't find all teams, make sure to assign the correct teams in your island settings.")
# WeaponGranters 配列にインデックスを付けることにより、
# Teams 配列内のチームのインデックスに基づいてプレイヤーに武器を付与します。
GrantTeamWeapon(InPlayer:player):void=
if(CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[InPlayer]):
for(TeamIndex -> PlayerTeam:AllTeams, PlayerTeam = CurrentTeam):
if(WeaponGranter := WeaponGranters[TeamIndex]):
WeaponGranter.GrantItem(InPlayer)
Logger.Print("Granted the a Player on team {TeamIndex + 1} a weapon")
# プレイヤーがスポーン パッドからスポーンすると実行されます。
# 提供された SpawnedAgent を使用して GrantTeamWeapon を呼び出します。
OnPlayerSpawn(SpawnedAgent:agent):void=
if(SpawnedPlayer := player[SpawnedAgent]):
Logger.Print("Attempting to grant spawned player a weapon")
GrantTeamWeapon(SpawnedPlayer)
# ゲームに参加した新しいプレイヤーを処理します。
OnPlayerAdded(InPlayer:player):void=
Logger.Print("A new Player joined, assigning them to a team")
FortTeamCollection := GetPlayspace().GetTeamCollection()
# 新しいプレイヤーを最小のチームに非対称に割り当てます。
BalancePlayer(InPlayer)
for:
TeamIndex -> PlayerTeam:AllTeams
PlayerTeam = FortTeamCollection.GetTeam[InPlayer]
TeamTeleporter := Teleporters[TeamIndex]
Transform := TeamTeleporter.GetTransform()
do:
InPlayer.Respawn(Transform.Translation, Transform.Rotation)
Logger.Print("Teleported the spawned player to their start location")
# プレイヤーが Infiltrator だった場合、InvisibilityManager で OnInfiltratorJoined を
# 呼び出します。
if(PlayerTeam = MaybeInfiltrators?):
InvisibilityManager.OnInfiltratorJoined(InPlayer)
# ゲーム内のすべてのチームのすべてのプレイヤーのバランスを調整します
BalanceTeams():void=
Logger.Print("Beginning to balance teams")
var AllPlayers:[]player := GetPlayspace().GetPlayers()
set AllPlayers = Shuffle(AllPlayers)
Logger.Print("AllPlayers Length is {AllPlayers.Length}")
for (TeamPlayer : AllPlayers):
BalancePlayer(TeamPlayer)
# 各プレイヤーに対し、チームのリストをイテレートし、
# それをプレイヤーの数が最も少ないチームに割り当てます。また、同点の場合は開始チームに割り当てます。
BalancePlayer(InPlayer:player):void=
Logger.Print("Beginning to balance player")
var TeamToAssign:?team = false
set TeamToAssign = FindTeamWithLargestDifference()
if (AssignedTeam := TeamToAssign?, GetPlayspace().GetTeamCollection().AddToTeam[InPlayer, AssignedTeam]):
Logger.Print("Assigned player to a new team")
else:
Logger.Print("This player was already on the smallest team")
# 最大プレイヤー数と現在のプレイヤー数との差分が最も大きい
# チームを特定します。
FindTeamWithLargestDifference():?team =
Logger.Print("Attempting to find smallest team")
var TeamToAssign:?team = false
var LargestDifference:int = 0
for:
CandidateTeamIndex -> CandidateTeam:AllTeams
CurrentTeamSize := GetPlayspace().GetTeamCollection().GetAgents[CandidateTeam].Length
MaximumTeamSize := TeamsAndTotals[CandidateTeam]
do:
Logger.Print("Checking a team...")
Logger.Print("Maximum size of team {CandidateTeamIndex + 1} is {MaximumTeamSize}")
DifferenceFromMaximum := MaximumTeamSize - CurrentTeamSize
Logger.Print("Difference from maximum is {DifferenceFromMaximum}")
if(LargestDifference < DifferenceFromMaximum):
set LargestDifference = DifferenceFromMaximum
set TeamToAssign = option{CandidateTeam}
Logger.Print("Found team {CandidateTeamIndex + 1} with difference {DifferenceFromMaximum}")
return TeamToAssign
# チームのバランス調整後にプレイヤーをチームのスポーンにテレポートさせます。
TeleportPlayersToStartLocations():void=
Logger.Print("Teleporting players to start locations")
for:
TeamIndex -> PlayerTeam:AllTeams
TeamPlayers := GetPlayspace().GetTeamCollection().GetAgents[PlayerTeam]
TeamTeleporter := Teleporters[TeamIndex]
do:
for(TeamPlayer:TeamPlayers):
TeamTeleporter.Teleport(TeamPlayer)
Logger.Print("Teleported this player to their start location")
invisibility.verse
using { /Fortnite.com/Devices}
using { /Fortnite.com/Characters}
using { /Verse.org/Simulation}
using { /UnrealEngine.com/Temporary/Diagnostics}
triad_invisibility_log_channel := class(log_channel){}
invisibility_manager := class(creative_device):
Logger:log = log{Channel := triad_invisibility_log_channel}
# Infiltrator チームのプレイヤー スポナーの配列
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Infiltrator の可視性をチームメートと共有するかどうか。
@editable
IsVisibilityShared:logic = true
# Infiltrator がダメージを受けた後、どのくらいの時間視認できるか。
@editable
VulnerableSeconds:float = 3.0
# Infiltrator がダメージを受けた後に、Infiltrator がどれくらいの速さでちらつくか。
@editable
FlickerRateSeconds:float = 0.4
# ゲーム内のすべてのチームの配列。
var AllTeams:[]team = array{}
# ちらつきの残りの秒数に対するプレイヤーのマップ。
var PlayerVisibilitySeconds:[agent]float = map{}
OnBegin<override>()<suspends>:void=
# プレイヤーを不可視にするイベントにサブスクライブする前に、チームのバランスが整うまで待機します。
Logger.Print("Waiting for teams to be balanced...")
# Invisibility Manager のロジックを開始します。チームのバランス調整後に triad_infiltration クラスから呼び出されます
StartInvisibilityManager<public>(GameTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Invisibility script started!")
set AllTeams = GameTeams
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# 各プレイヤーについて、Infiltrator チームでスポーンした場合は、そのプレイヤーの OnInfiltratorDamaged 関数をスポーンします。
# player.次に、キャラクターを不可視にします。
for(TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam:team := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Got this player's current team")
Infiltrators = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Added player to PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Player spawned as an infiltrator, making them invisible")
FortCharacter.Hide()
else:
Logger.Print("This player isn't an infiltrator)")
# fort_character の非表示と表示を繰り返し、エージェントの表示状態をちらつかせます
FlickerCharacter(InCharacter:fort_character)<suspends>:void=
Logger.Print("FlickerCharacter() invoked")
# キャラクターの非表示と表示をループして、ちらつきエフェクトを作成します。
loop:
InCharacter.Hide()
Sleep(FlickerRateSeconds)
InCharacter.Show()
Sleep(FlickerRateSeconds)
# 各ループで、キャラクターがちらつく時間を FlickerRateSeconds の値だけ減少させます。
#残り時間が 0 になった場合、ループから抜け出します。
if:
TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2
TimeRemaining <= 0.0
then:
InCharacter.Hide()
break
# ダメージを受けたときにエージェントをちらつかせます
OnInfiltratorDamaged(InAgent:agent)<suspends>:void=
Logger.Print("Attempting to start flickering this character")
TeamCollection := GetPlayspace().GetTeamCollection()
if(FortCharacter := InAgent.GetFortCharacter[]):
loop:
if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]):
# チームメイトごとに、PlayerVisibility 秒単位で設定し、FlickerEvent をスポーンします。
for(Teammate:TeamAgents):
Logger.Print("Calling StartOrResetFlickering on a Teammate")
StartOrResetFlickering(Teammate)
else:
# ダメージを受けたキャラクターのみをちらつかせます。
Logger.Print("Calling StartOrResetFlickering on InAgent")
StartOrResetFlickering(InAgent)
FortCharacter.DamagedEvent().Await()
# エージェントが非表示の場合は新しいちらつきイベントを開始し、それ以外の
# 場合は、エージェントの進行中のちらつきをリセットします。
StartOrResetFlickering(InAgent:agent):void=
if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]):
Logger.Print("Attempting to start NEW FlickerEvent for this character")
# 新しいちらつきが開始しました。
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
spawn{FlickerCharacter(FortCharacter)}
Logger.Print("Spawned a FlickerEvent for this character")
else:
# 進行中のちらつきをリセットします。
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
Logger.Print("Reset character's FlickerTimer to VulnerableSeconds")
# プレイヤーにちらつきの時間が残っているかどうかを返します
IsFlickering(InAgent:agent)<decides><transacts>:void=
PlayerVisibilitySeconds[InAgent] > 0.0
# 新しい Infiltrator がゲームに参加すると、OnInfiltratorDamaged 関数をスポーンします
OnInfiltratorJoined<public>(InAgent:agent):void=
spawn{OnInfiltratorDamaged(InAgent)}
# Infiltrator スポーン パッドからプレイヤーがスポーンするのを処理します
OnPlayerSpawn(SpawnedAgent:agent):void=
Logger.Print("A player just spawned from an infiltrator spawn pad!")
if:
FortCharacter:fort_character = SpawnedAgent.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[SpawnedAgent]
AllTeams[0] = CurrentTeam
Logger.Print("Player spawned as an infiltrator, making them invisible")
then:
FortCharacter.Hide()
item_capture_manager.verse
using { /Fortnite.com/Devices}
using { /Verse.org/Simulation}
using { /Fortnite.com/Characters}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/SpatialMath}
triad_item_capture_log_channel := class(log_channel){}
item_capture_manager := class(creative_device):
Logger:log = log{Channel := triad_item_capture_log_channel}
# キャプチャーするためのアイテムをスポーンするアイテム スポナーをキャプチャーします。
@editable
CaptureItemSpawner:capture_item_spawner_device = capture_item_spawner_device{}
# CaptureItemSpawner のアイテムを保持している場合に、プレイヤーの頭上に浮かぶ小道具。
# 頭上に浮かぶ小道具。
@editable
CaptureItemIndicator:creative_prop = creative_prop{}
# 各チームの目標があるマップに表示されるインジケータ。
@editable
MapIndicator:map_indicator_device = map_indicator_device{}
# CaptureItemIndicator がその位置を更新する頻度。
@editable
UpdateRateSeconds:float = 0.15
# CaptureItemIndicator がプレイヤーの頭上を浮かぶ高さ。
@editable
VerticalOffset:float = 180.0
# プレイヤーがキャプチャー アイテムを取得すると、メッセージを表示します。
@editable
ItemGrabbedMessageDevice:hud_message_device = hud_message_device{}
# キャプチャー アイテムおよびマップ インジケータを戻す前の待機時間。
# 負の時間は、目標のアイテムが再度ピックアップされない限りインジケータが戻らないことを
# 示します。
@editable
ReturnTime:float = 10.0
# プレイヤーがキャプチャー アイテムを取得すると、スコアを付与します。
@editable
ScoreManagerDevice:score_manager_device = score_manager_device{}
OnBegin<override>()<suspends>:void=
CaptureItemSpawner.ItemPickedUpEvent.Subscribe(OnItemPickedUp)
CaptureItemSpawner.ItemCapturedEvent.Subscribe(OnItemCaptured)
CaptureItemSpawner.ItemDroppedEvent.Subscribe(OnItemDropped)
SpawnerTransform := CaptureItemSpawner.GetTransform()
# テレポートしてスポナーに戻り、CaptureItemIndicator をマップの下の視野外で非表示にします。
CaptureItemIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)
MapIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)
Logger.Print("Returned Beacon to capture spawner")
# プレイヤーが目標のアイテムを取得すると、各プレイヤーに信号を送信します。
OnItemPickedUp(InAgent:agent):void=
Logger.Print("Objective Grabbed")
if(FortCharacter := InAgent.GetFortCharacter[]):
ItemGrabbedMessageDevice.Show()
spawn{FollowCharacter(FortCharacter)}
# プレイヤーがアイテムをドロップすると、ReturnTime が 0 より大きい場合に
# WaitForReturn() 関数をスポーンします。
OnItemDropped(InAgent:agent):void=
Logger.Print("Objective Dropped")
if(ReturnTime >= 0.0):
spawn{WaitForReturn()}
else:
Logger.Print("The dropped objective does not return")
# アイテムがキャプチャーされると、キャプチャーしているチームにスコアが付与され、インジケータを戻します。
OnItemCaptured(CapturingAgent:agent):void=
Logger.Print("Objective Captured")
ScoreManagerDevice.Activate()
ReturnIndicators()
# ReturnTime の時間数だけ待機し、インジケータを戻します。
WaitForReturn()<suspends>:void=
Logger.Print("Waiting t return the indicators...")
# 時間切れになる前にキャプチャー アイテムがピックアップされなかった場合、
# CaptureItem とマップ インジケータを戻します。
ShouldReturn:logic := race:
block:
Sleep(ReturnTime)
true
block:
CaptureItemSpawner.ItemPickedUpEvent.Await()
false
if(ShouldReturn?):
ReturnIndicators()
# CaptureItemIndicator がプレイヤーの頭上を継続的に追跡するようにします。
# CaptureItemIndictator の更新ループ、およびプレイヤーがアイテムをキャプチャするか、
# ドロップするか、撃破されるかの間でレースを行います。
FollowCharacter(FortCharacter:fort_character)<suspends>:void=
Logger.Print("Spawned FollowCharacter function")
race:
loop:
Transform := FortCharacter.GetTransform()
spawn{CaptureItemIndicator.MoveTo(Transform.Translation + vector3{Z := VerticalOffset}, Transform.Rotation, UpdateRateSeconds)}
spawn{MapIndicator.MoveTo(Transform.Translation + vector3{Z := VerticalOffset}, Transform.Rotation, UpdateRateSeconds)}
# このループは、シミュレーションの更新ごとに一度だけループが実行されるようにするため、ゲーム ティック 1 つ分だけスリープを行います。
Sleep(0.0)
CaptureItemSpawner.ItemCapturedEvent.Await()
CaptureItemSpawner.ItemDroppedEvent.Await()
FortCharacter.EliminatedEvent().Await()
Logger.Print("Objective dropped or captured")
# マップおよびキャプチャー アイテム インジケータをスポナーの上の初期位置に戻します。
ReturnIndicators():void=
SpawnerTransform := CaptureItemSpawner.GetTransform()
# テレポートしてスポナーに戻り、CaptureItemIndicator および MapIndicator はマップの上の視野外で非表示にします。
spawn{CaptureItemIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)}
spawn{MapIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)}
Logger.Print("Returned Indicators to capture spawner")
応用編
このガイドはこれで終わりです。これで、Verse を使用して、プレイヤーのチームのバランスを非対称にするゲームを作成する方法がわかったと思います。
このガイドで学習した知識を活用して、次の項目を実行してみましょう。
- 理想的なプレイ体験を作成するため、Infiltrator、Attacker、Defender のさまざまなパラメータを試してみてください。Infiltrator が近接武器を持っている状況や、Defender が透明になっている状況を検討してみてください。
- Infiltrator と Attacker の目的を同じにして戦わせたり、勝利条件を変更したりしてみてください。