Verse を使用して独自のコンポーネントを作成し、エンティティに追加することができます。 Verse のカスタム コンポーネントを使用すると、シーンでエンティティを生成/削除する、エンティティに対してコンポーネントを追加/削除する、または、ループ時に退場するなど、エンティティの動作を思いのままにカスタマイズすることもできます。
Verse コンポーネントを新規作成する
Verse テンプレート ファイルを使って Verse コンポーネントを新規作成できます。
Verse コンポーネントを新規作成するには:
エンティティの [Details (詳細)] パネルで [Add Component (コンポーネントを追加)] > [New Verse Component (新規 Verse コンポーネント)] を選択します。
Verse Explorer で新規 Verse ファイルを追加し、新規 Verse コンポーネントを作成することもできます。
Verse コード テンプレートのリストから、Scene Graph Component を選択します。
[Component Name (コンポーネント名)] に Verse で記述するコンポーネントの名前を設定します。 この例のコンポーネント名は
my_verse_componentです。[Create (作成)] をクリックして Verse コンポーネント ファイルを作成します。 エンティティにコンポーネントを追加するときのコンポーネント リストに、Verse で記述したコンポーネントが表示されるようになります。
追加できるのは特定のコンポーネント クラスまたはサブクラスの 1 つのみです。 たとえば、エンティティは mesh_component を 1 つだけ持つことができます。 これはコンポーネントのサブクラスにも適用されます。つまり、エンティティに capsule_light_component を追加した場合、両方とも light_component のサブクラスであるため、rect_light_component をさらに追加することはできません。 Verse で作成されたカスタム コンポーネントにも同じ制限が適用されます。
コンポーネントの存続期間
コンポーネントは、エンティティに追加されて、シーンに追加され、シミュレーションでの実行が開始されると、一連の存続期間関数を実行します。 Verse で作成されたコンポーネントは、シミュレーションのセットアップと実行のためにこれらのメソッドをオーバーライドする必要があります。
コンポーネントがシャットダウンすると、これらの関数のシャットダウン バージョンが実行され、破棄される前にコンポーネント上に保持されている状態をクリーンアップすることができます。
コンポーネントの存続期間の状態は次のとおりです。
初期化済み
AddedToScene
BeginSimulation
EndSimulation
RemovingFromScene
Uninitializing
コンポーネントの存続期間関数は仕掛けの存続期間とは異なります。 コンポーネント ロジックは編集モードおよびプレイ モードの両方で動作します。 追加した動作は、セッションを起動するとすぐに実行されます。 ゲームの開始時にのみコンポーネント ロジックを実行したい場合は、Verse の仕掛けの OnBegin() 関数でプレハブをスポーンできます。
Verse でエンティティとコンポーネントをクエリする
Verse コードでエンティティとコンポーネントを検索する方法にはいくつかあります。 Verse コードでクエリしたり、機能を開発したりする上で、エンティティとコンポーネントがどのような構造になっているかが影響します。
Verse でエンティティとコンポーネントをクエリするには、まずエンティティを指定し、階層内でそのエンティティの上/下位にネストされたエンティティが返されるようにする必要があります。 エンティティの直接の親または子、およびそのすべての祖先と子孫のエンティティをクエリできます。
コンポーネント型を持つエンティティを取得する
クエリするエンティティで呼び出すことで、特定の型のコンポーネントを持つエンティティをすべて検索できます。 クエリしているエンティティがシミュレーション エンティティの場合、シーン内にあるエンティティでその型のコンポーネントを持つものをすべて返します。
次の例では、Verse コンポーネントによって、light_component コンポーネントがアタッチされたすべてのエンティティを取得します。 見つかったエンティティごとに、particle_sytem_component をスポーンし、エンティティにアタッチします。 ここで、BlowingParticles は「Assets.digest.Verse」ファイルで参照される Niagara エミッタです。
light_component は、エンティティに追加できるさまざまなライト コンポーネント型すべてのスーパークラスです。 以下のクエリでは、LightComponent を使用して、あらゆる種類のライト コンポーネントを持つエンティティを検索します。
# Runs when the component should start simulating in a running game.
OnBeginSimulation<override>():void =
# Run OnBeginSimulation from the parent class before
# running this component's OnBeginSimulation logic
(super:)OnBeginSimulation()
for:
LightComponent : Entity.GetSimulationEntity[].FindDescendantEntitiesWithComponent(light_component)
do:
# Create a particle system component and add it to the entity.
次の例は、Verse コンポーネントがアタッチされたエンティティにネスティングされているエンティティのうち、パーティクル システムを持つものをすべてクエリする方法を説明しています。クエリには FindDescendantEntitiesWithComponent() 関数を使用します。 同様に、FindAncestorEntitiesWithComponent() を使用して、特定のコンポーネントを持つすべての祖先エンティティを取得することもできます。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Get all entities that have particle system components nested under the entity this component is attached to.
ParticleSystemEntities := Entity.FindDescendantEntitiesWithComponent(particle_system_component)
# Get all entities with particle system components that are ancestors of this entity.
シーン全体にわたってエンティティをクエリする必要がある場合は、シミュレーション エンティティを取得し、そのシミュレーション エンティティでクエリを実行します。 これにより、そのエンティティ構造の最上位から始め、クエリに合致する、ネスティングされたエンティティをすべて見つけることができます。 シミュレーション エンティティにアクセスするには、Verse コンポーネントがアタッチされたエンティティ上で、失敗する可能性がある関数 GetSimulationEntity[] を呼び出します。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Get the simulation entity.
if:
SimulationEntity := Entity.GetSimulationEntity[]
then:
Verse コンポーネントでエンティティ ツリーを常にくまなく検索するには代償もあります。 動作が特定のエンティティ構造に依存している場合、エンティティ構造に加えた些細な変更が、予想外の形で動作の変化につながったり、まったく機能しなくなったりすることがあります。
一方で、Verse コンポーネントを追加し正しいエンティティ構造にするだけでよいため、コンポーネントの設定に手間がかかりません。 こうしたトレードオフを念頭に置き、エンティティを作成したり、Verse コンポーネントのロジックを開発したりしてください。
Verse でエンティティとコンポーネントを扱う方法全般と、SceneGraph モジュールについて紹介します。 以下に、コードを使ってエンティティとコンポーネントをクエリする一般的な方法を説明します。
エンティティのコンポーネントを取得する
Verse を使ってエンティティ上の特定の型のコンポーネントを取得するには、GetComponent[] を呼び出します。 他のコンポーネントの動作に依存するカスタム ロジックを作成するときに便利です。 たとえば、sound_component を使用してライトの色に応じてオーディオを再生したり、particle_system_component を使用してエンティティが特定のゾーン内にあるかどうかに応じてエフェクトを適用したりします。
次の例では、Verse コンポーネントにより、ループで反復してプラットフォームが表示/非表示になります。 これは、エンティティ上のメッシュコンポーネントを取得して無効にし、設定された期間後に再度有効にすることで実行されます。
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SceneGraph }
# A Verse-authored component that can be added to entities.
# This component will make the entity appear and disappear on loop.
disappear_on_loop_component := class<final_super>(component):
# How long in seconds the entity should be hidden.
@editable
別の例では、Verse コンポーネントはエンティティ上の light_component を取得し、その色を濃いオレンジ色に変更します。 light_component は、エンティティに追加できるあらゆる種類の照明のスーパークラスであるため、この例では、そこからサブクラス化されたコンポーネントを検索します。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Find any component on this entity that subclasses from light_component.
if:
LightComponent := Entity.GetComponent[light_component]
then:
すべてのコンポーネントを取得する
GetComponents() 関数を使用すると、エンティティ上のすべてのコンポーネントを返すことができます。 コードではこれらのコンポーネントの型がわからないため、キャストを使用して各コンポーネントの型に基づいて異なる処理を実行できます。 次の例では、エンティティ上の全コンポーネントの配列を取得し、それらから enableable 型へのキャストを試みます。 キャストが成功すると、コンポーネントは enableable インターフェースを実装します。 次に、このインターフェースを実装する各コンポーネントを無効にします。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Get a list of all components on the entity.
ComponentList := Entity.GetComponents()
for:
Component:ComponentList
ゲームプレイ タグでエンティティを検索する
エンティティにタグ コンポーネントを追加して、シーン内にある特定のエンティティを見つけることができます。これはアクタにゲームプレイ タグを追加する方法と似ています。 どのコンポーネントを持っているか、シーンのどこにあるかなど、変化する条件に依存することなく、作業したいエンティティを抽出するのに便利です。
変化する条件に依存していると、エンティティを変更したり、新規エンティティをプロジェクトに追加したりしたときに、ゲーム内で予想外の動作につながるおそれがあります。 エディタでタグを追加するには、tag_component をエンティティに追加し、[Tags (タグ)] ドロップダウンからタグを選択します。または、Verse コードで AddTag() 関数を使用します。
次の例では、シミュレーション エンティティに対して、tag_component に my_tag タグのフラグが付けられたすべての子孫をクエリします。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically cancelled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void =
# Get the simulation entity.
if:
SimulationEntity := Entity.GetSimulationEntity[]
then:
オーバーラップがあるエンティティを見つける
コリジョン ボリュームは、メッシュのコリジョン形状を表すボリュームです。 これらを使用すると、オブジェクトがタワーに近づきすぎた場合にオブジェクトにダメージを与えたり、サッカーボールがゴールに入ったことを検出したりするなど、特定の形状内でオーバーラップするオブジェクトをクエリできます。 Verse では、FindOverlapHits() 関数を使用して、特定の領域内のすべてのエンティティを検索できます。
この領域は、エンティティ自体、球やボックスなどの特定のコリジョン ボリューム、またはエンティティをシミュレートする位置のいずれかになります。 これは overlap_hit のリストを返します。 それぞれの overlap_hit は、ソース ボリュームがオーバーラップしているコンポーネント/ボリュームに関する情報を提供します。これらのコンポーネントをクエリすると、関連するエンティティを見つけることができます。
次の例では、エンティティのトランスフォームが中心である、半径が 256.0 単位の球体を作成します。 次に、球体内にあるオーバーラップをすべて検出し、overlap_hit のリストを返します。 各 overlap_hit はコンポーネントまたはボリュームのいずれかであるため、TargetComponent または TargetVolume をクエリしてそのタイプを確認できます。 overlap_hit がコンポーネントである場合、コードはコンポーネントのエンティティを取得します。 最後に、エンティティにライト コンポーネントがあるかどうかを確認します。 ある場合は、ライトの色が青に変わります。 ボリュームが十分に大きければ、ほんの数行のコードで島のライトの色をすべて変更できます。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically canceled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void=
# Define a volume to find entities within.
# This is a sphere whose radius is 256.
CollisionSphere:collision_sphere = collision_sphere:
Radius := 256.0
このコードは、キューブ エンティティを中心とした半径 256.0 単位の円で示される、オーバーラップする collision_sphere ボリュームをシミュレートし、球体とオーバーラップするコンポーネントまたはボリュームを返します。 次に、オーバーラップした各コンポーネントの親エンティティを取得し、そこにあるライト コンポーネントを青色に変更します。 オーバーラップが始まるとき、キューブは collision_sphere の内側にあるため、overlap_hits のリストに含まれ、青色に変わります。 右端の赤い円錐エンティティは collision_sphere の外側にあるため、オーバーラップせず青色に変わらないことに注意してください。
スイープでエンティティを検索する
エンティティをクエリするもう 1 つの重要な方法は、スイープを使用することです。 スイープとは、オブジェクトを、特定のベクトルに沿って設定された距離にわたって移動することです。 たとえば、プラットフォーム上でブロックを移動してプレイヤーを隙間に押し込んだり、ミサイルをまっすぐ前方に発射して壁を破壊したりします。
Verse では、FindSweepHits() 関数を使用してスイープをシミュレートし、オブジェクト間のコリジョンをクエリできます。 この関数は、移動ベクトルに沿った、オブジェクトのスイープをシミュレートします。 親エンティティまたは特定のコリジョン ボリュームのいずれかを使用してスイープを実行し、スイープを開始するグローバル トランスフォームを指定できます。
FindSweepHits() 関数は sweep_hit のリストを返します。 それぞれの sweep_hit は、コンポーネントまたはボリュームのヒット、スイープを実行するソース ボリュームまたはコンポーネントなど、overlap_hit と同じ情報を提供します。 さらに、接触位置、法線、面法線、ヒットが発生したスイープに沿った距離に関する情報も提供します。
次の例では、エンティティを取得し、FindSweepHit() を呼び出して、Forward 値に長さ 1000.0 のベクトルを渡します。 その後、このコードはエンティティを正の Forward 方向に 1000.0 単位移動した場合にどのようなコリジョンが発生するのかをシミュレートし、sweep_hit のリストを返します。
各 sweep_hit はコンポーネントまたはボリュームのいずれかであるため、TargetComponent または TargetVolume をクエリしてそのタイプを確認できます。 ヒットがコンポーネントの場合、コードはコンポーネントの親エンティティを取得します。 最後に、エンティティにライト コンポーネントがあるかどうかを確認します。 ある場合は、ライトの色が青に変わります。
for:
# Simulate sweeping this entity 1000 units in the positive X direction, and return any components and volumes it overlaps with.
SweepHit : Entity.FindSweepHits(vector3{Left := 0.0, Up := 0.0, Forward := 1000.0}, Entity.GetGlobalTransform(), CollisionBox)
# Check that the overlap is a component, and if so get its parent entity.
# Then if the entity has a light component, change its color filter.
TargetComponent := SweepHit.TargetComponent
TargetEntity := TargetComponent.Entity
LightComponent := TargetEntity.GetComponent[light_component]
do:
このコードは、キューブ エンティティを正の X 方向に 1000.0 単位スイープするシミュレーションを実行し、オーバーラップするコンポーネントを返します。 次に、オーバーラップしたコンポーネントの親エンティティを取得し、そこにあるライト コンポーネントを青色に変更します。 ヒットのリストにはスイープを実行しているエンティティが含まれていないため、キューブ自体は青色に変わらないことに注意してください。 右端の赤い円錐エンティティもスイープの外側にあるため、青色に変わりません。
次の例は前の例と似ていますが、異なるのは、最初に collision_box ボリュームを構築し、それを使用してキューブ エンティティの中心から正の Forward 方向に 1000.0 単位スイープします。 スイープの開始時にキューブは collision_box 内にあるため、sweep_hits のリストに含まれ、青色に変わります。
# Define a volume to sweep over entities.
# This box is 1/4th the size of a standard 512x512 grid tile.
CollisionBox:collision_box = collision_box:
Extents := vector3:
Left := 128.0,
Up := 128.0,
Forward := 128.0
for:
# Simulate sweeping the CollisionBox 1000 units in the positive X direction, and return any components and volumes it overlaps with.
SweepHit : Entity.FindSweepHits(vector3{Left := 0.0, Up := 0.0, Forward := 1000.0}, Entity.GetGlobalTransform(), CollisionBox)
このコードは、黄色の四角形で示される collision_box ボリュームを正の Forward 方向に 1000.0 単位スイープし、オーバーラップするコンポーネントが返されるのをシミュレートします。 次に、オーバーラップしたコンポーネントの親エンティティを取得し、そこにあるライト コンポーネントを青色に変更します。 スイープの開始時にキューブは collision_box 内にあるため、sweep_hits のリストに含まれ、青色に変わります。 右端の赤い円錐エンティティはスイープの外側にあるため、青色に変わりません。
Verse によるエンティティのスポーンと削除
エンティティで RemoveFromParent() を呼び出すと、シーンからエンティティを削除できます。 親エンティティとなるエンティティで AddEntities() を呼び出すと、新しいエンティティまたは以前に削除されたエンティティをシーンに追加できます。
次の例では、Verse コンポーネントは、ゲームプレイ タグ my_tag でタグ付けされたすべてのエンティティを検索します。 見つかった各エンティティをその親から削除します。すると、シーンからもエンティティが削除されます。そして、5 秒後に同じエンティティをその親に再度追加して、シーン内に再度スポーンさせます。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically cancelled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void =
# Find all entities tagged and get their parent entity.
for:
TaggedEntity : Entity.GetSimulationEntity[].FindDescendantEntitiesWithTag(my_tag{})
Parent := TaggedEntity.GetParent[]
同様に、AddComponents() を呼び出して、追加するコンポーネントのリストを渡すことで、エンティティにコンポーネントを追加できます。 エンティティで RemoveFromParent() を呼び出すと、エンティティを削除できます。コンポーネントで RemoveFromEntity() を呼び出すことで、エンティティからコンポーネントを削除することもできます。 削除されたエンティティとコンポーネントは、それぞれ AddEntities() と AddComponents() を使用してシーンに再度追加できます。 シーンで削除して、再度追加したコンポーネントの親エンティティを変更できないことに注意してください。
プレハブ
プロジェクトで作成したプレハブは、プロジェクトの「Assets.digest.verse」ファイルの Verse クラスとして公開されます。 プレハブで定義されたエンティティとコンポーネントは、プレハブの GetEntities() と GetComponents() の呼び出しを通じて Verse でアクセスできます。
プレハブ クラスをインスタンス化し、シーン内のエンティティに追加することで、プレハブのインスタンスをスポーンできます。 次の例では、Verse コンポーネントはエディタで loop_disappearing_platform_prefab という名前のプレハブのインスタンスを作成し、それをシーンに追加しています。
# Runs when the component should start simulating in a running game.
# Can be suspended throughout the lifetime of the component. Suspensions
# will be automatically cancelled when the component is disposed or the
# game ends.
OnSimulate<override>()<suspends>:void =
if:
SimulationEntity := Entity.GetSimulationEntity[]
then:
# Create an instance of the disappearing on loop platform from its prefab.
DisappearingPlatform:disappearing_platform_prefab = disappearing_platform_prefab{}
ベストプラクティスとヒント
Verse で独自のコンポーネントを作成するときは、次のベストプラクティスとヒントを参考にしてください。
他のコンポーネントに依存する Verse コンポーネントは、通常、同じエンティティ上に存在する必要があります。
コンポーネントは、フレームごとに発生する物理演算前ティック イベントと物理演算後ティック イベントの両方を公開します。 これは、物理演算を適用する前 (トランスフォームの変更など) や物理演算を適用した後 (物理演算後のオブジェクト位置の読み取りなど) に、特定のロジックを実行する必要がある場合に便利です。
物理演算前のイベントまたは物理演算後のイベントが特に必要ない場合は、特定のロジックに基づいてタイム フローを制御するために、Verse 並列処理式を引き続き使用する必要があります。 詳細については、「タイム フローと並列処理」を参照してください。
コンポーネントの存続期間関数は仕掛けの存続期間とは異なります。 コンポーネント ロジックは編集モードおよびプレイ モードの両方で動作します。 ゲームの開始時にのみコンポーネント ロジックを実行する場合は、Verse の仕掛けの
OnBegin()関数でプレハブをスポーンできます。