ゾーンは、プレイヤーがアイテムを拾ったり、アイテムを配達したりできる (仕掛けで表される) マップのエリアです。 「タイムトライアル:ピザ配送」チュートリアルのこのステップを完了すると、これらのピックアップ ゾーンと配送ゾーンを作成し、それをプレイヤーに対してアクティブ化および非アクティブ化する方法を習得できます。
ゾーン クラスを作成するために抽象化を使用する
抽象化は、隠されている複雑な部分をユーザーが理解する必要がない場合に、不要な詳細がユーザーから隠される、プログラミングの原則です。 つまり、コードがどのように機能するのかを知らなくても、そのコードが何であるかがわかる状態です。 たとえば、自動販売機の仕組みを理解していなくても、自動販売機にお金を入れて商品を購入することはできます。
タイムトライアル:ピザ配送には、ピックアップ ゾーンと配送ゾーンという 2 種類のゾーンがあり、ピックアップ ゾーンではアイテム スポナーの仕掛けが使用され、配送ゾーンではキャプチャー エリアの仕掛けが使用されます。 これらのゾーンは異なる仕掛けであっても同じように動作することから、両方を有効/無効にして、この動作を特定の仕掛けのインタラクションを処理する汎用ゾーン オブジェクトに抽象化するクラスを作成できます。
この動作をクラスに抽象化することは、使用する仕掛けの種類を変更する場所が 1 つしかないことを意味します。 この実装では、このクラスを使用するコードはアクティブ化/非アクティブ化の関数のみについて把握するため、他のコードを変更せずにその詳細を変更できます。
このゾーン クラスを作成するには、次の手順に従います。
「pickup_delivery_zone.verse」という名前で空の Verse ファイルを新規作成し、Visual Studio Code で開きます。
この Verse ファイル内で、
base_zoneという新しいクラスをpublic指定子付きで作成し、次のように追加します。ゾーンで使用される仕掛けを格納するための、
public指定子付きのActivatorDeviceというcreative_object_interface定数。アイテムのピックアップや配達など、プレイヤーがこのゾーンとやり取りしたときに通知を行う、
public指定子を持つZoneCompletedEventという名前のイベント。ゾーンに使用される仕掛けを有効にする、戻り値の型が
voidでpublic指定子を持つActivateZone()という名前の関数。ゾーンに使用される仕掛けを無効にする、戻り値の型が
voidでpublic指定子を持つDeactivateZone()という名前の関数。Versebase_zone<public> := class: ActivatorDevice<public> : creative_object_interface ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){} ActivateZone<public>() : void = Print("Zone activated") DeactivateZone<public>() : void = Print("Zone deactivated")クラスやそのメンバーに
public指定子が付いていると、それらに他のコードからグローバルにアクセスできます。 詳細については、「指定子と属性」を参照してください。
ActivateZone()関数内で、ActivatorDeviceを異なる型に型変換し、変換後の仕掛けに対してEnable()関数を呼び出すことによって、ActivatorDeviceがキャプチャー エリアの仕掛けとアイテム スポナーの仕掛けのどちらであるかをチェックします。Disable()関数を呼び出すこと以外は、DeactivateZone()関数と同じことを行います。Versebase_zone<public> := class: ActivatorDevice<public> : creative_object_interface ActivateZone<public>() : void = Print("Zone activated") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable()private指定子とsuspends指定子を持つWaitForZoneCompleted()という名前の関数を作成します。 この関数は、仕掛け固有のイベントが発生したときにZoneCompletedEventを通知します。 この設定は、下層の仕掛けで使用されるイベントの種類に関係なく、他のコードではZoneCompletedEventを待つだけで済むことを意味しています。VerseWaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void = if (DeviceEvent := ZoneDeviceCompletionEventOpt?): DeviceEvent.Await() ZoneCompletedEvent.Signal(Self)この関数には、
DeviceEvent.Await()を呼び出すことができるように、suspendsエフェクトが付いている必要があります。ActivateZone()を、WaitForZoneCompleted()を呼び出すspawn式で更新し、仕掛けとやり取りするプレイヤーに適切な仕掛けイベントを指定します。キャプチャー エリアの仕掛けの場合はAgentEntersEvent、アイテム スポナーの仕掛けの場合はItemPickedUpEventです。WaitForZoneCompletedでは、option(?で表される)awaitable(agent)型のパラメータが想定されているため、agentと等しいパラメータ型を指定して、awaitableインターフェースを実装する任意の型を渡すことができます。CaptureArea.AgentEntersEventとItemSpawner.ItemPickedUpEventのどちらでもこの条件が考慮されるため、それらをパラメータとして使用できます。 抽象化のもう 1 つの例を次に示します。VerseActivateZone<public>() : void = Print("Zone activated") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() spawn { WaitForZoneCompleted(option{CaptureArea.AgentEntersEvent})} else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable() spawn { WaitForZoneCompleted(option{ItemSpawner.ItemPickedUpEvent}) }ZoneDeactivatedEventという名前でprotected指定子付きのもう 1 つのイベントを追加します。 このイベントは、プレイヤーがゾーンを完了する前にゾーンが非アクティブ化された場合に、WaitForZoneCompleted()関数を終了するために必要です。DeactivateZone()関数内でこのイベントを通知します。VerseZoneDeactivatedEvent<protected> : event() = event(){} DeactivateZone<public>() : void = Print("Zone deactivated") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Disable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Disable() ZoneDeactivatedEvent.Signal()プレイヤーがゾーンを完了するか、ゾーンが非アクティブ化されるまで関数が待機するように、
race式でWaitForZoneCompleted()を更新します。race式を使用すると、ZoneDeactivatedEvent.Await()の非同期関数呼び出しと、仕掛けのイベントおよびZoneCompletedEventシグナルによるblock式が同時に実行されますが、最初に終了しなかった方の式はキャンセルされます。VerseWaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void = if (DeviceEvent := ZoneDeviceCompletionEventOpt?): race: block: DeviceEvent.Await() ZoneCompletedEvent.Signal(Self) ZoneDeactivatedEvent.Await()最後に、
ActivatorDeviceフィールドを初期化するbase_zoneクラスのコンストラクタを作成します。VerseMakeBaseZone<constructor><public>(InActivatorDevice : creative_object_interface) := base_zone: ActivatorDevice := InActivatorDevicebase_zoneクラスの完全なコードを以下に示します。Verse# A zone is an area of the map (represented by a device) that can be Activated/Deactivated and that provides events to signal when the zone has been "Completed" (can't be completed anymore until next activation). # Zone "Completed" depends on the device type (ActivatorDevice) for the zone. # Suggested usage: ActivateZone() -> ZoneCompletedEvent.Await() -> DeactivateZone() # base_zone<public> := class: ActivatorDevice<public> : creative_object_interface ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){} GetTransform<public>() : transform = ActivatorDevice.GetTransform()
ゲームプレイ タグを使用して実行時にゾーンを見つける
これでゾーンを作成して有効/無効にする方法がわかりました。次は、レベルでタグ付けしたすべてのゾーンを初期化する方法と、次に有効にするゾーンを選択する方法を説明します。
この例は、ゾーンの作成と、次に有効にするゾーンの選択を行うクラスを使って、この操作を行う方法を示しています。
ゾーンを作成および選択するためのクラスを作成するには、次の手順に従います。
「pickup_delivery_zone.verse」ファイル内で、
tagged_zone_selectorという新しいクラスを作成します。 レベルにあるすべてのゾーンを格納するための変数配列を追加します。tagged_zone_selector<public> := class: var Zones<protected> : []base_zone = array{}そのゲームプレイ タグと関連付けられているすべてのゾーンを見つけてキャッシュするための
tagパラメータを持つ、InitZones()という名前のpublic指定子付きのメソッドを追加します。VerseInitZones<public>(ZoneTag : tag) : void = # On creation of a zone selector, find all available zones and cache them so we don't consume time searching for tagged devices every time the next zone is selected. ZoneDevices := GetCreativeObjectsWithTag(ZoneTag) set Zones = for (ZoneDevice : ZoneDevices): MakeBaseZone(ZoneDevice)別のゾーンを見つけるか失敗を返す
SelectNext()という名前のメソッドを、decides指定子とtransacts指定子を付けて追加します。 インデックスに対してGetRandomInt(0, Zones.Length - 1)を使用して、配列内のランダムなインデックスにあるゾーンを選択します。VerseSelectNext<public>()<transacts><decides> : base_zone = Zones[GetRandomInt(0, Zones.Length - 1)]「pickup_delivery_zone.verse」ファイルの完全なコードは次のようになるはずです。
Verseusing { /Verse.org/Simulation } using { /Verse.org/Random } using { /Verse.org/Concurrency } using { /Verse.org/Simulation/Tags } using { /UnrealEngine.com/Temporary/SpatialMath } using { /Fortnite.com/Devices } <# A zone is an area of the map (represented by a device) that can be Activated/Deactivated and that provides events to signal when the zone has been "Completed" (can't be completed anymore until next activation). Zone "Completed" depends on the device type (ActivatorDevice) for the zone.
ピックアップ ゾーンと配達ゾーンをテストする
2 つのクラスを作成できたので、次はコードをテストして、ゾーンの選択が期待どおりに機能するかどうかを確認してみてください。
次の手順に従って、「game_coordinator_device.verse」ファイルを更新します。
配送ゾーン セレクター用の定数とピックアップ ゾーン セレクター用の変数配列を、
game_coordinator_deviceに追加します。 ゲームでは、ピザをピックアップするたびにピックアップ レベルが上がっていくため、ゲームに必要なピックアップ レベルごとにtagged_zone_selectorが 1 つとPickupZoneSelectors配列も必要になります。 各ゾーン セレクタは、特定のレベルのすべてのピックアップ ゾーンを保持します。 ゾーン セレクターの設定はPickupZoneLevelTags内のpickup_zone_tagの数によって決まるため、ゾーン セレクターは変数である必要があります。この設定を使用してピックアップ レベルの数を増やすことで、コードの変更を最小限に抑えることができます。
pickup_zone_tagから派生した追加のゾーン セレクターでPickupZoneLevelTagsを更新し、エディタ内で仕掛けにタグ付けするだけで済みます。Versegame_coordinator_device<public> := class<concrete>(creative_device): DeliveryZoneSelector<private> : tagged_zone_selector = tagged_zone_selector{} var PickupZoneSelectors<private> : []tagged_zone_selector = array{}SetupZones()という名前でメソッドを追加し、そのメソッドをOnBegin()内で呼び出します。private指定子と戻り値の型voidを持つようにメソッドを設定します。配送ゾーン セレクターを
delivery_zone_tagで初期化します。ピックアップ ゾーン レベルのタグを作成し、ピックアップ ゾーン セレクターを初期化します。
VerseOnBegin<override>()<suspends> : void = SetupZones() SetupZones<private>() : void = DeliveryZoneSelector.InitZones(delivery_zone_tag{}) PickupZoneLevelTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} set PickupZoneSelectors = for(PickupZoneTag : PickupZoneLevelTags): PickupZone := tagged_zone_selector{}
OnBegin()内で、次のピックアップ ゾーンを選択してアクティブ化し、プレイヤーがそのゾーンを完了するのを待ってからゾーンを非アクティブ化するループを作成します。VerseOnBegin<override>()<suspends> : void = SetupZones() var PickupLevel : int = 0 loop: if (PickupZone : base_zone = PickupZoneSelectors[PickupLevel].SelectNext[]): PickupZone.ActivateZone() PickupZone.ZoneCompletedEvent.Await() PickupZone.DeactivateZone()Verse ファイルを保存し、コードをコンパイルして、レベルをプレイテストします。
レベルをプレイテストすると、アイテム スポナーの仕掛けの一つがゲームの開始時に有効になります。 アイテムをピックアップした後に、アイテム スポナーの仕掛けが無効になり、キャプチャー エリアの仕掛けが有効になります。 これは、手動でゲームを終了するまで続きます。