UEFN Verse言語 メタバース

【UEFN】移動する目標マーカー【Verseチュートリアル】

【UEFN】移動する目標マーカー【Verseチュートリアル】

こんにちは、あすか(@aars_inc)です。

今回は、移動する目標マーカーについて見ていきます。

※以下のコードの解説文は、プログラミング初心者の主がなんとか調べて記述したものです。

参考程度にどうぞ。

あすか

誤りがありましたら恐縮ですm(_ _)m

もくじ

移動する目標マーカーの概要

目標マーカーは、多くのゲームでプレイヤーを次の目標や注視点にガイドするために使用されます。

このチュートリアルでは、マップ インジケーターの仕掛け と Verse を使用して、再利用可能な目標マーカーを作成する方法について説明します。

ドキュメント

マップインジケーターでは、ミニマップと全体マップ上にカスタムの注目スポットとマーカーを設置することができます。

マップのインジケーター(指標)のことですね。

あすか

マップインジケーターの詳細は、以下の記事を参考にしてください。

>> 公式ドキュメント:マップインジケーター

使用するVerse言語機能

struct: 構造体 内のさまざまな 型 の 変数 をグループ化できます。

拡張メソッド:特別なタイプの 関数 で、既存の クラス または型の メンバー のように機能しますが、新しい型または サブクラス の作成を必要としません。このガイドでは、構造体の拡張 メソッド を作成します。

名前付き引数: パラメータ 名を指定して 関数呼び出し にパスされる 引数 です。

ドキュメント

使用するVerse API

Prop API: Prop API は、小道具を動かすための手段を提供します。

編集可能なプロパティ: 迅速にテストするため、仕掛けの参照と変数 値 の更新の両方に複数のプロパティを使用します。

ドキュメント

移動する目標マーカーのVerseコードを徹底解説

次の手順に従って、複数の目標や注視点に移動できる、単一の目標マーカーの仕掛けの設定方法を確認します。

完全なスクリプトは、参照用としてこのガイドの末尾に記載されています。

ドキュメント

以下、出典:EpicGames公式ドキュメント

完全なスクリプト

 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")

両者の価値提供

完全なスクリプトを2つに分ける理由

Objective_markerの価値

Objective_coordinator_deviceの価値

マーカーの制御とは

プレイヤーの検索とは

アクションの実行の詳細

ゲーム内の進行管理の詳細

両者のまとめ

レベルを設定する

レベルを設定する
この例では、次の小道具と仕掛けを使用します。

建築小道具 x 1:マップ インジケーターの仕掛けを移動するために使用される 小道具 です。

マップ インジケーターの仕掛け x 1:ミニマップおよび概要マップにカスタム仕様のマーカーを表示する仕掛けです。

プレイヤー スポーン パッドの仕掛け x 1:これを小道具の近くに追加することで、近くでプレイヤーがスポーンします。

ドキュメント

▼ まず、『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{}

<public>の詳細

creative_propクラスの詳細

creative_prop とcreative_objectの違い

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")
# 小道具を指定された位置と回転に瞬時に移動させるためのコード

if (RootProp.TeleportTo[Destination.Translation, Destination.Rotation]):
    # RootProp.TeleportTo メソッドが成功した場合の処理
    Print("Prop move successful")
else:
    # RootProp.TeleportTo メソッドが失敗した場合の処理
    Print("Prop move failed")

TeleportToメソッドの詳細

アクセス修飾子とは

Destinationプロパティから位置情報と回転情報を取得することについての詳細

[]の詳細

失敗する可能性について

失敗コンテキストとは

Verseコード以外は割愛します。

公式ドキュメントを参照するようお願いします。

>> EpicGames公式ドキュメント

 コード
 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 を使用して仕掛けを作成する方法 については説明しましたが、独自の仕掛けを持たない Verse ファイルを作成することもできます。

1.新しい Verse ファイルを作成し、名前を「objective_marker」とします。このファイルは仕掛けを作成しません。代わりに、前に作成した Verse の仕掛けに公開される struct の定義を含んでいます。

2.まず、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{}
# 「objective_marker」という公開される構造体を定義します
objective_marker<public> := struct<concrete>:

    # 「RootProp」という編集可能なメンバーを宣言します
    # 型は「creative_prop」で、初期値は「creative_prop{}」です
    @editable
    RootProp<public> : creative_prop = creative_prop{}

    # 「MapIndicator」という編集可能なメンバーを宣言します
    # 型は「map_indicator_device」で、初期値は「map_indicator_device{}」です
    @editable
    MapIndicator<public> : map_indicator_device = map_indicator_device{}

独自の仕掛けを持たないVerseファイルを作成する理由

アタッチとは

パブリック指定子と編集可能指定子の詳細

objective_marker~の詳細

map_indicator_deviceクラスの詳細

拡張メソッドと名前付き引数

単一のメソッドである MoveMarker を 宣言 します。これは、RootProp メンバーとそれにアタッチされたマップ インジケーターの仕掛けを移動します。

このメソッドは、拡張メソッドと名前付き引数という 2 つの言語の機能を導入します。

ドキュメント
  • 拡張メソッド:
    • MoveMarker() メソッドを objective_marker 構造体に追加します。
    • 拡張メソッドでは識別子と型をコロンで区切り、それらを括弧で囲んで宣言します。
    • この場合であれば、(Marker : objective_marker) となります。
  • 名前付き引数:
    • 2 番目の引数 ?OverTime は、? を使用することで MoveMarker 関数呼び出しで名前を指定する必要があることを示します。
    • これにより、MoveMarker への呼び出しを参照したり記述したりするデベロッパーにとって、float 引数が行う処理が理解しやすくなります。
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =

2つの引数の詳細

suspendsの詳細

?OverTimeの詳細

名前付き引数の詳細

通常の引数の順序を変えられない理由

名前付き引数が作られた理由

名前付き引数によって定義に変化はあるのだろうか。

【重要】OverTimeで名前付き引数を使うメリット

if..elseブロックを作成

MoveMarker() は、先ほど使用した TeleportTo[] または MoveTo() のいずれかのメソッドを Prop API から呼び出します。

if..else ブロックを作成し、パラメータ OverTime が 0.0 より大きいかをテストします。

大きい場合は、MoveTo() を呼び出します。
これにより、すぐにテレポートするのではなく、指定した期間の経過後に目標が次の場所に移動します。

ドキュメント

このコードを現時点でコンパイルすると成功はしますが、コンテンツ ブラウザ内の 「CreativeDevices」 フォルダには新しい仕掛けが表示されません。

これは、objective_marker が struct であり、creative_device から継承されたクラスではないためです。

ドキュメント
(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]
# MoveMarker() メソッドは、objective_marker マーカーを移動します。
# パブリック (public) なメソッドとして定義されています。
# <suspends> キーワードは、メソッドが一時停止できることを示します。
# void 型は、メソッドが値を返さないことを表します。
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
# もし OverTime の値が 0.0 より大きい場合は、目標位置への滑らかな移動を行います。
# MoveTo() メソッドを呼び出し、Marker の RootProp を指定した位置と回転で移動させます。
# Transform.Translation は移動先の座標、Transform.Rotation は回転情報を表します。
if (OverTime > 0.0):
    Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
# OverTime の値が 0.0 以下の場合は、目標位置への瞬時のテレポートを行います。
# Marker の RootProp を指定した位置と回転に即座にテレポートさせます。
else:
    if:
        Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]

OverTimeの詳細

(Marker : objective_marker).Mov~の計算式の論理

RootPropを入力する理由

Marker.RootProp.MoveTo()の詳細

MoveTo()の詳細

TeleportTo[]の詳細

TeleportToが[]を使用している理由

structの詳細

クラスじゃないとインスタンスできない??

目標コーディネーターの仕掛けを更新する

新しいタイプの参照ができたため、今度は objective_coordinator_device を参照するように更新する必要があります。

①RootProp プロパティを削除し、型が objective_marker の PickupMarker というプロパティで置き換えます。これは、自分で作成した型です。

②MoveMarker() には float 型の引数が必要であるため、これを MoveTime という名前の編集可能なプロパティとして作成します。

③TeleportTo[] への呼び出しを削除します。代わりに、objective_marker のために作成した MoveMarker() メソッドを呼び出します。これには、名前付き引数である ?OverTime が必要です。


このコードをコンパイルし、目標コーディネーターの仕掛けの詳細を確認します。PickupMarker プロパティおよび MoveTime プロパティが表示されます。また、PickupMarker には RootProp および MapIndicator が含まれています。

ドキュメント
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)
# 'objective_coordinator_device'クラスを定義します。このクラスは公開範囲があり、具体的なスクリプトとなっています。
# このクラスは'creative_device'クラスを継承しています。
objective_coordinator_device<public> := class<concrete>(creative_device):

# 'PickupMarker'という名前の公開範囲で編集可能なプロパティを定義します。
# このプロパティは`objective_marker{}`という既定値を持ちます。
@editable
PickupMarker<public> : objective_marker = objective_marker{}

    # マーカーが移動する場所
# 'Destination'という名前の公開範囲で編集可能なプロパティを定義します。
# このプロパティは`transform{}`という既定値を持ちます。
@editable
Destination<public> : transform = transform{}

    # マーカーが新しい場所に到達するまでにかかる時間
# 'MoveTime'という名前の公開範囲で編集可能なプロパティを定義します。
# このプロパティは`0.0`という既定値を持ちます。
@editable
MoveTime<public> : float = 0.0

# 'OnBegin'メソッドをオーバーライドします。
OnBegin<override>()<suspends> : void =

        # 'PickupMarker'オブジェクトの`MoveMarker()`メソッドを呼び出し、`Destination`を移動先の位置として指定します。
# また、オプション引数`OverTime`には`MoveTime`の値が使用されます。
PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)

RootPropプロパティを削除するのは管轄だから。

MoveMarker() には float 型の引数が必要であるため、これを MoveTime という名前の編集可能なプロパティとして作成します。の詳細

OverTimeとMoveTimeの関係性

TeleportTo[] への呼び出しを削除します。代わりに、objective_marker のために作成した MoveMarker() メソッドを呼び出します。これには、名前付き引数である ?OverTime が必要です。の詳細

わざわざTeleportTo[]への呼び出しを削除する理由

PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)の計算式

名前付き引数を使うメリットの詳細

つまり、上記のコードはすべて現場監督側の視点です。

あすか

GetPlayers() と ActivateObjectivePulse()

プレイヤーが次の目標にたどり着けるように、手助けできる手段が用意されています。

これは 目標パルス と呼ばれ、これが有効なときはプレイヤーから マップ インジケーターの仕掛け に向かって破線が表示されます。

以下の手順に従って、目標パルスを目標コーディネーターの仕掛けに追加します。

ドキュメント
目標パルス
出典:ドキュメント

目標パルスを有効にするために必要なメソッドは ActivateObjectivePulse() で、これには agent 型の引数が 1 つ必要です。

まず、プレイヤーのキャラクターを表す agent のインスタンスを取得するメソッドの作成から始めましょう。

ドキュメント

FindPlayer() という関数を <private> に設定して宣言し、戻り値は void にします。

Self.GetPlayspace().GetPlayers() を使用し、レベル内のすべてのプレイヤーの配列を取得します。AllPlayers という変数にこの配列を格納します。

 FindPlayer<private>() : void =

     AllPlayers := Self.GetPlayspace().GetPlayers()
# プレイヤーのキャラクターを表す agent のインスタンスを取得するためのメソッドを作成します。
# FindPlayer() という関数を <private> に設定して宣言し、戻り値は void にします。
FindPlayer<private>() : void =

# レベル内のすべてのプレイヤーの配列を取得します。
# AllPlayers という変数にこの配列を格納します。
# プレイヤーの配列を AllPlayers という変数に格納します。
     AllPlayers := Self.GetPlayspace().GetPlayers()

プレイヤーの情報が必要な理由

FindPlayer()~の計算式

目標パルスとは

パルスの由来

パルスがパルスである理由

Selfのインスタンスの詳細

継承の詳細

FindPlayer() という関数をprivateに設定した意図

privateにするときの主な例

if (FirstPlayer := AllPlayers[0]):について

▼ レベル内のプレイヤーが 1 人のみの場合にその参照を取得するには、最初の配列要素を独自の変数に割り当てます。

配列へのアクセスは 失敗する可能性がある式 であるため、if 式の中に配置します。

 if (FirstPlayer := AllPlayers[0]):
# AllPlayersがプレイヤーの参照を持つ配列であることを前提とします
# 最初のプレイヤーの参照を取得し、FirstPlayerという変数に割り当てます。
if (FirstPlayer := AllPlayers[0]):

失敗する可能性がある式の詳細

option型の変数の使用について

▼ 変数への 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
# objective_coordinator_deviceクラスを宣言し、creative_deviceクラスを継承します
objective_coordinator_device<public> := class<concrete>(creative_device):

    # PlayerOptというプロパティを宣言し、初期値をfalseとします
    var PlayerOpt<private> : ?player = false
        
    # マーカーの指定された位置を表すobjective_markerという変数を宣言し、初期値としてobjective_marker{}を与えます
    @editable
    PickupMarker<public> : objective_marker = objective_marker{}

    # マーカーが移動する先の場所を表すtransformという変数を宣言し、初期値としてtransform{}を与えます
    @editable
    Destination<public> : transform = transform{}

    # マーカーが新しい場所に到達するまでにかかる時間を表すfloat型の変数MoveTimeを宣言し、初期値として0.0を与えます
    @editable
    MoveTime<public> : float = 0.0

▼ 変数への player の割り当ては失敗する可能性があるため、コード内でプレイヤーを参照する際には、option 型の変数を使用することもできます。の詳細

?演算子とoption{}の使用方法

PlayerOptの詳細

目標コーディネーターデバイスにプレイヤーを格納する理由

コーディネーターとは

コーディネーターの歴史

コーディネーターの由来

コーディネーターとマネージャーの違い

まとめ

▼ 新しい変数を設定し、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")
FindPlayer<private>() : void =
    # これは単一のプレイヤー体験なので、最初のプレイヤー [0] のみが使用可能となる必要があります。
    AllPlayers := Self.GetPlayspace().GetPlayers()

    # `FirstPlayer` 変数を定義し、`AllPlayers` リストの最初の要素を代入します。この行では、条件式と同時に代入も行っており、最初のプレイヤーが存在する場合にのみ、条件が真となります。
    if (FirstPlayer := AllPlayers[0]):
        # `PlayerOpt` 変数に `option{FirstPlayer}` を代入します。これにより、`PlayerOpt` 変数は最初のプレイヤーを保持するオプション型の変数となります。また、"Player found" というメッセージを表示します。
        set PlayerOpt = option{FirstPlayer}
        Print("Player found")
    else:
        # 上の `if` 条件式が偽の場合、つまりプレイヤーが見つからなかった場合に実行されるブロックです。"Can't find valid player" というメッセージを表示します。
        Print("Can't find valid player")

FindPlayerの意味

FindPlayer() : void =がない場合

条件式とoptionを使っている理由

FindPlayer() : void =に()が付く理由

voidと()について

引数を受け取らないことに関して

プレイヤーを検索するには、このメソッドが特定の情報やデータを入力として必要としません。代わりに、このメソッドはobjective_coordinator_deviceクラスのインスタンスの状態や内部の他のデータメンバーにアクセスします。の詳細

引数を受け取る際は同じクラス内に情報がないことを意味する

目標パルスの表示

OnBegin() 関数に戻り、次の 2 つの変更を加える必要があります。

ドキュメント

FindPlayer() 関数を呼び出します。

 OnBegin<override>()<suspends> : void =

     FindPlayer()

MoveMarker() の呼び出し後、別の if 式を使用してオプションのプレイヤーの変数を新しい変数に設定し、それを PickupMarker.MapIndicator.ActivateObjectivePulse() の引数としてパスします。

 if (FoundPlayer := PlayerOpt?):
     PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
if (FoundPlayer := PlayerOpt?):  # プレーヤーが見つかった場合の条件分岐
    # プレイヤーが見つかった場合に実行されるコード
    PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)  # プレイヤーを目標パルスに渡してアクティブにする

ActivateObjectivePulse関数の詳細

2回も条件式を使うのは単なる保険

PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)の論理

各キーワードの意味

目標マーカーの表示を担うコード

PickupMarkerの詳細

目標パルスをアクティブ化させるのに目標マーカーが必要になる理由

まとめると

よくある質問

プログラミングが英語ベースな理由

404コードとは

失敗する可能性があるコンテキストの[]の詳細

transform構造体とは

Destination.Translationについて

Destinationとは

タプルとリストの違い

イミュータブルの詳細

イミュータブルが使われるとき

イミュータブルの語源

小道具を使う理由

Objective_marker.verseは構造体として定義

構造体の詳細

アーキタイプとは

concreteの詳細

RootPropを記述する理由

 obujective_coordinator_deviceから↓

PickupMarker : objective_marker = objective_marker{}はobjective_markerを参照していることの詳細

プログラミングで-が使われないで_が使われる理由

識別子が識別子である件について

インスタンスの名前の由来

オブジェクト指向プログラミングの考え方

パラダイムとは

パラとは

objective_coordinator_device := class(creative_device):の詳細

複合型とは

メンバーの詳細

フィールドとプロパティの詳細

メンバの詳細

ネスティングの詳細

compositeの詳細

creative_deviceを継承した理由

フィールドとメソッドについて

Onbeginから上のコードは、フィールドの定義

変数の意義

OnBegin() : void =下の詳細

Objective_coordinator_device.verseのフィールドの詳細

OnBegin関数の詳細

まとめ

今回は、移動する目標マーカーについて解説しました。

最後に、完全なスクリプトを再掲しておきますね↓

 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")

今回もお疲れさまでした🍵

  • この記事を書いた人
  • 最新記事

あすか(Asuka)

自己紹介:UEFNクリエイター
Verse言語独学中 | UE・プログラミング・3Dモデリング 6月開始 | 備忘録として学習記録をブログに残しています。
■好きな言葉
徳は弧ならず必ず隣あり.
■ひとこと
まだまだ未熟者ですが、
よろしくお願いします。

-UEFN, Verse言語, メタバース
-, ,