第15回 関数を作ってみる

前回の振り返り問題

下の画像では、まず変数TestValueのデフォルト値が表示されます。次にHelloが表示されます。では、最後にオレンジ色で表示される値は何でしょうか?

b15-1 (画像 1)
答は次のようになります。
b15-2 (画像 2)
つまり、999が正解です(一番上に表示されているものが一番最後に表示されたものです)。デフォルト値の0が表示された後に、セットノードによって999がTestValueにセットされているのですから。

「あたりまえ」と思われた方は、以下の説明を飛ばして、さっそく今回の本編に入ってください。「TestValueは、最初のPrint Stringの前に置かれている。この時点ではデフォルト値の0になっている。その値を引っ張ってきて3つ目のPrint Stringで表示されているので、0が表示されるはずだ…」と考えた方は次のように考えてください。

TestValueの値を考える場合、どのTestValueノードを使っているかは問題になりません。TestValueノードに固定的な値が入っているのではないからです。TestValueノードを使うことによって、その時点でメモリ上に置かれているTestValueの値を取りに行くのですから。どのTestValueノードを使っても同じなのであれば、もちろん次のようにしても(画像 1)のグラフと同じ結果になります。
b15-3 (画像 3)
このTestValueはセットノードによって値が999に変更されています。そのセットノードを実行した後は、どのTestValueノードを利用してメモリ上にあるTestValueの値を取って来ても999になっていいます。

以上のことは当たり前のことなのですが、最初は混乱することもあるでしょう。混乱しそうになったら、メモリの存在を思い浮かべてください。そのことは、第13回 変数の型について調べてみようの「【ノート】① 型とメモリ」で取り上げられています。

関数とは

さて、今回の本題である関数です。まず、ゲームでHPの値を変更する場合を例にとって考えてみます。キャラクター(ただし今回の場合、キャラクターはこのBoxになっています)があるゾーンに入るとHPの値から60が引かれてから2倍するような計算をするとします。たとえば、HPのデフォルト値が100の場合にそのゾーンに入ると、
(100-60)*2=80
となります。ところで、「キャラクターがあるゾーンに入る」ための設定はUE4では簡単にできるのですが、関数以外の説明が増えるのでそのやり方は次回以降に回して、「0~1000のループで666になったら」をその代用とします(あまり関係ないのですが)。つまり、0~1000のループで666になったらキャラクターのHPが、(100-60)*2という計算で80になるようにします。そのためのブループリントは次のようになります。
b15-6 (画像 4)
順番に見ていきましょう。まず、ゲームが開始されるとイベントBeginPlayに続くノードが実行されるので、ForLoopが実行されます。このループは0~1000のループを行います。その時、Indexの値が666に等しい時、==ノードの右側のピンから送られる値はtrueになります。そのtrueがブランチノードに渡されて、右側のTrueピンにつながるノードが実行されます。この場合はHPのセットノードです。このHPセットノードのHPピンにつながっている値が新たに変数HPにセットされます。その前に、HPの値は、引き算ノード(マイナスのマークがついています。右クリックメニューからこのマイナスマークで検索してIntegerーIntegerと書かれているものを選択します)で、60を引かれてから、掛け算ノードで2倍されます。そして、その2倍された値がHPのセットノードにつなげられていて、HPに新たな値としてセットされます。さらにPrint Stringで表示されます。結果は下の画像のようになります。
b15-7 (画像 5)

さて、これはこれでよいのですが、ここでもし、MPの値も同じ計算をしなければならないのでしたら、どのようなグラフを作るべきでしょうか?MPのデフォルト値を200とすると、次のようなグラフを作らなければなりません。
b15-8 (画像 6)
結果は次のようになります。(MPの計算は (200-60)*2=280 です)
b15-9 (画像 7)
しかし、どうもすっきりしないのは、同じ計算を2回やっている点です。下の画像の赤枠の部分です。
b15-10 (画像 8)
このような「同じことを2回以上やる」というのはなるべく避けるべきです。2回ならまだ我慢できたとしても、この先その計算が3回以上必要となるかもしれません。何度も同じことを繰り返している場合(今後繰り返されると予想できる場合)、「工夫してその回数を減らせるのではないか?」という発想が今後プログラミングしていく上で大切になってきます。それもプログラミングの楽しみの一つかもしれません。

では、どうしたら減らせるのでしょうか?共通の処理(ここでは、60を引いてから2倍すること)をくくり出して(頭の中で抽出して)、それを何らかの手段で独立させ、その独立したものをHPでもMPでも利用できるようにすればよいのです。そのくくり出して独立させたものが関数です。実際に関数を作りながら具体的にどういうことなのか見ていきましょう。

関数を作ってみる

まず、関数を作るには、[マイブループリント]パネルで+関数ボタンをクリックします。
b15-11 (画像 9)
すると、次のように、関数を作るためのタブが新たに開かれます。
b15-12 (画像 10)
まず、関数の名前を付けます。そのためには、[マイブループリント]パネルの「新規関数~」をクリックして好きな名前に変更します。今回はHPなどの計算なので、単にKeisanとしてみました。

次に「インプット」と「アウトプット」の設定を行います。関数とは簡単に言えば、ある値が与えられると、その値に対して何らかの処理をして、その結果を関数の呼び出し側に返す(答える)ノードです(そうではない関数もいずれ扱います)。先の(100-60)×2=80の計算のことを例にとると、100という値が関数呼び出し側から関数に入ってくると、関数はその値から60を引いて2倍してから、80を答として関数呼び出し側に出します(返します)。この「入る」値と「出る」値がブループリントではインプットとアウトプットと呼ばれています。

それではまず入ってくるHPやMPの値の受け皿となるインプットを作りましょう。そのためには、[詳細パネル]の[インプット]で[新規]をクリックしてから、Parameterというところに名前を付けます。今回はNouryokuとします。変数の型はInteger(整数)型にします。HPなどと同じ型にするのです。さて、このNouryokuが、関数呼び出し側から入ってくるHPなどの値を受け止めます。簡単に言えば、このNouryokuにHPなどの値が入ります。
b15-13 (画像 11)

次に計算結果として出て行くことになるアウトプットも作っておきます。[詳細パネル]の[アウトプット]で[新規]をクリックすると、リターンノードというのが出現します(「返り値」という意味です。アウトプットの値を関数呼び出し側に返すためのノードです)。さらに、[アウトプット]欄で名前をNew Nouryokuとします。そして、計算部分は白枠で囲ったようにします。
b15-14 (画像 12)
説明を加えると、Nouryokuピンから引っ張ってきて引き算ノード(マイナス記号で検索できます)の一方のピンに接続して、もう一方のピンには空欄に60を入れます。引き算ノードの結果は右側のピンから出るので、それを掛け算ノードの一方のピンに接続し、もう一方のピンには空欄に2を入れます(この計算本体の部分は画像 8とまったく同じノード構成になっています)。さらに、掛け算ノードの結果が出て来る右側のピンからワイヤーを延ばしてリターンノードにつなげます。これでNouryokuピンに入ってくる(たとえば)HPの値から60引かれて、2倍され、返り値として関数の呼び出し側に戻されることになります。本当にそうなるか、関数を使ってみましょう。

関数を使ってみる

[イベントグラフ]タブに戻って、次のようにkeisanで検索してみてください。
b15-15 (画像 13)
上のようにUE4はちゃんとKeisan関数を認識しており、「関数呼び出し」の項目に表示してくれます。これをクリックして出してください。
b15-16 (画像 14)

これがKeisan関数を呼び出すためのノードです。先ほどの関数本体のノード(画像 11)は紫色でしたが、この呼び出しのためのノードは青です。このように関数のノードは、呼び出す側のノードと計算を請け負う関数本体のノードがあります。関数を活用する時は呼び出しノードを使います。関数を作る時は本体の紫のノードを使うことになります。

さて、この関数を材料にして次のようなグラフを組んでみます。
b15-17 (画像 15)
関数を作った時に説明が出ていますが、NouryokuピンにはHPをつないでいます。HPの値100をKeisan関数に渡しているのですね。そして、NewNouryokuピンからは(100-60)×2=80が関数からの返り値として出て来るはずです。この値をPrint Stringノードに接続して表示するようにしています。実行してみましょう。
b15-18 (画像 16)
ちゃんと80と表示されました。

では、(画像 6)のグラフを関数を使ったバージョンに修正してみます。
b15-19 (画像 17)
(画像 6)に比べると引き算ノードと掛け算ノードがなくなってKeisanノードになっているので、若干すっきりしました。あまり関数化した効果が感じられないかもしれませんが、もしノードをたくさん使うような複雑な計算だったなら、その効果はもっと分かりやすくなったでしょう。

また、次のようなKeisanHyoujiという関数を作って、表示部分も関数の中に入れ込んでしまえば(ついでに返り値も返さないようにしてみました)、画像19のようにものすごくすっきりさせることができます(画像6と比べてみてください)。
b15-20 (画像 18)
b15-21 (画像 19)

[振り返り問題]
float型の配列を作り、index 0から3までに、2, 0, 1, 7 というデータを入れて、その平均値を求めてください。ただし、平均を求める部分はご自分で関数化してください。(これまで紹介した知識だけで組むことができます。)

【ノート】
① (画像 8)の計算部分だけではなく、新たな値のセットについても関数に任せることができます。
b15-22 (画像 20)
その場合、下のようなKeisanSet関数を作ります。
b15-23 (画像 21)
この関数には変数の値だけではなく変数の参照というものも渡さなければなりません。これについては、今後取り上げます(非常に重要な概念であり、動画チュートリアルで頻繁に使われているので、基礎的な橋渡しの役割を目指している本ページとしても避けて通ることはできません)。なお、このKeisanSet関数は次のように使います。実行結果はもちろん(画像 8)のグラフと同じになります。
b15-24 (画像 22)
グラフはかなりすっきりしていますね。ただし、注意しなければならないのは、背後で(関数の中で)HPとMPの値が変わってしまっているという点です。この画像22のグラフだけを見るとそれが分かりません。バグが出て、それが何が原因なのか発見できない時などに、これが落とし穴になっている可能性が出てきます。特に注意が必要なやり方なのです。

② すでにお気づきのことかもしれませんが、実はこれまで多用してきたPrint Stringなども関数です。その証拠にPrint Stringの頭には f のマークがついています。function (関数)の印です。これからはそのようなノードは関数を付けて呼ぶことにしたいと思います。なお、この関数も戻り値は返していません。

[振り返り問題]
float型の配列を作り、index 0から3までに、2, 0, 1, 7 というデータを入れて、その平均値を求めてください。ただし、平均を求める部分はご自分で関数化してください。(これまで紹介した知識だけで組むことができます。)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする