目標マーカーは、多くのゲームでプレイヤーを次の目標や次の注視点にガイドしたりするために使用されます。このチュートリアルでは、マップ インジケーターの仕掛け と Verse を使用して、再利用可能な目標マーカーを作成する方法について説明します。
使用する Verse 言語機能
-
拡張メソッド:特別なタイプの 関数 で、既存の クラス または型の メンバー のように機能しますが、新しい型または サブクラス の作成を必要としません。このガイドでは、構造体の拡張 メソッド を作成します。
使用する Verse API
-
Prop API: Prop API は、小道具を動かすための手段を提供します。
-
編集可能なプロパティ: 迅速にテストするため、仕掛けの参照と変数 値 の更新の両方に複数のプロパティを使用します。
手順
次の手順に従って、複数の目標や注視点に移動できる、単一の目標マーカーの仕掛けの設定方法を確認します。完全なスクリプトは、参照用としてこのガイドの末尾に記載されています。
レベルを設定する
この例では、次の小道具と仕掛けを使用しています。
-
建築小道具 × 1:マップ インジケーターの仕掛けを移動するために使用される 小道具 です。
-
マップ インジケーターの仕掛け × 1:ミニマップおよび概要マップにカスタム仕様のマーカーを表示する仕掛けです。
-
プレイヤー スポーン パッドの仕掛け × 1:これを小道具の近くに追加することで、近くでプレイヤーがスポーンします。
Prop API を使用する
Verse を使用して仕掛けを移動するための最初のステップは、Prop API を使用して小道具を移動することです。以下の手順に従って、レベルで小道具を移動します。
-
objective_coordinator_device という 新しい Verse の仕掛けを作成 します。
-
Verse ファイルの一番上のデフォルトの
using
式の下に、SpatialMath
モジュール のusing
式を追加します。このモジュールには小道具を動かすために参照するコードが含まれます。using { /UnrealEngine.com/Temporary/SpatialMath }
-
2 つの編集可能なプロパティを追加します。
-
動く小道具への参照を格納する
RootProp
という名前のcreative_prop
の定数。 -
小道具の移動先の場所を格納する
Destination
という名前のtransform
の定数。
objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} @editable Destination<public> : transform = transform{}
-
-
このコードを実行し、objective_coordinator_device をレベル内にドラッグすると、2 つのプロパティが [Details (詳細)] パネルに表示されます。
-
小道具を実際に移動させるのは
TeleportTo[]
メソッドです。TeleportTo[]
は失敗する可能性がある式であるため、それをif
式 内で呼び出して、括弧ではなく 角括弧 を使用します。if
は、失敗コンテキスト を作成します。if(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Prop move successful") else: Print("Prop move failed")
-
TeleportTo[]
の引数は、Translation および Rotation です。これらはどちらも Destination プロパティに由来するものです。 -
エディタに戻り、コンテンツ ブラウザ で 「Fortnite」>「Galleries」>「Props」 から小道具をドラッグします。このガイドで使用しているのは Coastal Buoy 02B という名前のものですが、「Props」フォルダのものであればどれでも構いません。
-
[Outliner (アウトライナー)] で 目標コーディネーター の仕掛けを選択します。[Details (詳細)] パネルで、小道具に RootProp を設定します。この例では、RootProp は Coastal Buoy 02B に設定されています。
-
[Details] パネルで、[Destination (目的地)] を展開します。[Destination] は、
transform
型であるため、[Scale (スケール)]、[Rotation (向き)]、および [Translation (トランスレーション)] で構成されます。[Translation] を変更するだけでこの小道具を移動できるため、これを展開します。X が 5000.0 で終了するようにフィールドを設定します。コードのテスト時は、効果をわかりやすくするため、値を大きく変更すると良いでしょう。小さい変更では、コードが期待する処理を行っているか判断するのが難しくなります。
using { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/SpatialMath } objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} # マーカーが移動する場所 @editable Destination<public> : transform = transform{} OnBegin<override>()<suspends> : void = if(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Prop move successful") else: Print("Prop move failed")
-
[Verse] をクリックして [Build Verse Code (Verse コードをビルド)] を選択し、[Launch Session (セッションを起動)] をクリックします。最後に、[Start Game (ゲームを開始)] をクリックします。小道具が移動するのを確認できるはずです。
親と構造体
これで、小道具はレベル内で移動するようになりましたが、本当の目標はマップ インジケーターの仕掛けを移動してプレイヤーがウェイポイントとして使用できるようにすることです。以下の手順に従って、建築小道具およびマップ インジケーターの仕掛けをレベルに追加し、その仕掛けを建築小道具にアタッチします。
-
コンテンツ ブラウザ 内を右クリックしてコンテキスト メニューを開きます。
-
コンテキスト メニューから [Blueprint Class (ブループリント クラス)] を選択します。
-
[Pick Parent Class (親クラスを選択)] ウィンドウで、[Building Prop (建築小道具)] をクリックします。
-
コンテンツ ブラウザに新しいブループリント クラスが表示されます。その名前を「BuildingProp」に変更します。
-
建築小道具をレベル内にドラッグします。この小道具には メッシュ がないため、トランスフォーム ギズモ のみが表示されます。
-
アウトライナー内で Map Indicator Device (マップ インジケーターの仕掛け) を BuildingProp にドラッグします。これにより、BuildingProp が Map Indicator Device の親になります。これで、BuildingProp が移動すると、Map Indicator Device も一緒に移動するようになりました。
Verse を使用して仕掛けを作成する方法 については説明しましたが、独自の仕掛けを持たない Verse ファイルを作成することもできます。
-
新しい Verse ファイルを作成し、名前を「objective_marker」とします。このファイルは仕掛けを作成しません。代わりに、前に作成した Verse の仕掛けに公開される
struct
の定義を含んでいます。 -
まず、objective_marker という
struct
を宣言します。これには、RootProp
とMapIndicator
というメンバーがあります。これらの両方に@editable
指定子が必要です。objective_marker<public> := struct<concrete>: @editable RootProp<public> : creative_prop = creative_prop{} @editable MapIndicator<public> : map_indicator_device = map_indicator_device{}
拡張メソッドと名前付き引数
単一のメソッドである MoveMarker
を 宣言 します。これは、RootProp
メンバーとそれにアタッチされたマップ インジケーターの仕掛けを移動します。このメソッドは、拡張メソッドと名前付き引数という 2 つの言語の機能を導入します。
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
-
拡張メソッド:
MoveMarker()
メソッドをobjective_marker
構造体に追加します。拡張メソッドでは識別子と型をコロンで区切り、それらを括弧で囲んで宣言します。この場合であれば、(Marker : objective_marker)
となります。 -
名前付き引数: 2 番目の引数
?OverTime
は、?
を使用することでMoveMarker
関数呼び出しで名前を指定する必要があることを示します。これにより、MoveMarker
への呼び出しを参照したり記述したりするデベロッパーにとって、float
引数が行う処理が理解しやすくなります。
MoveMarker()
は、先ほど使用した TeleportTo[]
または MoveTo()
のいずれかのメソッドを Prop API から呼び出します。if..else
ブロックを作成し、パラメータ OverTime
が 0.0
より大きいかをテストします。大きい場合は、MoveTo()
を呼び出します。これにより、すぐにテレポートするのではなく、指定した期間の経過後に目標が次の場所に移動します。
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]
このコードを現時点でコンパイルすると成功はしますが、コンテンツ ブラウザ内の 「CreativeDevices」 フォルダには新しい仕掛けが表示されません。これは、objective_marker が struct
であり、creative_device
から継承されたクラスではないためです。
目標コーディネーターの仕掛けを更新する
新しいタイプの参照ができたため、今度は objective_coordinator_device を参照するように更新する必要があります。
-
RootProp
プロパティを削除し、型がobjective_marker
のPickupMarker
というプロパティで置き換えます。これは、自分で作成した型です。 -
MoveMarker()
にはfloat
型の引数が必要であるため、これをMoveTime
という名前の編集可能なプロパティとして作成します。 -
TeleportTo[]
への呼び出しを削除します。代わりに、objective_marker
のために作成したMoveMarker()
メソッドを呼び出します。これには、名前付き引数である?OverTime
が必要です。
objective_coordinator_device<public> := class<concrete>(creative_device):
@editable
PickupMarker<public> : objective_marker = objective_marker{}
# マーカーが移動する場所
@editable
Destination<public> : transform = transform{}
# マーカーが新しい場所に到達するまでにかかる時間
@editable
MoveTime<public> : float = 0.0
OnBegin<override>()<suspends> : void =
PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)
このコードをコンパイルし、目標コーディネーターの仕掛けの詳細を確認します。PickupMarker プロパティおよび MoveTime プロパティが表示されます。また、PickupMarker には RootProp および MapIndicator が含まれています。
-
[RootProp] フィールドを BuildingProp に、[MapIndicator] フィールドを Map Indicator Device に設定します。
-
コードをコンパイルし、[Launch Session] をクリックします。ゲームが開始すると、少ししてから ミニマップ に移動するマーカーが表示されます。
MoveTime
にさまざまな値 (0.0
も含む) を設定してこのマーカーを試してみましょう。さまざまなシナリオで、どのような動きが最適かを検討してください。
GetPlayers() と ActivateObjectivePulse()
プレイヤーが次の目標にたどり着けるように、手助けできる手段が用意されています。これは 目標パルス と呼ばれ、これが有効なときはプレイヤーから マップ インジケーターの仕掛け に向かって破線が表示されます。以下の手順に従って、目標パルスを目標コーディネーターの仕掛けに追加します。
目標パルスを有効にするために必要なメソッドは ActivateObjectivePulse()
で、これには agent
型の引数が 1 つ必要です。まず、プレイヤーのキャラクターを表す agent
のインスタンスを取得するメソッドの作成から始めましょう。
-
FindPlayer()
という関数を<private>
に設定して宣言し、戻り値はvoid
にします。 -
Self.GetPlayspace().GetPlayers()
を使用し、レベル内のすべてのプレイヤーの配列を取得します。AllPlayers
という変数にこの配列を格納します。FindPlayer<private>() : void = AllPlayers := Self.GetPlayspace().GetPlayers()
-
レベル内のプレイヤーが 1 人のみの場合にその参照を取得するには、最初の配列要素を独自の変数に割り当てます。配列へのアクセスは 失敗する可能性がある式 であるため、
if
式の中に配置します。if (FirstPlayer := AllPlayers[0]):
-
変数への
player
の割り当ては失敗する可能性があるため、コード内でプレイヤーを参照する際には、option
型の変数を使用することもできます。任意のプレイヤー変数?player
を宣言します。他のメンバー変数とともに使用する必要があります。objective_coordinator_device<public> := class<concrete>(creative_device): var PlayerOpt<private> : ?player = false @editable PickupMarker<public> : objective_marker = objective_marker{} # マーカーが移動する場所 @editable Destination<public> : transform = transform{} # マーカーが新しい場所に到達するまでにかかる時間 @editable MoveTime<public> : float = 0.0
-
新しい変数を設定し、
Print()
式を使用してelse
ブロックを作成し、プレイヤーが見つからない場合には通知を表示するようにします。これで、FindPlayer()
関数が完成しました。FindPlayer<private>() : void = # これは単一のプレイヤー体験であるため、最初のプレイヤー (0) のみが # 使用可能となる必要があります。 AllPlayers := Self.GetPlayspace().GetPlayers() if (FirstPlayer := AllPlayers[0]): set PlayerOpt = option{FirstPlayer} Print("Player found") else: # プレイヤーが見つからない場合はエラーをログに記録します。 Print("Can't find valid player")
OnBegin()
関数に戻り、次の 2 つの変更を加える必要があります。
-
FindPlayer()
関数を呼び出します。OnBegin<override>()<suspends> : void = FindPlayer()
-
MoveMarker()
の呼び出し後、別のif
式を使用してオプションのプレイヤーの変数を新しい変数に設定し、それをPickupMarker.MapIndicator.ActivateObjectivePulse()
の引数としてパスします。if (FoundPlayer := PlayerOpt?): PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
これでコードを実行すると、キャラクターからレベル内の目標マーカーの位置を指す目標パルスが表示されます。
完全なスクリプト
Objective_marker.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Devices/CreativeAnimation }
objective_marker<public> := struct<concrete>:
# 動かされる小道具
@editable
RootProp<public> : creative_prop = creative_prop{}
# 一緒に動く小道具の子
@editable
MapIndicator<public> : map_indicator_device = map_indicator_device{}
# objective_marker の拡張メソッド
# OverTime の前の ? は名前付き引数として指定します
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]
Objective_coordinator_device.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /UnrealEngine.com/Temporary/SpatialMath }
objective_coordinator_device<public> := class<concrete>(creative_device):
var PlayerOpt<private> : ?player = false
@editable
PickupMarker<public> : objective_marker = objective_marker{}
# マーカーが移動する場所
@editable
Destination<public> : transform = transform{}
# マーカーが新しい場所に到達するまでにかかる時間
@editable
MoveTime<public> : float = 0.0
OnBegin<override>()<suspends> : void =
FindPlayer()
PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)
# プレイヤーが false に設定されている場合、見つかったプレイヤーの objective pulse をアクティブにします
if (FoundPlayer := PlayerOpt?):
PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
FindPlayer<private>() : void =
# これは単一のプレイヤー体験であるため、最初のプレイヤー (0) のみが
# 使用可能となる必要があります。
AllPlayers := Self.GetPlayspace().GetPlayers()
if (FirstPlayer := AllPlayers[0]):
set PlayerOpt = option{FirstPlayer}
Print("Player found")
else:
# プレイヤーが見つからない場合はエラーをログに記録します。
Print("Can't find valid player")
応用編
ここで記述した移動のコードはどの小道具に対しても有効です。移動可能な小道具を仕掛けの親として作成すれば、その仕掛けは小道具とともに移動します。他の小道具や仕掛けも移動させてみましょう。また、これらを利用したゲームが考えられないか検討しましょう。
次のステップ
このガイドに沿って集荷/配達ゲームを開発している場合は、次のステップとしてカウントダウン タイマー機能の作成方法を学習しましょう。