UEFN Verse言語 メタバース

【2023年】UEFNのVerseで関数を定義する方法・組み立て方

みなみ

UEFNのVerse言語で関数を定義する方法・組み立て方について教えてください!

りゅう

初心者向けにわかりやすく解説してほしいですね!

こんなお悩みを解決します。

 本記事の内容
 本記事の信頼性

この記事を書いている僕は、金融系専門卒の投資家兼ブロガーです。実際にUEFNを使っており、現在進行形で学んでいます。

今回は、UEFNのVerseで関数を定義する方法・組み立て方について解説していきます。

さっそく、やっていきましょう。

あすか

関数を定義する方法

関数を定義する方法

念のため、前提の関数の説明をします。

関数(またはルーチン)は、再利用可能なコードであり、アクションの実行や入力に基づいた出力の生成などの命令を出します。

詳しくは、【2023年最新版】UEFNのVerseで関数を呼び出す方法で解説していますので、参考にどうぞ。

【2023年最新版】UEFNのVerseで関数を呼び出す方法
【2023年最新版】UEFNのVerseで関数を呼び出す方法

続きを見る

関数シグネチャ

関数シグネチャは、関数の一意な識別子や必要な入力を表すパラメータ、および結果である戻り値の型などを使って関数を定義します。

実際の処理は、コードブロック内で関数が行う操作です。

コード ブロック

コードブロックは、関数シグネチャの後に続く一連の式のグループです。

関数の構文は以下のとおり。

name() : type =
    codeblock

コードブロックの書式にはいくつかの方法がありますが、一般的な方法は、識別子(name)の後にコード行をインデントすることです。

ただし、今の段階ではこの書式を使います(コードブロックのセットアップ方法の詳細は後で学習します)。

コードブロック内の式は、その関数が呼び出されたときに実際に実行されます。

プログラムがコードブロックの終わりに到達すると、これらの式は完了し、プログラムはその関数呼び出しの後に続くコードを実行します(少なくとも次回の関数呼び出しまで)。

要するに、コードブロックは関数の本体であり、その中に関数が行う具体的な処理を記述します。

関数が呼び出されたときに、コードブロック内の式が順番に実行されます。

これにより、関数が特定のタスクを実行できるようになります。

本体

コードブロックは、関数の本体とも呼ばれ、関数が実行する具体的な処理を定義しています。

スコープ

ここまでの学びをまとめると次のようになります。

  • 関数は、プログラム内で特定の処理を実行するために識別される一意の名前(識別子)を持ちます。
  • 関数の本体には、関数が実行する具体的な処理が定義されています。
  • 関数の実行が完了すると、その関数呼び出しの後に続くコードが実行されます。

関数のスコープは、関数内の命令やそれによって生成される値によって定まります。

関数本体内に変数を配置すると、その変数はその関数のスコープ内でローカルになります。

ここでの「ローカル」という言葉は、そのコードが関数の本体内に存在し、かつ関数が呼び出されているときにのみ存在し、動作することを意味します。

インスタンス化

インスタンス化とは、関数が呼び出されて実行される場合において、その関数の独自の適用を指します。

ちなみに、インスタンスの作成とは、インスタンス化することを意味しますね。

また、この概念によって "存続期間" という概念が提起されます。

あすか

存続期間

インスタンスには「存続期間」という概念があります。

つまり、始まりがあり、途中があり、終わりがあります。

したがって、関数本体内の要素は、本体のスコープに制限されており、そのスコープ外ではローカル変数(現在のスコープにのみ限定されている変数)にアクセスすることはできません。

条件式

以下の例は、プレイヤーが所持しているコインの数に基づいて購入できるネズミ捕り器の最大数を計算する方法を示しています。

定数MousetrapsYouCanBuyは、if文のブロック内で作成されているため、そのスコープはそのifブロックに限定されます。

if文のコードブロックの外側でMaxMousetrapsYouCanBuyを使用しようとすると、そのスコープの外側にはMaxMousetrapsYouCanBuyという名前は存在しないため、エラーが発生し、Unknown Identifierというメッセージが表示されます。

これは、「MaxMousetrapsYouCanBuy」がそのスコープ内でのみ有効であることを意味します。

あすか
UEFN VERSE ネズミ捕り器の最大数を計算
出典:EpicGames

式が完了すると、関数本体内で作成されたローカル定数やローカル変数は存在しなくなります。

これは、そのスコープの存続期間が終了したからですね。

スコープは、実行されるコードの小さなブロックと考えることができます。

関数の構文

先ほども関数の構文をご紹介しましたが、今度はさらに細かく解説します。

name() : type =
    codeblock
 構文
  • name() : type =
    • 関数シグネチャは、"name()"と"type"という要素で構成され、それらの間にコロンがあります。
    • "name()"は定数に似ていますが、"()"内には関数が必要とする入力を表すパラメータを追加します
      (パラメータの詳細はレッスン8で学びます)。
    • 関数シグネチャには、使用する入力の型も含まれます。
    • 関数は、"="の後に続く値を返す必要がありますが、その値の型はシグネチャと一致する必要があります。
  • codeblock
    • 関数の本体、つまりコードブロックには、定数や変数、そして関数が呼び出されたときに実行される式を追加します。
    • これらの式は、関数が実際に何を行うかを定義します。

たとえば、ネズミ捕り器の代金を支払うための関数は次のようになります。

var Coins : int = 500
CoinsPerMousetrap : int = 100

set Coins = Coins - CoinsPerMousetrap
Print("Mousetrap bought! You have {Coins} coins left.")

【引用:EpicGames、以後同じ】

関数名の命名規則とルールは、変数名や定数名と同じです。

関数は、一連のアクションを実行するため、関数名には実行されるアクションを明確に表す名前を付けるとよいでしょう。

例えば、「BuyMousetrap()」のように、行われるアクションを具体的に示す名前を使用することが推奨されますね。

これにより、コードを読む人が関数の目的や意図を素早く理解できるようになります。

void

void型は、その関数呼び出しにおいて何も返さないことを意味します。

具体的には、関数が結果や値を返さないことを示しますね。

voidについての詳細と使用方法については、次のレッスンで学んでいきましょう。

繰り返しを避ける

プログラミングにおいて重要な原則の1つは、「繰り返しを避けること (DRY:Don't Repeat Yourself)」です。

この原則は、同じコード行を3回以上繰り返す場合は、別のアプローチを検討して記述するべきであることを意味します。

コード行が重複するほど、コードの保守性が低下し、バグの原因になる可能性が高まってしまうんです。

あすか

例えば、1か所でコードを変更する必要がありながら、他の場所では変更されていない場合にバグが発生する可能性があります。

関数は、定数と同様に、コードの繰り返しを避ける手段の1つです。

関数を使用することで、コードの重複を回避するだけでなく、メンテナンスの容易性も向上し、コードエラーの可能性も低減されます。

ポイント

名前を付けるための式や関数、またはキーワードや組み込み関数を使用する場合、大文字の使用には注意が必要です。

Verseでは、大文字と小文字が区別されるため、大文字の使用に誤りがあるとバグやコンパイルエラーの原因になる可能性があります。

したがって、正しいかつ一貫した大文字の使用に留意することが重要です。

練習問題

UEFN VERSE 関数を定義する方法 練習問題

それでは、今回学んだ「関数を定義する方法」の練習問題をやっていきましょう。

▼ 関数名が HurtPlayer() であり、戻り値の型は void です。

これは、関数が値を返さないことを意味します。

この関数の宣言は、Verse ファイルの末尾に配置されます。

※HurtPlayer()関数はプレイヤーキャラクターに50.0のダメージを与えるだけであり、それ以上の処理や計算結果を返す必要がないため、戻り値が不要です。

関数の目的は単純にダメージを与えることであり、その結果に基づく値を返す必要がないため、Void型が適切な選択といえます。

 HurtPlayer() : void =
     Playspace: fort_playspace = GetPlayspace()
     AllPlayers: []player = Playspace.GetPlayers()
     if (FirstPlayer : player = AllPlayers[0]):
         if (FortniteCharacter : fort_character = FirstPlayer.GetFortCharacter[]):
             FortniteCharacter.Damage(50.0)

関数の内部で別の関数を定義することはできません。

したがって、すべての関数を OnBegin() と同じ階層のインデントに配置する必要があります。

あすか

これで、いつでもキャラクターにダメージを与えることができる関数が作成されました。

ただ、関数の宣言自体では関数の本体は実行されません。

▼ 関数の本体を実行するためには、その関数を呼び出す必要があります。

これらの関数は、必ず OnBegin() 内で呼び出しましょう。

 HurtPlayer()
 Print("Player Hurt")

お気づきかもしれませんが、キャラクターを回復させるためのメソッドが存在します。

そのメソッドの名前はHeal()です。

このメソッドは、Damage()と同じ方法で呼び出すことができます。

HealPlayer()関数の宣言を作成しましょう。このコードはVerseファイルの最後に配置されます。

 HealPlayer() : void =
     Playspace: fort_playspace = GetPlayspace()
     AllPlayers: []player = Playspace.GetPlayers()
     if (FirstPlayer : player = AllPlayers[0]):
         if (FortniteCharacter : fort_character = FirstPlayer.GetFortCharacter[]):
             FortniteCharacter.Heal(20.0)

次に、HealPlayer() の呼び出しを、OnBegin() のボディ内の関数呼び出しに追加します。

 HurtPlayer()
 Print("Player Hurt")
 Sleep(5.0)
 HealPlayer()
 Print("Player Healed")

お気づきかもしれませんが、Sleep()関数の新しい呼び出しが追加されています。

Sleep()関数は、プログラムを一時停止し、指定した時間(秒単位)だけ待機させるために使用されますね。

具体的な待機時間は、Sleep()関数の引数に渡されるパラメータによって指定されます。

ここでのポイントは、Sleep()関数がない場合、HealPlayer()関数はHurtPlayer()の直後にすぐ実行されるため、2つの関数呼び出しが同時に行われたことに気づかないでしょう。

まとめ

UEFN VERSE 関数を定義する方法 まとめ

今回は、関数を定義する方法・組み立て方について解説しました。

 今回のまとめ
  • 関数は再利用可能なコードであり、アクションを実行したり、入力に基づいて出力を生成したりするための指示を提供します。
  • 関数は、3つの要素で構成されています。まず、識別子、そして関数が呼び出されたときにどのような結果を返すかを定義するコードブロックです。
  • 関数は、その関数シグネチャによって呼び出されます。
  • コードブロックは、関数の本体とも呼ばれ、関数が実行する内容を定義します。
  • スコープもコードブロックに関連付けられており、関数内で定義された値が有効な範囲を決定します。
  • コードブロック内のコードは、そのスコープ内でローカルな変数として扱われます。
  • 存続期間 は、スコープの期間のことです。
  • 繰り返しを避けます

また、今回の演習で扱ったコードは以下のとおりです。

 完全なスクリプト
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Playspaces }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }

hello_world_device := class(creative_device):

    # Runs when the device is started in a running game
    OnBegin<override>()<suspends>:void=

        HurtPlayer()
        Print("Player Hurt")
        Sleep(5.0)
        HealPlayer()
        Print("Player Healed")  

    HurtPlayer() : void =
        Playspace: fort_playspace = GetPlayspace()
        AllPlayers: []player = Playspace.GetPlayers()
        if (FirstPlayer : player = AllPlayers[0]):
            if (FortniteCharacter : fort_character = FirstPlayer.GetFortCharacter[]):
                FortniteCharacter.Damage(50.0)

    HealPlayer() : void =
        Playspace: fort_playspace = GetPlayspace()
        AllPlayers: []player = Playspace.GetPlayers()
        if (FirstPlayer : player = AllPlayers[0]):
            if (FortniteCharacter : fort_character = FirstPlayer.GetFortCharacter[]):
                FortniteCharacter.Heal(20.0)  

次回は、関数の結果を指定する方法についてやっていきます。

がんばっていきましょう🐣

>> 【2023年】UEFNのVerseで関数の結果を指定する方法

【2023年】UEFNのVerseで関数の結果を指定する方法
【2023年】UEFNのVerseで関数の結果を指定する方法

続きを見る

※参考文献:Epic Games

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

あすか(Asuka)

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

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