前回の振り返り問題
float型の配列を作り、index 0から3までに、2, 0, 1, 7 というデータを入れて、その平均値を求めてください。ただし、平均を求める部分はご自分で関数化してください。(これまで紹介した知識だけで組むことができます。)
この場合の平均値は、
(2+0+1+7)÷4
で求めることができます(答は2.5です)。このような計算を行う関数Heikinは次のようになります。
(画像 1)
どのような仕組みになっているか、たどってみましょう。まず関数Heikinが受け取るデータ(ブループリントでは「インプット」と言います。一般的には「引数」と言います)は、Dataという名前を付けました。このDataはFloat型の配列にしています。つまりFloat型の配列しか受け取りません。
受け取ったDataは、ForEachLoopにつながっています。このループは配列に特化したループでしたね。配列を受け取るとそのすべての要素(Element)についてループを実行します。具体的には、Data配列のインデックス0の要素から順番に処理していきます。まず、Data配列のインデックス0に入っている値をArray Element(配列の要素)ピンから取り出し、それをGoukeiという変数と足し算してその結果をGoukeiに入れています。(その部分のノードを先の画像に赤枠をつけてみます。)
(画像 2)
この部分重要です。自分自身に自分自身の値を入れているような感じがして、不思議に思う方がいるかもしれませんので、表を使ってもう少し詳しく追ってみます。
Dataのインデックス | Dataの値 | Goukeiの値
(足し算前) |
Goukeiの値
(足し算の値をセットした後) |
0 | 2 | 0 | 2 |
1 | 0 | 2 | 2 |
2 | 1 | 2 | 3 |
3 | 7 | 3 | 10 |
まず表の1行目(つまりループの1回目)。Dataのインデックスが0の時、Dataの値は2です。その時の変数Goukeiの値はデフォルト値の0です(この時点までに変数Goukeiの値は書き換えられていませんので)。さて、Data+Goukeiが行われますが、今回は2+0のことですから答は2となります。この2という値が変数Goukeiに新たにセットされます(正確に言うと、Goukeiのためのメモリに2がセットされます)。
次に2行目。Dataのインデックスが1の時、Dataの値は0です。その時の変数Goukeiの値は2です(先に「Goukeiのためのメモリに2がセット」されると説明したとおりです)。0+2=2なので、2が変数Goukeiにセットされます(前の値と変わりないのです)。3行目では、Dataのインデックスが2の時、Dataの値は1です。その時の変数Goukeiの値は2です。1+2=3なので、3が変数Goukeiに新たにセットされます。最後の行では、Dataのインデックスが3の時、Dataの値は7です。その時の変数Goukeiの値は3です。7+3=10なので、10が変数Goukeiに新たにセットされます。
このように合計はちゃんと出ます。たとえ変数が自分自身に自分自身の値を入れてもまったく問題ありません。
変数Goukeiの今の値+別の値 → 変数Goukeiの新たな値
とすることは何ら問題ありません。むしろプログラムではよく使われる技法です。
さて、長くなりましたが、ループが完了したら、Completed(完了)の実行ピン以下が実行されます。つまり、
10÷4
を実行しているのですが、細かく見ると、現在のArray Index(配列のインデックス)に1を加えて4にしてから、
現在のGoukei÷4
を行って、その答をHeikinchiとしてこの関数の呼び出し元にリターンしています。その呼び出し元では、次のようにしてみました。
(画像 3)
Float型の配列TestDataを作って、デフォルト値で2,0,1,7をセットしておき、それを関数Heikinに渡しています。すぐさま、Heikinchiピンから2.5を受け取り、それを表示しています。
なお、これは関数なので、これ以外の配列のためにも当然使えます。このような汎用性がなければ関数にしてもあまり意味がありませんからね。たとえば、要素数の異なるTestAtaiという配列を作ってもちゃんと平均値が出てきます。
(画像 4)
(画像 5)
イベントとは
イベントとは一般的には出来事という意味です。ソフトウェアでは、あるボタンがユーザーによってクリックされたなどがイベントに当たります。たとえば、ウェブブラウザで拡大のボタンが押されるのもイベントです。ウェブブラウザはそのイベントに対してウィンドウを拡大するという動作を行います。あるいは、キーボードのCtrlキーとWキーが同時に押されるのもイベントです。この場合はタブが閉じるなどの動作になるかもしれません。ブループリントでも同じで、あるキーが押されるというイベントが発生したら、キャラクターなどがある動作を行うようにすることができます。
すでにお気づきのとおり、イベントはこれまで何回も使ってきました。先の画像3でもイベントBeginPlayを使っています。「ゲームが開始した」というイベントが発生したら、確かに、何らかの動作や処理を行っています(画像3の場合は関数を使って平均を求めて表示しています)。このように、ブループリントでは、あるイベントが発生したら、そのイベントノードに続くグラフが実行される、ということになります。
さまざまなイベント
これからいくつか代表的なイベントを紹介していきたいと思います。まず、Tick(ティック)というイベントがあります。これは、フレームが更新される度に呼び出されるイベントです。「フレームの更新」とはゲーム画面で描画を更新することです。ゲームでキャラクターなどが動いているように見えるのは、パラパラ漫画や映画と同じで、1枚の静止画(フレーム)を高速で差し替えて連続で表示しているから脳が錯覚を起こして動いているように見えます。このようにフレームを差し替えることをフレームの更新と呼んでいます。ティックノードは、フレームが更新される度に呼び出されるノードです。たとえば、120fps(frames per second=1秒あたり差し替えられるフレームの数)の場合は、Tickも1秒間に120回呼び出されることになります。Tickが呼び出されると、他のイベントノードと同じように、それに続くノード(下の画像ではPrint Stringが2つ)が実行されます。
(画像 6)
これを実行すると、1秒間に180回の速さで表示が一気に行われます。
(画像 7)
何かにぶつかった時に発動するイベントはHitと呼ばれています。
(画像 8)
プレイボタンを押してからキャラクターをCubeのところまで走らせ(操作はキーボードのWASDキーを使います。最初にゲーム画面の任意の場所をクリックするとキー入力が受け付けられるようになります)、CubeにぶつからせるとHitイベントが発動します。
(画像 9)
ただし、このHitイベントが発動するためには重要な条件があります。それは、このCubeのコリジョン設定が他のアクタをブロックするように設定されていることです。そのためには、[コリジョン プリセット(衝突に関する事前設定)]という項目をDefault(デフォルト)、または、BlockAll(すべてを通さない)にします(DefaultではBlockAllになっています)。
(画像 10)
これがOverlapAll(すべてと重なる)になっていると、キャラクターはCubeを通り抜けることができ、Hitイベントは発動しません。(Hitというのは「ぶつかる」という意味なので、キャラクターが湯船にでもつかっているような状態になり、Hitなんてものではありません。)
(画像 11)
次は、キーボードのキーを押すと発動するイベントについてです。そのためには、まずThirdPersonCharacterというブループリントを開きます(なぜこれまでのMyCubeを使わないのかという理由については、ノート①をご覧ください)。
(画像 12)
これはキャラクターのためのデータと操作が入っているブループリントです。もちろん、そこにデータと操作(イベントや関数など)を新たに追加することもできます。そこで、次のようなグラフを追加してみます。
(画像 13)
左端のノードはFキーイベントです。右クリックメニューからFで検索することができます。
プレイボタンを押して画面上を一回クリックしてからFキーを押すと、Fイベントに続くノードが実行され、次のようになります。
(画像 14)
なお、キーボードのイベントはアルファベット以外にもいろいろあります。
(画像 15)
このようにイベントにはさまざまなものがあります。このようなイベントが発生した時にキャラクターや他のアクタに何かをやらせたり、データの値を変化させたりして、ゲームは進んで行きます。次回はカスタムのイベントについて勉強してみます。
振り返り問題
キャラクターが何かにぶつかった時に、そのHPが10ずつ減るようなグラフを組んでみましょう。キャラクターのHPはInteger型の変数にして適当な名前を付けてください。
【ノート】
① ThirdPersonCharacterというブループリントはその名前からも分かるように、キャラクターの動きなどを制御するためのブループリントです。ここで一つ重要なことを紹介しておきたいと思います(今回のテーマからは若干外れるのですが)。それは、プレイヤー(つまりゲームをプレイする私たちです)が動かすキャラクター(たとえば、画像11のグレイのキャラクター)は、目には見えないPlayerControllerというアクタに所有(Possess)されることになっているということです。言い方を換えますと、PlayerControllerに所有されなければ、そのキャラクターをプレイヤーは動かすことができません。さらに、言い換えれば、PlayerControllerがキャラクターを所有することによって、キーボードなどからの入力をキャラクターが受け取ることができるようになります。さらにさらに、言い換えれば、PlayerControllerはキャラクターとプレイヤーの橋渡しをする役割がある、ということになります。先ほどはWASDキーを使ってキャラクターを動かしましたが、プレイヤーがたとえばWキーを押すと、その入力情報がPlayerControllerにもたらされ、それによってキャラクターが前方に動かされることになります。
ところでなぜこのような面倒なことをしているのかと言えば、そうすると便利なことがあるからです。たとえば、キャラクターが死んでしまってそのデータ(能力値やアイテムなど)も一緒に消滅するのでは扱いづらいので、PlayerControllerにそのようなデータを最初から託しておきます。こうすることによってキャラクターが新生しても簡単に前のデータを継続させることができます。
ところがMyCubeなどは、もともとプレイヤーが操作できるもの(「ポーン」と言います)ではないので、PlayerControllerが所有することはできず、したがってキーボードなどからの入力を受け取ることもできません。ただし、Enable Inputというノードを使うと、PlayerControllerに入ってくる入力のイベントをMyCubeで受け取って利用できるようにはなります。
(画像 16)
(画像 17)
これについては、クラスの勉強の時に再度取り上げたいと思います。
② なお、HitイベントやBeginPlayイベントノードなど最初から用意されているイベントノードは、関数や次回取り上げるカスタムのイベントとは異なり、そのブループリントの中で何回(何個)も使えるものではありません。あるイベントとそれによって引き起こされる動作は一対一対応になっていると考えると良いでしょう。