このセクションには、自分が作成した Verse ファイルに追加できる完全なコードが含まれています。
完全なコード
このプロジェクトには複数の Verse ファイルがあります。
-
heartbeat.verse:ファイルの完全なコードについては、以下を参照してください。
-
base_team.verse:ファイルの完全なコードについては、以下を参照してください。
-
hunter_team.verse:ファイルの完全なコードについては、以下を参照してください。
-
prop_team.verse:ファイルの完全なコードについては、以下を参照してください。
-
round_timer.verse:ファイルの完全なコードについては、以下を参照してください。
-
waiting_for_more_players.verse:ファイルの完全なコードについては、以下を参照してください。
heartbeat.verse
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices}
using { /Fortnite.com/UI}
using { /UnrealEngine.com/Temporary/SpatialMath}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/UI}
using { /Verse.org/Colors}
using { /Verse.org/Simulation}
log_heart_beat := class(log_channel){}
# これらのメッセージは、ハートビートが表示されないように移動する必要があるときに、メッセージで小道具エージェントに通知する (またはメッセージを非表示にする) ために使用されます。
HeartBeatWarningMessage<localizes>(Time:int):message = "Heart Beat in {Time} Seconds. Move!"
HeartBeatWarningClear<localizes>:message = ""
# このクラスでは prop_hunt の仕掛けにハートビートの編集可能なプロパティを公開しました。
heart_beat := class<concrete>():
Logger:log = log{Channel:=log_heart_beat}
@editable # 小道具エージェントがハートビートによって自分の位置が明らかになる前に移動しなければならない秒数です。
MoveTime:float = 15.0
@editable # ハートビート警告が表示されるまでの残り秒数です。HeartBeatTimer を超えることはできません。
WarningTime:float = 5.0
@editable # ハートビート VFX の仕掛けの配列です。プレイヤー 1 人につき 1 つの仕掛けがあります。
AgentVFX:[]heartbeat_vfx = array{}
@editable # ハートビート サウンド エフェクト (SFX) を再生するために使用するオーディオ プレーヤーの仕掛けです。
SFXPlayer:radio_device = radio_device{}
# このマップは、ハートビート警告を表示する UI を各小道具エージェントに関連付けます。
var WarningUI:[agent]heartbeat_warning_ui = map{}
#SFX の仕掛けを管理できるように、ハートビートがアクティブになっているプレイヤー数を追跡します。
var NumberOfHeartBeats:int = 0
# エージェントにハートビート UI を設定します。
SetUpUI(PropAgent:agent):void =
if:
not WarningUI[PropAgent]
AsPlayer := player[PropAgent]
PlayerUI := GetPlayerUI[AsPlayer]
then:
UIData:heartbeat_warning_ui = heartbeat_warning_ui{}
UIData.CreateCanvas()
PlayerUI.AddWidget(UIData.Canvas, player_ui_slot{ZOrder := 1})
if (set WarningUI[PropAgent] = UIData) {}
#指定されたプレイヤーのハートビート VFX および SFX をアクティブ化します。
Enable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
if:
# キャラクターを取得します。これは、シーン内で小道具エージェントの位置を見つけるために使用されます。
Character := PropAgent.GetFortCharacter[]
then:
#ハートビート VFX の位置を小道具エージェントの位置に設定します。
HeartBeatVFXData.Activate(Character.GetTransform())
# ハートビートのカウントを増やし、最初のハートビートの再生である場合は、オーディオを再生してハートビートを開始する必要があります。
set NumberOfHeartBeats += 1
if (NumberOfHeartBeats = 1) then SFXPlayer.Play()
# 小道具エージェントをオーディオ プレーヤーの仕掛けに登録し、ハートビートのオーディオがその位置から再生されるようにします。
SFXPlayer.Register(PropAgent)
else:
Logger.Print("Character, Index, or HeartBeatVFXData not found.Cannot start the heartbeat")
#指定した小道具エージェントのハートビート VFX および SFX をクリアします。
Disable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
Logger.Print("Disabling heart beat.")
#VFX ビジュアルを無効にします。
HeartBeatVFXData.Deactivate()
# オーディオ プレーヤーの仕掛けから小道具エージェントの登録を解除し、ハートビート オーディオを停止させます。
SFXPlayer.Unregister(PropAgent)
# ハートビートのカウンターを減らします。このカウンターが 0 を下回ることはありません。
set NumberOfHeartBeats -= 1
if (NumberOfHeartBeats < 0) then set NumberOfHeartBeats = 0
#すべての小道具エージェントのすべてのハートビート VFX および SFX をクリアします。
DisableAll():void =
Logger.Print("Disabling all heart beats.")
#すべての VFX をイテレートし、0,0,0 に移動させます。
for (HeartBeatVFXDevice : AgentVFX):
HeartBeatVFXDevice.Deactivate()
# ハートビート オーディオから全プレイヤーを登録解除します。
SFXPlayer.UnregisterAll()
#ハートビート カウンターを 0 に再初期化します。
set NumberOfHeartBeats = 0
# heartbeat_warning_ui クラスには、プレイヤーごとの UI キャンバスおよび text_block を追跡するデータの構造体と、新しいハートビートの警告 UI キャンバスを作成する関数が含まれています。
heartbeat_warning_ui := class:
var Canvas:canvas = canvas{}
var Text:text_block = text_block{}
# 警告メッセージの UI キャンバスを作成します。
CreateCanvas():void =
set Text = text_block{DefaultTextColor := NamedColors.White, DefaultShadowColor := NamedColors.Black}
set Canvas = canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.75}, Maximum := vector2{X := 0.5, Y := 0.75}}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := vector2{X := 0.5, Y := 1.0}
SizeToContent := true
Widget := Text
#heartbeat_vfx クラスには、プレイヤーごとの VFX のルート オブジェクトおよび vfx_spawner_device オブジェクトを追跡するためのデータの構造体と、VFX を特定の位置に設定したり、リセットしたりする関数が含まれています。
heartbeat_vfx := class<concrete>:
@editable # 各ハートビートの VFX の仕掛けです。
VFXDevice:vfx_spawner_device = vfx_spawner_device{}
# このオフセットは、ハートビートを小道具エージェントの頭上に配置するために使用されます。
HeartBeatVFXOffset:vector3 = vector3{X := 0.0, Y := 0.0, Z := 110.0}
#ハートビート VFX の位置を設定し、VFX を有効にします。
Activate(Transform:transform):void =
VFXPosition := Transform.Translation + HeartBeatVFXOffset
if (VFXDevice.TeleportTo[VFXPosition, Transform.Rotation]):
VFXDevice.Enable()
#VFX を無効にし、ハートビートのビジュアルを非表示にします。
Deactivate():void =
VFXDevice.Disable()
base_team.verse
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices}
using { /Fortnite.com/UI}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/SpatialMath}
using { /UnrealEngine.com/Temporary/UI}
using { /Verse.org/Colors}
using { /Verse.org/Simulation}
log_team := class(log_channel){}
# このクラスは、体験中にさまざまなチームに必要な仕掛けを定義します。
# このクラスは抽象クラスであるため、単独で使用することはできません。他のクラスによって継承される必要があります。
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # プレイヤーをチームに設定するために使用します。
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # チームのエージェントにスコアを付与するために使用します。
ScoreManager:score_manager_device = score_manager_device{}
@editable # チームの任務のタイトルを表示するために使用します。
TeamTitle:hud_message_device = hud_message_device{}
@editable # チームの任務の説明を表示するために使用します。
TeamDescription:hud_message_device = hud_message_device{}
@editable # チーム メンバー (小道具チーム) または敵 (ハンター チーム) が撃破したイベントをサブスクライブするために使用します。
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# これはチームでのエージェントの配列です。
var TeamAgents<private>:[]agent = array{}
# このイベントは、TeamAgents 配列が空になったときに通知されます (ラウンドの終了を示す)。
TeamEmptyEvent:event() = event(){}
# 現在の TeamAgents 配列を返します。
# これは必須です。TeamAgents 配列はプライベートであり、他のクラスが直接アクセスできないためです。
GetAgents()<decides><transacts>:[]agent =
TeamAgents
# TeamAgents 配列のサイズを返します。
# これには、関数が必要です。TeamAgents 配列はプライベートであり、他のクラスが直接アクセスできないためです。
Count()<transacts>:int =
TeamAgents.Length
# エージェントの TeamAgents 配列内のインデックスを返します。それ以外の場合は失敗します。
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# エージェントをチームに設定し、プレイヤーに通知します。
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# エージェントを TeamAgents に追加します。
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Adding agent to team.")
set TeamAgents += array{AgentToAdd}
#HUD メッセージの仕掛けをアクティブ化し、プレイヤーが所属するチームを表示します。
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# エージェントがマッチから退出するときに、TeamAgents 配列からエージェントを削除し、ラウンドが終了するかどうかを確認します。
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # 1 ゲーム ティック遅延させることで、進む前にプレイヤーが確実にリスポーンされるようにします。
RemoveAgentFromTeam(Agent)
# TeamAgents からエージェントを削除します。
# 削除されたエージェントが最後のエージェントだった場合、TeamEmptyEvent を通知します。
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("{Count()} agent(s) on team remaining.")
if (Count() < 1):
Logger.Print("No agents on team remaining.Ending the round.")
TeamEmptyEvent.Signal()
hunter_team.verse
using { /Fortnite.com/Devices}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /Verse.org/Simulation}
# base_team クラスを継承する hunter_team クラスには、ハンター チームとそのエージェントに関連する仕掛けの定義と関数が含まれています。
hunter_team := class<concrete>(base_team):
@editable # n 人のプレイヤー対して 1 人のハンター エージェントがラウンドごとに作成されます。例:HunterTeamPerNumberOfPlayers = 5.0 は、5 人のプレイヤーにつきハンターが 1 人です。プレイヤーが 6 人の場合は、ハンター エージェントが 2 人作成されます。
HunterAgentPerNumberOfPlayers:float = 5.0 # 少なくとも 1 人の小道具エージェントが作成されるように、1.1 以上が強制的に適用されます。
@editable # ハンター エージェントがスポーンされるまでの秒数。小道具エージェントに隠れるための時間を提供します。
SpawnDelay:float = 15.0
@editable # ハンター エージェントが小道具エージェントを撃破したときに得られる最大基本ポイントです。これらのポイントは、残りの小道具エージェントの数で除算されます。
MaxEliminationScore:int = 5000
@editable # タイマーの仕掛けは小道具に隠れるための猶予を与えるために使用されます。
WaitTimer:timer_device = timer_device{}
# エージェントをハンター エージェントに設定します。
InitializeAgent<override>(NewHunterAgent:agent):void =
Logger.Print("Setting a new hunter agent.")
(super:)InitializeAgent(NewHunterAgent)
# ハンター エージェントがマッチを退出するときに、HunterAgents 配列からエージェントを削除し、ラウンドが終了するかどうかを確認します。
# なお、この関数をオーバーライドしているのは、小道具チームのように追加のデータを渡す必要がないためです。
EliminateAgent<override>(HunterAgent:agent)<suspends>:void =
Logger.Print("Hunter agent eliminated.")
(super:)EliminateAgent(HunterAgent)
prop_team.verse
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices}
using { /Fortnite.com/UI}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/SpatialMath}
using { /UnrealEngine.com/Temporary/UI}
using { /Verse.org/Colors}
using { /Verse.org/Simulation}
# このメッセージは、ラウンド中に全プレイヤーに残りの小道具の数を表示するために使用されます。
PropAgentsRemainingMessage<localizes>(Count:int):message = "{Count} Prop(s) Remaining"
# base_team クラスを継承する prop_team クラスには、小道具チームとそのエージェントに関連する仕掛けの定義と関数が含まれています。
# 特に、小道具エージェントのハートビートの動作はこのクラス内に記述されています。
prop_team := class<concrete>(base_team):
@editable # 小道具エージェントが 1 秒間に受け取るスコアです。
ScorePerSecond:int = 10
@editable # 小道具エージェントがハートビート タイマーをリセットするために移動しなければならない最小距離です。
MinimumMoveDistance:float = 100.0
@editable # 小道具にスコアを付与するために使用されるタイマーの仕掛けです。
ScoreTimer:timer_device = timer_device{}
@editable # この Tracker の仕掛けは、小道具の残りを画面に表示するために使用されます。
PropsRemainingTracker:tracker_device = tracker_device{}
@editable # heart_beat クラスからプロパティを取得します
HeartBeat:heart_beat = heart_beat{}
# エージェントを小道具エージェントに設定し、ハートビート警告 UI を割り当てます。
InitializeAgent<override>(NewPropAgent:agent):void =
Logger.Print("Setting a new prop agent.")
(super:)InitializeAgent(NewPropAgent)
# PropScoreTimer が完了したら、すべての小道具エージェントにポイントを付与します。PropInstigator はイベントのサブスクリプションには必要であるものの、使用されません。
OnPropsScore(PropInstigator:?agent):void =
if (PropAgents := GetAgents[]):
for (PropAgent : PropAgents):
ScoreManager.Activate(PropAgent)
# 小道具エージェントが撃破されるか、マッチから退出するときに、PropAgents 配列からエージェントを削除し、ラウンドが終了するかを確認します。
# なお、これをオーバーライドしないのは、関数にすべてのプレイヤーを渡して、残りの小道具のメッセージを更新するためです。
EliminateAgent<override>(PropAgent:agent)<suspends>:void =
Logger.Print("Prop agent eliminated.")
(super:)EliminateAgent(PropAgent)
# 残りの小道具の数を更新します。
UpdatePropsRemainingTracker()
# 残りの小道具の数を示す Tracker の仕掛けの値を更新します。
UpdatePropsRemainingTracker():void =
PropsRemainingTracker.SetValue(Count())
# 小道具エージェントが移動を停止した場合、小道具エージェントが MinimumMoveDistance を超えて移動するか、ハートビートタイマーが完了するか、小道具エージェントが撃破されるかの、いずれが先に起こるかを確認します。
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Starting prop agent game loop.")
# 小道具エージェントが撃破されるか、プレイヤーがセッションを退出するまで、小道具の動作を永続的にループします。
race:
PropAgent.AwaitNoLongerAProp()
loop:
# 小道具エージェントの移動距離が最小移動距離を下回るまで待機してから、先に進みます。
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# 小道具エージェントの移動距離が最小移動距離を超えるまで、ハートビートまでのカウントダウンを実行し、ハートビートを無期限に再生します。
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # レースが完了したら (小道具エージェントが移動したら)、再びループを開始します。
# 小道具エージェントが PropAgents 配列の一部でなくなるまでループします。小道具エージェントが撃破されてハンターになるか、プレイヤーがセッションから退出すると削除が実行されます。
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[PropAgent]):
Logger.Print("Cancelling prop agent behavior.")
break
Sleep(0.0) # 次のゲーム ティックに進みます。
# エージェントの移動が MinimumDistance 未満になるまでループします。
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Checking if the agent has moved less than the minimum distance.")
# シーン内のエージェントのキャラクターからエージェントの初期位置を取得します。
if (Tracked := PropAgent.GetFortCharacter[]):
var StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # 次のゲーム ティックでのエージェントの位置を取得します。
NewPosition := Tracked.GetTransform().Translation
# 開始位置からの新しい位置の距離が MinimumDistance 未満である場合は、エージェントは移動していないため、ループを抜けます。
if (Distance(StartPosition, NewPosition) < MinimumDistance):
Logger.Print("Agent has moved less than the minimum distance.")
break
# その他の場合は、StartPosition をリセットして、プレイヤーの新しい位置からの移動を確認します。
else:
set StartPosition = NewPosition
# エージェントが MinimumDistance を上回る距離を移動するまでループします。
(PropAgent:agent).AwaitStartMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Checking if the agent moves further than the minimum distance.")
# シーン内のエージェントのキャラクターからエージェントの初期位置を取得します。
if (Tracked := PropAgent.GetFortCharacter[]):
StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # 次のゲーム ティックでのエージェントの位置を取得します。
NewPosition := Tracked.GetTransform().Translation
# 開始位置からの新しい位置の距離が MinimumDistance 以上である場合、エージェントは移動しているため、ループを抜けます。
if (Distance(StartPosition, NewPosition) >= MinimumDistance):
Logger.Print("Agent has moved more than or equal to the minimum distance.")
break
# HeartBeatWarningTime が開始するまで遅延します。その後、HeartBeatWarningTime でカウントダウンし、カウントダウン テキストを設定します。遅延時は、テキストをクリアします。
CountdownTimer(PropAgent:agent)<suspends>:void =
Logger.Print("Starting heart beat countdown.")
if (UIData := HeartBeat.WarningUI[PropAgent]):
Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # 警告が表示されるまでの期間スリープします。
Logger.Print("Starting heart beat warning.")
var WarningTimeRemaining:int = 0
if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {}
# defer は、関数が完了したときや、レースに負けた場合など、関数がキャンセルされたときに実行されます。
# そのため、この場合、カウントダウンが終了するか、カウントダウンが終了する前に小道具エージェントが移動すると、警告テキストはクリアされます。
defer:
UIData.Text.SetText(HeartBeatWarningClear)
# 警告テキストを残り時間に設定し、1 秒待機してから残り時間を減らします。カウントダウンが完了したら、ループを抜けます。
loop:
Logger.Print("Heart beat in {WarningTimeRemaining} seconds.")
UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining))
Sleep(1.0)
set WarningTimeRemaining -= 1
if (WarningTimeRemaining <= 0):
break
else:
Logger.Print("UIData not found.")
#ハートビート VFX および SFX を有効にします。遅延するまで無期限に待機し、ハートビート VFX および SFX を無効にします。
(PropAgent:agent).StartHeartbeat()<suspends>:void =
Logger.Print("Spawning heart beat.")
# ハートビート データを保存して、小道具エージェントが破壊されたりゲームから退出したりした後に、defer でこのデータを渡せるようにします。
var HeartBeatVFXData:heartbeat_vfx = heartbeat_vfx{}
if:
#PropAgents 配列で小道具エージェントのインデックスを取得し、対応するハートビート VFX アクタにアクセスします。
Index := FindOnTeam[PropAgent]
set HeartBeatVFXData = HeartBeat.AgentVFX[Index]
then:
HeartBeat.Enable(PropAgent, HeartBeatVFXData)
# 小道具エージェントが移動したり、撃破されたり、プレイヤーがセッションから退出したりしてこの関数がキャンセルされたら、ハートビートを無効にします
defer:
HeartBeat.Disable(PropAgent, HeartBeatVFXData)
Sleep(Inf) # レースが完了するまでスリープを停止しないようにします。
round_timer.verse
using { /Fortnite.com/Devices}
using { /Fortnite.com/UI}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/SpatialMath}
using { /UnrealEngine.com/Temporary/UI}
using { /Verse.org/Colors}
using { /Verse.org/Simulation}
log_round_timer_device := class(log_channel){}
# 指定された範囲の値を受け入れる int です。この型は player_ui_slot.ZOrder に必要です。
round_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# このメッセージはラウンド終了までの残り時間を表示するために使用されます。
TimeRemainingMessage<localizes>(Minutes:string, Seconds:string):message = "{Minutes}:{Seconds}"
<#
このクラスには、ラウンド時間を管理し、時間を画面に表示するためのすべてのロジックが含まれています。
この仕掛けを round_settings_device と一緒に使用することで、実際にラウンドを終了させることができます。
この仕掛けはタイマーを使用しないで、時間を管理します。
このクラスを使用するには、次の手順を実行します。
1) プロジェクトにファイルを追加します。
2) ツールバーの [Verse] メニューで Verse コードをコンパイルします。
3) コンテンツ ブラウザの「Content/Creative Devices」フォルダから仕掛けを島にドラッグします。
4) 以下を使用して、別の Verse スクリプトに waiting_for_more_players クラスをインクルードします。
@editable
RoundTimer:round_timer = round_timer{}
5) ツールバーの [Verse] メニューで Verse コードをコンパイルします。
6) ステップ 3 で作成した仕掛けを Verse の仕掛けに接続します。
7) 以下の Verse を使用してラウンド タイマーを開始します。
RoundTimer.Start()
8) 同等の関数を使用して、タイマーを再起動または停止します。
9) 以下を使用して、タイマーの開始を待機します。
RoundTimer.AwaitStart()
10) 以下を使用して、タイマーの完了を待機します。
RoundTimer.AwaitEnd()
実際にゲーム ラウンドを終了するには、round_settings_device で EndRound 関数を呼び出します。
#>
round_timer := class(creative_device):
Logger:log = log{Channel:=log_prop_hunt_device}
@editable # ラウンドの持続時間 (分単位) です。
RoundTimeInMinutes:float = 5.0
@editable # 画面上のタイマー UI の水平位置と垂直位置です。X 0-1 は左右で、Y 0-1 は上下です。
UIPosition:vector2 = vector2{X:= 0.98, Y:=0.13}
@editable # 画面上のタイマー UI の水平位置と垂直位置です。X 0-1 は左右で、Y 0-1 は上下です。
UIAlignment:vector2 = vector2{X := 1.0, Y := 0.0}
@editable # 他の UI 要素と比較した UI の ZOrder です。
UIZOrder:round_int_clamped = 4
# ラウンドが開始されたときに通知します。
RoundStarted:event() = event(){}
# ラウンドがまもなく終了するときに通知します。
RoundEndedEvent:event() = event(){}
# このマップは、各プレイヤーに時間を表示するためのテキスト ボックスを関連付けます。
var TimeRemainingTextBlocks:[player]text_block = map{}
# ラウンドが終了するまでの残り時間 (整数) です。
var TimeRemainingInSeconds:int = 0
# ラウンド タイマーが開始するまで待機します。
AwaitStart()<suspends>:void =
RoundStarted.Await()
Logger.Print("Round timer started.")
# ラウンド タイマーを開始するために使用します。
Start():void =
Logger.Print("Starting the round timer.")
RoundStarted.Signal()
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
spawn{ Running() }
# タイマーを RoundTime まで再スタートします。
Restart():void =
Logger.Print("Restarting the round timer.")
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
# タイマー ロジックを実行します。
Running()<suspends>:void =
Logger.Print("Round timer running.")
loop:
UpdateTimeUI()
Sleep(1.0)
#TimeRemaining を 1 秒ずつ減らし、時間切れかどうかを確認します。時間切れの場合は、ラウンドを終了します。
set TimeRemainingInSeconds -= 1
if (TimeRemainingInSeconds < 0):
Stop()
break
# タイマーを停止し、ラウンドを終了します。
Stop():void =
Logger.Print("Ending the round timer.")
# シーンに残っているプレイヤーの中からラウンドを終了させるプレイヤーを取得します。
Players:[]player = GetPlayspace().GetPlayers()
if (Instigator := Players[0]):
RoundEndedEvent.Signal()
# ラウンド タイマーがまもなく終了するまで待機します。
AwaitEnd()<suspends>:void =
RoundEndedEvent.Await()
Logger.Print("Round timer ended.")
# 分単位の時間値を受け入れ、秒単位の値を返します。
GetRoundTimeInSeconds():int =
var InSeconds:int = 0
if (set InSeconds = Round[RoundTimeInMinutes * 60.0]) {}
InSeconds
# タイマーが終了したら、残り時間を更新し、時間切れかどうかを確認します。
UpdateTimeUI():void =
#Minutes を TimeRemainingInSeconds/60 の剰余を除いた値に設定します。
var Minutes:int = 0
if (set Minutes = Floor(TimeRemainingInSeconds / 60)) {}
#Seconds を TimeRemainingInSeconds/60 の剰余に設定します。
var Seconds:int = 0
if (set Seconds = Mod[TimeRemainingInSeconds, 60]) {}
# 分と秒を文字列に変換します。
MinutesAsString := string("{Minutes}")
#Seconds < 10 の場合は、値の前に 0 を追加して、:# ではなく :0# と表示されるようにする必要があります。
SecondsAsString := if (Seconds < 10) then Join(array{string("{0}"),string("{Seconds}")},string()) else string("{Seconds}")
# すべてのプレイヤーをイテレートし、TimeRemainingTextBlock を含むかどうかを確認して、含んでいない場合は、提供します。次に、テキストを更新します。
Players:[]player = GetPlayspace().GetPlayers()
for (Player : Players):
var TextBlock:text_block = text_block{}
if (set TextBlock = TimeRemainingTextBlocks[Player]) {}
else:
set TextBlock = SetUpTimeRemainingUI(Player)
TextBlock.SetText(TimeRemainingMessage(MinutesAsString, SecondsAsString))
# プレイヤーを受け入れ、ラウンド時間 UI キャンバスを画面に追加し、後で更新できるように TimeRemainingTextBlock を格納します。
SetUpTimeRemainingUI(Player:player):text_block =
Logger.Print("Adding round timer UI to a player.")
# これは残り時間のテキストを画面に表示する text_block です。
TextBlock:text_block = text_block:
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
if (PlayerUI := GetPlayerUI[Player]):
if (set TimeRemainingTextBlocks[Player] = TextBlock) {}
# これは、text_block を保持し、画面上に配置するキャンバスです。
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# キャンバスがプレイヤーに割り当てられます。
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# text_block がマップに保存され、時間の経過とともに更新されるようにするため、text_block が返されます。
return TextBlock
waiting_for_more_players.verse
using { /Fortnite.com/Devices}
using { /Fortnite.com/UI}
using { /UnrealEngine.com/Temporary/Diagnostics}
using { /UnrealEngine.com/Temporary/SpatialMath}
using { /UnrealEngine.com/Temporary/UI}
using { /Verse.org/Colors}
using { /Verse.org/Simulation}
log_waiting_for_more_players_device := class(log_channel){}
# 指定された範囲の値を受け入れる int です。この型は player_ui_slot.ZOrder に必要です。
waiting_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# このメッセージは、ラウンドを開始する前に必要なプレイヤーの数を表示するために使用されます。
WaitingForMorePlayersMessage<localizes>(Count:int):message = "Waiting for {Count} more Player(s)"
# これは、ラウンドを開始するために必要なプレイヤーの数を表示するためのクラスです。
waiting_for_more_players_ui := class:
var Canvas:canvas
var TextBlock:text_block
<#
このクラスには、最小プレイヤー数を設定し、ラウンドを開始するのに十分な人数がいるかどうかを確認するためのすべてのロジックが含まれています。
このクラスを使用するには、次の手順を実行します。
1) プロジェクトにファイルを追加します。
2) ツールバーの [Verse] メニューで Verse コードをコンパイルします。
3) コンテンツ ブラウザの「Content/Creative Devices」フォルダから仕掛けを島にドラッグします。
4) タイマーの仕掛けをこの仕掛けの「WaitingForMorePlayersTimer 」プロパティに接続します。
5) 以下を使用して、別の Verse スクリプトに waiting_for_more_players クラスをインクルードします。
@editable
WaitingForMorePlayers:waiting_for_more_players = waiting_for_more_players{}
6) ツールバーの [Verse] メニューで Verse コードをコンパイルします。
7) ステップ 3 で作成した仕掛けをステップ 6 で公開した Verse の仕掛けとプロパティに接続します。
8) CheckForMinimumNumberOfPlayers 関数にプレイヤーを渡して待機します。 次に例を示します。
Players = GetPlayspace().GetPlayers()
CheckForMinimumNumberOfPlayers(Players)
9) IslandSettings で、[Game Start Countdown (ゲーム スタート カウントダウン)] を「0.0」に設定します。
#>
waiting_for_more_players := class(creative_device):
Logger:log = log{Channel:=log_waiting_for_more_players_device}
@editable # ラウンドを開始するためにマッチで必要な最小プレイヤーの数です。
MinimumNumberOfPlayers:int = 2
@editable # 画面上のタイマー UI の水平位置と垂直位置です。X 0-1 は左右で、Y 0-1 は上下です。
UIPosition:vector2 = vector2{X:= 0.5, Y:=0.4}
@editable # 画面上のタイマー UI の水平位置と垂直位置です。X 0-1 は左右で、Y 0-1 は上下です。
UIAlignment:vector2 = vector2{X := 0.5, Y := 0.5}
@editable # 他の UI 要素と比較した UI の ZOrder です。
UIZOrder:waiting_int_clamped = 3
@editable # このタイマーは、プレイヤーのマッチへの参加を待機してから、ラウンド開始までのカウントダウンに使用されます。
WaitingForMorePlayersTimer:timer_device = timer_device{}
# このマップは、各プレイヤーにラウンドを開始するために必要なプレイヤー数を表示するための UI キャンバスを関連付けます。
var WaitingForMorePlayersUI:[player]?waiting_for_more_players_ui = map{}
# ラウンドを開始するのに十分な人数のプレイヤーがいるかどうかを確認します。いない場合は、プレイヤー数が MinimumNumberOfPlayers 以上になるまで待機します。
WaitForMinimumNumberOfPlayers(Players:[]player)<suspends>:[]player =
Logger.Print("Waiting if there are enough players for the round to start.")
# 参加プレイヤーが増えたら変更できるように新しい変数を作成します。関数に渡されたプレイヤーの配列を使用して、その変数を初期化します。
var PlayersWaiting:[]player = Players
# プレイヤーの数がラウンドを開始するのに必要な最小人数より少ない場合...
if (PlayersWaiting.Length < MinimumNumberOfPlayers):
loop: # プレイヤー数が必要最小人数以上になるまでループします。
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} players needed for the round to start.")
# 追加の参加プレイヤーを待機する UI を更新します。
DisplayWaitingForMorePlayers(PlayersWaiting)
Sleep(2.0) # さらにプレイヤーがマッチに参加するかどうかを待機し、ラウンドを開始するのに十分なプレイヤーがいるかどうかを確認します。
set PlayersWaiting = GetPlayspace().GetPlayers()
if (PlayersWaiting.Length >= MinimumNumberOfPlayers):
# 十分な人数のプレイヤーが揃ったら、追加の参加プレイヤーを待機する UI をクリアしてから、
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} players in round, preparing for round start.")
ClearWaitingForMorePlayers()
# ループを抜けます。
break
# ラウンド開始のカウントダウンを開始し、カウントダウンが完了するまで待機します。
WaitingForMorePlayersTimer.Start()
WaitingForMorePlayersTimer.SuccessEvent.Await()
Logger.Print("Starting round.")
# セッションのプレイヤー リストを返します。
return PlayersWaiting
# 各プレイヤーに「Waiting for more players (参加プレイヤーを待機中)」という UI メッセージがまだ表示されていない場合は、このメッセージを表示します。すべてのプレイヤーのプレイヤー カウンターを更新します。
DisplayWaitingForMorePlayers(Players:[]player):void =
PlayersNeededCount := MinimumNumberOfPlayers - Players.Length
Logger.Print("{Players.Length} players in round, waiting for {PlayersNeededCount} more player(s) to join.")
for (Player : Players):
# プレイヤーに WaitingForMorePlayersUI がある場合は、TextBlock を取得してテキストを更新し、ラウンドの開始に必要なプレイヤー数を正しく表示します。
if (UIData := WaitingForMorePlayersUI[Player]?):
UIData.TextBlock.SetText(WaitingForMorePlayersMessage(PlayersNeededCount))
# ない場合は、そのプレイヤーの WaitingForMorePlayersUI を作成します。
else:
SetUpWaitingForMorePlayersUI(Player, PlayersNeededCount)
# プレイヤーと player_ui を受け入れ、画面に「Waiting for more players (参加プレイヤーを待機中)」の UI キャンバスを追加します。
SetUpWaitingForMorePlayersUI(Player:player, PlayersNeededCount:int):void =
Logger.Print("Creating 'waiting for more players' UI.")
if (PlayerUI := GetPlayerUI[Player]):
# これは、画面に「Waiting for more players (参加プレイヤーを待機中)」というテキストを出力する text_block です。
TextBlock:text_block = text_block:
DefaultText := WaitingForMorePlayersMessage(PlayersNeededCount)
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
# これは、text_block を保持し、画面上に配置するキャンバスです。
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# キャンバスがプレイヤーに割り当てられます。
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# ゲームに参加するプレイヤーが増えたときに、後でテキストを更新できるように、text_block をマップに保存します。
if (set WaitingForMorePlayersUI[Player] = option{ waiting_for_more_players_ui{Canvas := Canvas, TextBlock := TextBlock} }) {}
# メッセージが表示されたプレイヤーごとに、「Waiting for more players (参加プレイヤーを待機中)」という UI メッセージを削除します。
ClearWaitingForMorePlayers():void =
Logger.Print("Clearing 'waiting for more players' UI.")
for (Player -> UIData : WaitingForMorePlayersUI):
if:
PlayerUI := GetPlayerUI[Player]
Canvas := UIData?.Canvas
set WaitingForMorePlayersUI[Player] = false
then:
PlayerUI.RemoveWidget(Canvas)