🗂

[UEFN][Verse]ゼロからUEFNでプロジェクトを作成し、フォートナイトの島で最小限のサンプルコードを動かす記事

2023/12/10に公開

この記事は、Unreal Engine (UE) アドベントカレンダーの10日目の記事です。
https://qiita.com/advent-calendar/2023/ue
 普段はVerse言語の文法のマニアックな仕様をつつき回しているのですが、今回は初心にかえって、「オブジェクトを円運動させる」というシンプルなコードを書いてみます。
 UEFN/Verse言語を知らない方向けに、UEFNエディタの作業手順やVerse言語の文法仕様について厚めに解説していますんで、これまでUEFN/Verseに触れた事が無い方も、よければ読んでくださいませー!

UEFN/Verseの超簡単な説明

「UEFN(Unreal Editor for Fortnite)」はフォートナイト用のUGCツールで、フォートナイトのオリジナル島を作成できます。Verse言語は、Epic Gamesが開発した「関数論理型プログラミング言語」という特殊な言語パラダイムに属する、オリジナルのプログラミング言語です。そして、現時点ではUEFNで使用できるプログラミング言語はこのVerseのみです。

今回の記事では、最小限のVerse言語のサンプルコードを構築し、実際にフォートナイトマップ上で動作させてみたいと思います。

サンプルコードの動作イメージ

サンプルコードを実行するとこのように、椅子のオブジェクトがくるくると同じ位置で円運動を繰り返します。

サンプルコード

こちらが今回のサンプルコードです。

#①標準ライブラリのインポート
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }

#②カスタムデバイスクラスを定義
sample_device := class(creative_device):

    #③パラメータ
    @editable 
    TargetProp:creative_prop = creative_prop{}
    @editable 
    Radius:float = 80.0

    #④エントリポイント
    OnBegin<override>()<suspends>:void=
        #⑤定数定義:プロップの初期座標を回転の原点とする
        Origin := TargetProp.GetTransform().Translation
        #⑥変数定義:回転角(ラジアン)
        var Theta:float = 0.0

        #⑥以下セッションが終了するまでループし続ける 
        loop:
            #⑦回転を計算
            V := vector3 { X := Cos(Theta), Y := Sin(Theta), Z := 0.0 }
            #⑧オブジェクトを0.1秒かけて移動(移動が終わるまで処理は返らない)
            TargetProp.MoveTo(Origin + Radius * V, rotation {}, 0.1)
            #⑨回転角を増加
            set Theta += (2 * 3.1416)/20.0
            #⑩次のフレームまで待機
            Sleep(0.0)

ゼロからプロジェクトを構築する

今回のコードを実行するまでの手順を説明します。UEFN/Forniteのインストールは済んでいる物とします。

①UEFNを起動してプロジェクトを新規作成する

まず、新規のプロジェクトを作成します。UEFNではプロジェクトの単位はマップでありマップの事を「島」と呼びます。

UEFNを起動するとプロジェクトブラウザが開きます。左側の「島テンプレート」ペインをクリックすると、ベースとして選択出来る島のリストが表示されます。
 ここでは「オアシス島」を選択します。「作成」をクリックするとプロジェクトが作成されます。

これが初期画面。真ん中に小さく見える小島が今回のマップです。まずは島まで移動します。
 方法は色々ありますが、ここではアウトライナーで「CP_Prop_Rock_02_C0」を選択し「F(Forcus)」キーを押します。

選択したオブジェクトがフォーカスされ、オアシス島に到着しました。

②回転させるプロップを配置

さっそく、回転させる対象物を島に配置しましょう。UEFNでは、動かせたり壊せたりするオブジェクト群を「プロップ」と呼びます。
 UEFNには、これまでフォートナイトのために作成された膨大なプロップが予め用意されています。試しに適当な椅子を選んでみましょう。
 コンテンツブラウザの「Fortnite」フォルダを選択し、テキストボックスに「chair」と入力します。

椅子(chair)が山ほど現れました。ドラッグして島に配置します。

③Verseファイルを作成

次に、島を回転させるためのコードを用意します。

 VerseExplorerタブを開き[1]、プロジェクト名を右クリックして「Add new verse file to project」を選択します。

テンプレートコードは必要無いので「空のスクリプトを作成」を選びます。

プロジェクトにVerseファイルを追加する確認を求められるので「選択内容を保存」を選択します。

空のVerseファイルが追加された事が、VerseExplorerタブ上で確認出来ます。

④VSCodeでコードを記述

作成したVerseファイルにサンプルコードを記述します。追加したファイルをダブルクリックしてください。

VSCodeが起動するので、先述したサンプルコードをまるっと貼り付けます。

これで準備が出来ました。Ctrl+Sでファイルを保存します。ただし、ここで保存しなくても、UEFNにフォーカスが移った時に自動的に保存される筈です。

⑤creative_deviceを配置

UEFNでは、プログラムによってインタラクションされるオブジェクト群を「デバイス」と呼びます。UEFNでは、プロップと同じように、様々なデバイスがあらかじめ提供されています。例えば、ユーザーのインタラクションによってボタンをオンオフできるスイッチは「スイッチデバイス(SwitchDevice)」という具合です。

ユーザーが作成したVerseコードは、ビルドすることで「クリエイトデバイス(CreativeDevice)」の派生オブジェクトとなり、島に配置出来るようになります。

というわけでまずはサンプルコードをビルドしましょう。UEFNに戻ってビルドのショートカットキーであるCtrl+Shift+Bを押します。ビルドは数秒で終わります。

ビルドが終わると、コンテンツブラウザタブのCreativeDevicesフォルダ配下にsample_deviceが追加されます。これがサンプルコードをビルドして作成されたデバイスになります。これを、椅子と同じように島にドラッグ&ドロップします。

⑥デバイスにパラメータを設定

さて、配置したデバイスのパラメータとして、先程の椅子を設定します。
 島に配置したデバイスを選択し、詳細(Detail)タブ>TargetPropのリストをクリックし、追加した椅子プロップ(ArtDeco Bank Chair 01)を選択します。

これで準備は終わりました。

⑦プレイヤースポナーのセットアップ

最後に、プレイヤースポナーのセットアップを行います。
 ゲーム開始時にプレイヤーがポップする位置は「プレイヤースポナーデバイス」で設定します。
 オアシス島には初期状態でプレイヤースポナーデバイスが配置されているのですが、なぜか座標が島から遠く離れた海上でして、このままだとまともに操作出来ないので、デバイスを設定しなおします。
 まず、コンテンツブラウザでプレイヤースポナーを検索し、島にドロップします。

元から配置されていたプレイヤースポナーは不要なので削除します。アウトライナー上で「プレイヤースポナー」を選択してデリートキーを押します(先程追加した方は「プレイヤースポナー2」として表示されています)。

設定はこれだけ。ゲームを開始すると、新しいプレイヤースポナーの位置にプレイヤーがスポーンします(正確には、新規追加したデバイスはそうなるように初期設定されています)。

⑧セッションを開始

全ての準備が終わりました。ゲームを起動しましょう。
 「セッションを開始」を選択すると、先程と同じようなウィンドウが開きます。

これはプロジェクトが未保存であるためで、「選択内容を保存」を選択すればプロジェクトが保存され、セッションが開始します。
 フォートナイトのアプリが起動し、しばらく待てばプレイヤーがオアシス島にスポーンします。島を見渡せば、回転している椅子を確認できます。

サンプルコードの解説

ここからはサンプルコードの解説です。Verse言語の文法も合わせて解説していきます。

#①標準ライブラリのインポート
using { /Verse.org/Simulation } #Verseの標準ライブラリ
using { /Fortnite.com/Devices } #Fortniteのデバイス群のライブラリ
using { /UnrealEngine.com/Temporary/SpatialMath } #数学関連のライブラリ

using式はライブラリをインポートします。上2つのライブラリはほぼ常にインポートする事になります。
 Verseでは#以降の文字列はコメントになります。

#②カスタムデバイスクラスを定義
sample_device := class(creative_device):

クラス定義にはclass式を使います。ここではcreative_deviceクラスを継承したsample_deviceを定義しています。
 Verseではスコープをインデントで表します。この行から半角空白でインデントされた全ての行がクラス定義に含まれます。

    #③パラメータ
    @editable 
    TargetProp:creative_prop = creative_prop{}
    @editable 
    Radius:float = 80.0

クラスメンバとしてcreative_prop型TargetProp定数とfloat型Radius定数を定義しています。Verseではこのように定義された変数は定数扱いになります。
 @editable属性を付与した定数(変数)は、エディタ上でパラメータを設定出来るようになります。TargetProp定数には任意のプロップを指定できます(今回は椅子を設定しました)。Radius定数は回転の半径になります。

    #④エントリポイント
    OnBegin<override>()<suspends>:void=

OnBegin()メソッドを定義します(引数無し、戻り値無し(void型))。Verse言語にはMain()のようなエントリポイントが用意されておらず、通常はこのOnBegin()メソッドを使用します。creative_device派生クラスがマップに配置された状態でゲームが起動すると、このOnBegin()メソッドが実行されます。
 "<override>"や"<suspends>"は「指定子(specifire)」と呼ばれる物です(今回は説明を省略します)

        #⑤定数定義:プロップの初期座標を回転の原点とする
        Origin := TargetProp.GetTransform().Translation

プロップの初期座標を取得し、Vector3型Origin定数に定義します。この式は、以下と同じ意味です。

        Origin:Vector3 = TargetProp.GetTransform().Translation

Origin定数の型(Vector3)は型推論できるため、省略できるのです。

        #⑥変数定義:回転角(ラジアン)
        var Theta:float = 0.0

varを付与すると変数定義となり、後から値を更新可能になります。

        #以下セッションが終了するまでループし続ける 
        loop:

loop式は、ブロック内のコードを永久に繰り返します。

            #⑦回転を計算
            V := vector3 { X := Cos(Theta), Y := Sin(Theta), Z := 0.0 }

現在の回転角に応じて、回転後の座標を算出します。

            #⑧オブジェクトを0.1秒かけて移動(移動が終わるまで処理は返らない)
            TargetProp.MoveTo(Origin + Radius * V, rotation {}, 0.1)

MoveTo()メソッドは、プロップを現在座標から指定座標に一定時間かけて移動させます。プロップの初期位置を原点、Radiusを半径に回転させています。
 このメソッドが実行されると、プロップの移動が終了するまでコードは先に進みません。ただし、ここでは移動時間(ミリ秒)に0.1を指定しているのですぐに処理が戻ってきます(このような使い方は実際には変則的です)。

            #⑨回転角を増加
            set Theta += (2 * 3.1416)/20.0

回転角を増加させます。変数の更新はset 変数名 = 値という記法になります。

            #⑩次のフレームまで待機
            Sleep(0.0)

Sleep()は指定時間(秒数)処理を中断(=システムに処理を戻す)します。0.0を指定した場合は、1フレームのみ待機し、次フレームから処理を再開します。
 このSleep()メソッドは本当は必要無いのですが、省略するとプロップの回転がぎこちなくなるので入れています[2]

以上です。お疲れ様でした。

終わりに:宣伝:同人誌Verse言語解説本のご紹介

今回紹介したVerse言語の言語仕様を解説した同人誌を電子書籍で頒布しています。「関数論理型プログラミング言語Verse」に興味を持たれましたら、是非ご検討下さい。
https://s-games.booth.pm/items/5326231

お知らせ

verse言語とUEFNの記事を他にも書いているので御覧下さい。
https://zenn.dev/t_tutiya

最後まで読んで頂きありがとうございました。この記事がお役に立てたようであれば、是非LIKEとフォローをお願いします(今後の執筆のモチベーションに繋がります)。

#Verse #UEFN #Fortnite #Verselang #UnrealEngine

宣伝

「Unityシェーダープログラミングの教科書」シリーズ1~5をBOOTHで頒布中です。
https://s-games.booth.pm/

脚注
  1. VerseExplorerが無い場合は、上部メニューから"Verse>Verse Explorer"を選択 ↩︎

  2. Sleep()が無いと、前の回転の最後のフレームと次の回転の最初のフレームが重複するからじゃないかと考えていますが、検証していません ↩︎

Discussion