Scene Graph には、エンティティとコンポーネントの間のさまざまなインタラクションを通じて柔軟で動的なゲームプレイを作成する方法が複数用意されています。 このテンプレートでは、エンティティのスポーンと削除を通じてランタイム時にエンティティを操作するいくつかの方法や、コンポーネント イベントとクエリを使用してエンティティ間の通信を作成したり、再利用可能なゲームプレイ要素を構築する方法を紹介します。
プレハブをスポーンおよび削除する
例として、2 つ目のホールのインタラクティブ パッドを見てください。 プレイヤーがこのパッドを踏むとプレハブ ランタンがシーンに追加され、パッドから降りるとプレハブ ランタンが削除されます。
「SpawnPrefabDevice.verse」を開いてこのコードの仕組みを確認してみましょう。 これは、ボリュームの仕掛けを使用してプレイヤーが乗ったタイミングを把握するクリエイティブの仕掛けです。 プレイヤーがトリガーに乗ったときにプレハブをスポーンし、トリガーから降りたときにプレハブを削除します。 SpawnPrefabDevice クラス定義の冒頭で、編集可能な TriggerVolume がレベルのトリガー ボリュームを参照しています。
SpawnPrefabDevice := class(creative_device):
@editable
TriggerVolume:volume_device = volume_device{}ゲームが始まるときに、この仕掛けはスポーンする新しいライトポスト プレハブとスポーン位置を定義します。
OnBegin<override>()<suspends>:void =
PrefabToSpawn:entity = LightPost.P_LightPost{}
SpawnTransform:transform = transform:
Translation := vector3:
Left := -9084.0
Up := -8.0
Forward := -919.0その後、シミュレーション エンティティを取得し、処理を実行するための一連の失敗コンテキストを作成します。 最初に内側のコードを繰り返し実行する loop があり、それに race が続いて、2 つの block 文をレースさせます。
if:
SimulationEntity := GetSimulationEntity[]
then:
loop:
race:
block:
TriggerVolume.AgentEntersEvent.Await()
SimulationEntity.AddEntities(array{ PrefabToSpawn })
PrefabToSpawn.SetGlobalTransform(SpawnTransform)
最初の block 文はエージェントがボリュームの仕掛けに乗るのを待機します。 エージェントが乗ったら、AddEntities() を使用してシーンにランタン プレハブを追加し、SetGlobalTransform() を使用して位置を正しく設定します。 2 つ目の block はエージェントがボリュームから降りるのを待機し、エージェントが降りたら親 (この場合はシミュレーション エンティティ) からプレハブを削除します。
これらの block はループ内でレースしているため、それぞれが常に実行されており、プレイヤーがボリュームに乗り降りするとライトポストが何度もリスポーンします。
完全なスクリプト
以下がシーンへのエンティティの追加と削除の完全版です。
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
SpawnPrefabDevice := class(creative_device):
エンティティのオーバーラップ
2 つ目のホールには、エンティティで可能なさまざまなインタラクト方法を紹介する一連のサンプルがあります。 たとえば、メッシュ コンポーネントが設定されているエンティティは、メッシュ コンポーネントの EntityEnteredEvent と EntityExitedEvent を利用して、別のエンティティとオーバーラップしているときに変化させることができます。 2 つ目のホールにある UFO の例で、この機能の実際の挙動を見てみましょう。
ゲームが始まると、牛は自身を上に移動させるアニメーションを再生し、牛が UFO に連れ去られているかのように見せます。 この牛はメッシュ コンポーネントの EntityEnteredEvent を使用して、自身が UFO とオーバーラップしたタイミングを認識したら、自身を不可視にして連れ去られたかのように見せます。
この例のコードは「EntityEnteredExampleComponent.verse」で定義されています。 このファイルを Verse Explorer から開きます。
entity_entered_example_component クラスは最初に、牛の誘拐アニメーションを作成するための編集可能 Keyframes 配列を定義しています。 また、シーン内の牛の開始位置を示す StartTransform 変数を定義しています。
entity_entered_example_component<public> := class<final_super>(component):
@editable
Keyframes:[]keyframed_movement_delta = array{}
var StartTransform:transform = transform{}OnBeginSimulation() で、スクリプトは最初に fort_round_manager を取得します。 これは、ラウンドの開始と終了のイベントをサブスクライブするために使用できるフォートナイト ラウンド マネージャーへのインターフェースです。 この例では、OnRoundStarted() 関数が SubscribeRoundStarted() を使用してラウンドの開始をサブスクライブしています。つまり、この関数はエンティティがシミュレーションを開始したときではなく、ゲームが開始したときにのみ実行されます。
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
if:
FortRoundManager := Entity.GetFortRoundManager[]
then:
FortRoundManager.SubscribeRoundStarted(OnRoundStarted)OnRoundStarted() ではまず StartTransform をエンティティのグローバル トランスフォームに設定した後、メッシュ コンポーネントの EntityEnteredEvent に新しい関数 OnEntityEntered() をサブスクライブして、別のメッシュがエンティティにオーバーラップしたときに関数がトリガーされるようになっています。 その後、牛を上に移動させ始めるための PlayCowAbductionAnimation() 関数をスポーンします。
OnRoundStarted():void =
set StartTransform = Entity.GetGlobalTransform()
if:
Mesh := Entity.GetComponent[mesh_component]
then:
Mesh.EntityEnteredEvent.Subscribe(OnEntityEntered)
spawn { PlayCowAbdcutionAnimation() }PlayCowAbductionAnimation() 関数は、牛のグローバル トランスフォームを開始時のトランスフォームに設定して、少しの間待機します。 その後、エンティティのメッシュとキーフレーム移動コンポーネントを取得し、メッシュで牛を可視にして、キーフレーム移動コンポーネントにアニメーションを設定して再生します。
PlayCowAbdcutionAnimation()<suspends>:void =
Entity.SetGlobalTransform(StartTransform)
Sleep(2.0)
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
set Mesh.Visible = true
MovementComponent.SetKeyframes(Keyframes, oneshot_keyframed_movement_playback_mode{})
MovementComponent.Play()最後に OnEntityEntered() 関数が使用され、牛が別のエンティティ (この場合は UFO) にオーバーラップしたときにコードを実行します。 このコードは、実行されると先ほどと同様にメッシュとキーフレーム移動コンポーネントを取得しますが、今度は進行中のアニメーションの再生を停止し、牛を不可視にして連れ去られたかのように見せます。 その後、PlayCowAbductionAnimation() の新規インスタンスをスポーンし、処理を最初からやり直します。
OnEntityEntered(OtherEntity:entity):void =
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Stop()
set Mesh.Visible = false
spawn { PlayCowAbdcutionAnimation() }完全なスクリプト
以下が誘拐される牛メッシュと UFO メッシュの完全なスクリプトです。
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
entity_entered_example_component<public> := class<final_super>(component):
オーバーラップ ヒットをクエリする
エンティティでは、オーバーラップ クエリを使用して他のエンティティをクエリすることもできます。 特定のメッシュに対するエンティティの出入りを検出する代わりに、オーバーラップ クエリを使用すれば、特定のエリアにオーバーラップしているエンティティとコンポーネントをすべて返すことができます。
この領域は、エンティティ自体、球やボックスなどの特定のコリジョン ボリューム、またはエンティティをシミュレートする位置のいずれかになります。 これは overlap_hit のリストを返します。 それぞれの overlap_hit は、ソース ボリュームがオーバーラップしているコンポーネント/ボリュームに関する情報を提供します。これらのコンポーネントをクエリすると、関連するエンティティを見つけることができます。
たとえば、FindOverlapHits サンプルの前にあるインタラクティブ パッドに乗ってみてください。 パッドとインタラクトすると、UFO が不可視のコリジョン ボリュームをスポーンし、それを使用してオーバーラップ クエリを行います。 コリジョン ボリュームが牛にオーバーラップすると、UFO が牛を連れ去ります!
この例のコードは「FindOverlapHitsExampleComponent.verse」で定義されています。 このファイルを Verse Explorer から開きます。
このクラスではまず、プレイヤーが乗るインタラクティブ パッドを参照する Trigger という名前の編集可能なボリュームの仕掛けと、牛が連れ去られている最中かどうかを示す logic 変数 IsAbducting を定義しています。 コンポーネントは、シミュレーションを開始すると、OnRoundStarted() 関数をラウンド マネージャーのラウンド開始にサブスクライブします。 OnRoundStarted() 関数も同様に Trigger の AgentEntersEvent と AgentExitsEvent を OnTriggerEntered() 関数と OnTriggerExited() 関数にそれぞれサブスクライブします。
find_overlaphits_example_component<public> := class<final_super>(component):
@editable
Trigger:volume_device = volume_device{}
var IsAbducting:logic = false
OnBeginSimulation<override>():void =
プレイヤーがパッドに乗ったときに、UFO が牛を連れ去っている最中でなければ、OnTriggerEntered() 関数が呼び出されて、keyframed_movement_component の取得と Pause() の呼び出しが行われ、UFO の動きを一時停止します。 その後 EnableAbductionBeam() と PerformOverlapCheck() を呼び出して、誘拐光線を有効にし、牛が UFO の下にいるかどうかを確認します。
OnTriggerEntered(Agent:agent):void=
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()
PerformOverlapCheck()オーバーラップをチェックする実際のロジックは PerformOverlapCheck() 関数に含まれています。 誘拐光線を再現するために、この関数はコリジョン カプセルをスポーンし、UFO 直下に設定された CollisionTransform を定義します。
PerformOverlapCheck():void =
CollisionCapsule := collision_capsule{Radius := 36.0, Length := 328.0}
var CollisionTransform:transform = Entity.GetGlobalTransform()
set CollisionTransform.Translation.Up = CollisionTransform.Translation.Up - 248.0次に、この関数は for 文で FindOverlapHits() を呼び出し、コンポーネントまたはボリュームがあればそれを検出して返します。 コリジョンを確認するボリュームとして CollisionCapsule、コリジョンをシミュレートする場所として CollisionTransform を渡します。 その後、各オーバーラップをイテレートして、オーバーラップしたコンポーネントがメッシュ コンポーネントであるかどうか (具体的には、牛エンティティの SM_Toy_Cow メッシュであるかどうか) を確認します。 牛のメッシュだった場合は、AbductCow() 関数をスポーンし、連れ去る対象として牛を渡します。
# Perform the overlap check from the entity that contains the mesh_component
for:
Overlap : Entity.FindOverlapHits(CollisionTransform, CollisionCapsule)
# Cast to see if what was overlapped was the Cow
CowMesh := Meshes.SM_Toy_Cow[Overlap.TargetComponent]
CowPrefab := CowMesh.Entity
do:
spawn { AbductCow(CowPrefab) }牛の連れ去りを再現するために、エンティティはアニメーションを構築して、それを牛エンティティで再生します。上述のエンティティのオーバーラップの例と似たような方法です。 このコードは牛ではなく UFO エンティティから呼び出されているため、コードでは牛エンティティのコンポーネントを取得して、アニメーションを牛に渡して再生する必要があります。 牛のメッシュとキーフレーム移動コンポーネントを取得した後、IsAbducting を true に設定します。
AbductCow(CowEntity:entity)<suspends>:void =
# Get the components on the Cow Prefab
if:
CowMesh := CowEntity.GetComponent[mesh_component]
MovementComponent := CowEntity.GetComponent[keyframed_movement_component]
then:
set IsAbducting = true牛の連れ去りアニメーションで使用するキーフレームの設定はアウトライナーで行わないので、UFO と牛の位置の差を基に構築する必要があります。 そのために、牛と UFO の平行移動の差を取得して、それらの値から新しい keyframed_movement_delta を構築します。 その後、キーフレーム移動コンポーネントの配列として 1 つのキーフレームを設定し、再生関数を呼び出して開始位置と UFO の間で牛をアニメートします。
# Get the delta between Cow and UFO
DeltaTransform:transform = transform:
Translation:= Entity.GetGlobalTransform().Translation - CowEntity.GetGlobalTransform().Translation
Scale := vector3{Left:= 0.0, Up:= 0.0, Forward:= 0.0}
# Create a key frame
Keyframe := keyframed_movement_delta:
Transform := DeltaTransform
Duration := 2.0
Easing := ease_in_cubic_bezier_easing_function{}
連れ去る際は牛を UFO に向けてアニメートするだけでなく、牛が UFO にオーバーラップしたときに牛を不可視にする必要もあります。 それには、コードで牛のメッシュ コンポーネントの EntityEnteredEvent を待機した後、RemoveFromParent() を呼び出して、シーンから牛エンティティを削除します。 牛がいなくなったので UFO は巡回を再開できます。コードは UFO のキーフレーム移動コンポーネントの再生関数を呼び出して、UFO を動かします。
# Wait for Entity Entered Event
CowMesh.EntityEnteredEvent.Await()
# Remove Cow from world
CowEntity.RemoveFromParent()
# Resume UFO Patrol
set IsAbducting = false
if:
UFOMovementComponent := Entity.GetComponent[keyframed_movement_component]
最後に取り上げる EnableAdbuctionBeam() 関数と DisableAbductionBeam() 関数ですが、これらは呼び出されると UFO の誘拐光線メッシュとスポット ライト コンポーネントのオン/オフをそれぞれ切り替える単純なヘルパーの役割です。
EnableAbductionBeam():void =
for:
Mesh : Entity.FindDescendantComponents(Meshes.S_EV_SimpleLightBeam_01)
do:
Mesh.Enable()
for:
Light : Entity.FindDescendantComponents(spot_light_component)
do:
Light.Enable()
完全なスクリプト
以下に、オーバーラップ ヒットをクエリするための完全なスクリプトを示します。
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }
スイープ ヒットをクエリする
スイープ ヒットも、エンティティのオーバーラップをクエリする重要な手段です。 スイープとは、オブジェクトを、特定のベクトルに沿って設定された距離にわたって移動することです。 たとえば、プラットフォーム上でブロックを移動してプレイヤーを隙間に押し込んだり、ミサイルをまっすぐ前方に発射して壁を破壊したりします。
FindSweepHits() 関数は sweep_hit のリストを返します。 それぞれの sweep_hit は、コンポーネントまたはボリュームのヒット、スイープを実行するソース ボリュームまたはコンポーネントなど、overlap_hit と同じ情報を提供します。 さらに、接触位置、法線、面法線、ヒットが発生したスイープに沿った距離に関する情報も提供します。
このテンプレートでは、スイープ ヒットを使用して、前のオーバーラップ ヒットの例よりも高度なバージョンを作成しています。 2 つ目のホールにある最後のインタラクティブ パッドに乗って、その例をご確認ください。 パッドに乗ると、UFO が誘拐光線をスポーンします。 その後、UFO メッシュがスイープ ヒットを行い、スイープがオーバーラップする最初のエンティティを確認します。 エンティティが牛の場合、UFO は通常どおり牛を連れ去ります。 しかし、牛が球体エンティティで保護されている場合は、スイープが最初に球体にヒットしてブロックされるので、牛は UFO に連れ去られません。
「FindOverlapHitsExampleComponent.verse」を Verse Explorer から開いてコードを確認しましょう。 ここでのセットアップは、上記のオーバーラップ ヒットの例にとてもよく似ており、牛の連れ去りと誘拐光線のオン/オフのロジックは同じです。 大きな違いは、プレイヤーがサンプルの前面のインタラクティブ パッドに乗ったときに実行される OnTriggerEntered() 関数にあります。
オーバーラップ ヒットの例と似たように、この関数のコードでは最初に、エンティティからキーフレーム移動コンポーネントを取得して誘拐ビームを有効にします。
OnTriggerEntered(Agent:agent):void=
# When a cow is inside the abduction area, stop the ship moving and start the abduction beam.
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()しかし、オーバーラップではなくスイープを使用しているので、牛が誘拐光線の下にいるかどうかを判断するロジックは少し異なったものになっています。 まず、UFO エンティティの最初の子 (この場合は UFO のメッシュ エンティティ) を取得します。 次に、スイープを行うための UFO から真っ直ぐ下を向いた変位ベクターを作成します。
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}その後、この変位ベクターを使用して FindFirstSweepHit() ヘルパー関数を呼び出し、UFO のメッシュとベクターを渡します。 最初のコンポーネントが牛のメッシュ コンポーネントだった場合、AbductCow() 関数をスポーンして牛を連れ去る様子を再現します。
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}
FirstSweepHitEntity := FindFirstSweepHit(Child, DisplacementVector)
# If the First Hit Entity is the Cow Mesh, then abduct the Cow
if (HitEntity := FirstSweepHitEntity?; HitEntity.GetComponent[Meshes.SM_Toy_Cow]):
spawn { AbductCow(HitEntity) }FindFirstSweepHit() 関数はスイープ対象のエンティティと、スイープの経路となる変位ベクターを受け取ります。 FindSweepHits() を呼び出してスイープをシミュレートした後、返されたスイープのヒットそれぞれを「for」文でイテレートします。 各 sweep_hit に含まれているのはコンポーネントとボリュームのいずれかなので、TargetComponent または TargetVolume をクエリしてその型を確認できます。 この場合、コードは TargetComponent の所有エンティティを取得してオプションとして返します。つまり、スイープがコンポーネントにヒットした場合は「true」を返し、そうでない場合は false を返します。
# Returns the first Entity hit by FindSweepHits
FindFirstSweepHit(InEntity:entity, DisplacementVector:vector3):?entity =
for (SweepHit : InEntity.FindSweepHits(DisplacementVector)):
return option{ SweepHit.TargetComponent.Entity }
return false完全なスクリプト
以下に、スイープ ヒットをクエリするための完全なスクリプトを示します。
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }