【第13回】移動する足場②
- 概要
- 確認用レベルを作成する
- 座標を変える「Set Actor Location」ノード
- 「Tick」で移動させる
- SplineComponent を使って位置を取得する
- 足場アクタから、Splineアクタの情報を取得する
- レベル上で参照を設定する
- SplineからLocationを取得する
- 数値の増加処理と移動を組み合わせる
- アクタの移動状況を判断する
- アクタが終点(始点)に到着したか判断する
- (補足)変数のウォッチ
- レベルにアクタを配置して値を調整する
- (補足)処理を工夫する
- プロジェクトのダウンロード
概要
今回はスムーズに曲線的な動きをする足場を作ります。
使用頻度の高い「Tick」イベントで「Set Actor Location」ノードを使った移動と、「Spline(スプライン)」を利用した座標の参照を学びます。今までより少しだけ難しい機能を扱うので、じっくり腰を据えて進めていきましょう。
実装したい機能は以下の通りです。
- 指定した複数ポイントを経由してメッシュを目的地まで移動させる
- 目的地まで移動したら、往路と同じルートを通って開始位置まで移動する
- 指定した速度で移動させる
- 複数個配置できるようにして、個別にパラメータを設定できるようにする
確認用レベルを作成する
確認用のレベルを作成しておきます。
ゲームモードも設定しておきましょう。
詳しくは以前の解説を参照してください。
座標を変える「Set Actor Location」ノード
アクタやコンポーネントの位置を変更するノードはいくつか存在しますが、まずは使用頻度の高い「Set Actor Location」を覚えましょう。
これはターゲットのアクタを指定した座標に変更するノードです。
使ってみます。
まずは処理を載せるアクタを新規作成。
「追加/インポート」→「ブループリントクラス」→「Actor」の順に選択し新たなアクタを作成します。
作成するフォルダは前回作成した足場アクタと同じ場所が良いでしょう。名前は任意で構いません。
ブループリントエディタを開いて、メッシュを設定してください。
今回も別のコリジョンは設定せず、メッシュ自体の当たり判定を使います。
設定したら、イベントグラフを開きましょう。
「Begin Play」イベント付近を右クリックして「Set Actor Location」を検索して配置します。
このノードは実行されると、ターゲットの座標を「New Location」で指定した位置に変更します。
初期状態ではターゲットが「Self」となっています。自身(このアクタ)が動くと考えてください。
確認してみましょう。
「Begin Play」イベントと実行ピンを接続し、仮の数値を入れてためしてみましょう。
なお、指定する座標は「ワールド座標」です。
図では、Z座標に500を入力しました。
確認用のレベルに配置しましょう。
(配置したアクタはワールド原点から離しておきます)
シミュレートでプレイしてみます。
プレイボタン右側の「▼」を押して、モードを「シミュレート」にして開始。
少々わかりづらいですが、起動直後に位置が変わったことに気づくはずです。
起動直後でわかりづらい場合は、直前にDelayノードなどを差し込むといいかもしれませんね。
この座標指定を「アクタの現在位置」+「移動したい距離」に変更してみましょう。
現在位置は「Get Actor Location」で取得できますね。
右クリックからノードを検索して設置してください。
続けて、「Get Actor Location」ノードのReturn Value出力ピンからワイヤーを伸ばして「+」と検索して「Vector + Vector」ノードを設置します(絞り込み範囲を限定するために必ず出力ピンからワイヤーを伸ばして検索しましょう)
適当な値をいれておきます。
ここでは X に 「1」を指定しました。
これで「アクタの現在位置」+「移動したい距離」が完成です。
プレイしてみましょう。
先ほどと異なり、自身の場所から指定方向(図ではZ+500)だけ移動しましたね。
これを繰り返すと、アクタを移動させることができます。
例として、もっとも簡単な「Delay」による繰り返しをしてみましょう。
指定した値によっては少しわかりづらいかもしれませんが、しっかり移動しています。
とはいえ、上図の状態ではアクタの向きを変えたときでも、常にワールド方向「X:1.0」方向へ進むため、アクタの向きや形状を考慮した動きをしてくれません。
もちろんそれが有用な場合もありますが、アクタの向きに準じた動きをしてくれた方が配置しやすいですよね。
「アクタから見て前方方向」に移動させるようにしてみます。
UE4で前方方向は「Forward Vector」と言われ、この場合は「X」を指すと考えましょう。
アクタのForward Vectorは、「Get Forward Vector」ノードから取得できます。
「Get Actor Forward Vector」を検索して配置します。
このノードはアクタが向いている方向からワールドの向きを算出して返してくれます。
例えば、アクタが回転していない場合(R: 0.0 , P:0.0 , Y:0.0)は(X: 0.0, Y:0.0 , Z:0.0)ですが、
アクタが45度回転していた場合(R: 0.0, P:0.0, Y:45.0)、(X: 0.707, Y:0.707 , Z:0.0)が返されます。
「アクタの向きに合わせて前方方向への方向を出してくれる」くらいに考えておくと簡単かもしれません。
プレイしてみましょう。
若干分かりづらいですが、常にアクタの前方方向へ進むようになりました。
このように「Set Actor Location」を使えば、「アクタの現在位置」+「移動したい距離」の指定を繰り返すだけで簡単にアクタの移動が実現できてしまいます。
ただこのようにDelayで繰り返しする方法は、あまり一般的ではなく汎用性も高くありません。
つづけて、一般的な「Tick」を使って移動させる方法も学習しましょう。
「Tick」で移動させる
「Tick」(ティック)とは、ブループリントグラフにあるイベントです。
アクタの初期状態では無効になっていますが、なにかノードを繋げば有効化します。
この「Tick」イベントは、基本的に1フレーム(1F)に1回実行されることが特徴で、「何かの処理をフレーム単位で継続して行いたい」場合に多用されるイベントと覚えておけば問題ありません。
1フレームごとに、このノードに繋がった処理が繰り返されると考えると良いかもしれません。
もちろん処理のあるブループリントアクタがレベル上に存在している必要があります。
より詳しい内容を知りたい方は公式サイトをご覧ください。
たとえば、図のように組んでプレイしてみましょう
Begin Play の処理は削除してください。
1フレームに1回「Set Actor Location」が実行されるようになりました。
先程の「Delay」接続よりもなめらかに移動するように見えるはずです。
ただ移動させるだけならこれで完成です。簡単ですね。
しかし、この処理のままでは少しばかり問題点があります。
実のところUE4では、1フレームにかかる秒数はスペックに依存しており一定ではありません(可変フレームと言われます)。そのため、上記のような組み方の場合、スペックの低いPCと高いPCで移動速度に差が出てきてしまいますし、処理負荷の影響によっても1フレームにかかる時間は可変されてしまうのです。
公平な環境が求められるゲームにおいて、これは致命的ですね。
筆者は過去それを知らずに「徐々に迫ってくる致死エリア」を作ったことがあります。プレイした環境によってエリアの速度が変わるため、なぜかプレイした人によって難しさが違う!? という恐ろしい経験をしました。
それを回避する手段として一般的なのは「Delta Seconds 」値を使う方法です。
「Delta Seconds 」値は、Tick ノードの下にあるピンから、あるいは「Get World Delta Seconds」ノードから取得できます。
「Delta Seconds」からはフレーム間の時間が出力されており、フレームごとに処理する計算に対しては、この値を掛けることでどんな可変フレームにも対応できる、と覚えておけば大丈夫です。
やってみましょう。
「Get Forward Vector」出力ピンからワイヤーを伸ばして 「Vector * Float」ノードを配置します。
図のように移動量に対してDelta Seconds を乗算するように接続してください。
そして現在の座標に加算します。
プレイしてみましょう。
意図的に処理負荷などをかけないと分かりづらいですが問題ないはずです。
うまく動くようになりましたが、速度が足らないので対応しましょう。
プレイ時に120FPSが出ているPCの場合、Delta Seconds からは 1フレームごとに0.0083程度が出力されるため、当然それが乗算されて遅くなります。ということは、元の値を大きくすれば問題ないはずです。
SplineComponent を使って位置を取得する
さて、現在位置と移動したい場所の座標さえわかれば「Set Actor Location」を使って移動できることがわかりましたね。続いては「Spline(スプライン)」のコンポーネントである「Spline Component」を利用して座標を取得します。
「Spline(スプライン)」は「3D空間上に自由に引くことができる線」と考えてください。
空間上にいくつもの「スプラインポイント」と呼ばれる点を指定して作成します。
それぞれのポイントとポイントは線で結ばれていて、任意の場所へ動かしたり、好きなポイントを増減できる特性を持ちます。
今回はブループリントを使って、この「スプラインポイントの座標」を取得して移動に利用します。
追加してみましょう。
「Spline」コンポーネントのみのActorブループリントを作成する
「追加/インポート」>「ブループリントクラス」>「Actor」で作成します。
名前は何でも構いません。
ダブルクリックしてブループリントエディタを開いてください。
コンポーネントパネルから「Spline」(Spline Component)を追加。
Spline Meshと間違えないように注意しましょう。
構造上、SplineをRootにしても問題ありません。コンポーネントからSplineをRootまでドラッグ&ドロップすることでSplineをRootに変更できます。
図のようなSpline(白いライン)が追加されました。
保存したあと、テスト用レベルに配置しましょう。
Spline(スプラインポイント)を編集します。
ブループリントのビューポート上でスプライン形状を編集しても構いません。
今回はレベルで編集して確認します(ある程度はブループリントエディタ上で形状を変更して微調整するのもアリですね)
レベルのアクタをクリックして選択します。
この状態は、まだアクタを選択している状態であり、そこに設定されたコンポーネントを選択している状態ではありません。
さらにそこからスプラインポイントをクリックします。
根本付近のスプラインポイントは、DefaultSceneRootコンポーネントがあるので見づらいかもしれませんが、ズームして押しましょう。
図のようなマークが出れば、スプラインポイントが選択できています。
さらに、詳細パネルの「Sppline」を選択しましょう。
詳細パネルにて、「Selected Points」という項目が確認できるのがわかります。
これが現在選択しているポイントの設定です。
ポイントが選択されてないとこの設定は確認できません。
位置はローカル座標なので、原点と同じ場所にある1個目のポイント(インプットキー0)の位置は全て0となっていますね。その先にある2個目のポイントを選択してみると
インプットキーが「1.0」のパラメータが位置が表示されることがわかります。
パラメータを変えてみましょう。
詳細パネルの位置にある値を入力するか、手動でポイントを雨後します。
ポイントの両脇にある「方向点」を選択して移動させれば、Tangent値を変更することも可能です。これを調整することで、微妙なカーブを描かせることが可能になります。
ポイントを増やしてみましょう。
ポイントをクリックして右クリックメニューから複製を選ぶことで増加できます。
あるいは「Alt」キーを押しながらポイントを動かすことでも同様にポイントを追加できます。個人的にオススメなのはこちらですね。
好きなように増やしてみましょう。
右クリックやDeleteキーでポイントを削除することもできます。
また、スプラインポイント・方向点ともに向きがありますので注意しましょう。
微調整段階では詳細パネルから値を打ち込んでもいいかもしれませんね。
適当に作成できたら次へ進んでください。
足場アクタから、Splineアクタの情報を取得する
Splineの設定ができましたね。
次に、足場アクタブループリントからSplineの情報を参照できるようにしましょう。
足場アクタのブループリントを開いてください。
マイブループリントパネルから変数を作成します。
わかりやすく「Spline Actor」としました。
詳細パネルから変数の型を変更しましょう。
プルダウンから「BP_SplineActor」型(Object Reference)を選びます。
先ほど命名したアクタ名を持つ変数です。
以前、変数は格納される情報に合わせた型にする必要があると学習しましたね。
この「BP_SplineActor」型の変数には、「BP_SplineActor」しか格納することができません。
しかもこの型の変数からは、その中にある情報をすべて抜き出すことができます。
つまり、(変数に格納さえすれば)Splineの情報も抜き出すことができるのです。
最後に「インスタンス編集可能」にチェックを入れておきましょう。
この設定がないと後ほど学習する項目が実行できないので注意してください。
レベル上で参照を設定する
続けてSplineを参照できるか確認してみましょう。
マイブループリントパネルからSplineActor変数をドラッグ&ドロップしてゲットします。
続けて出力ピンからワイヤを伸ばしてSplineコンポーネントを取得しましょう。
これはSplineアクタのSplineコンポーネントの情報にアクセスしていると考えてください。
ポイントのLocationを取り出してみましょう。Splineコンポーネントの出力ピンから、ワイヤーを伸ばして「Get Location at Spline Point」を検索して配置します。
このノードは引数で指定した「Point Index」のLocationを返します。
「Point Index」 はインプットキーの番号に対応しています。
ワールド座標を得たいので、Coordinate Spaceを「World」に変更します。
ためしに「Begin Play」イベントで、「PrintString」に接続して確認してみます。
プレイするとわかるのですが、うまくPrintStringが表示されませんね。
停止するとエラーメッセージが現れます。
これは、変数が格納されていないので参照できないというエラーです。
レベル上で、『「BP_SplineActor」のアクタ』を指定してあげなければなりません。
レベル上の足場アクタを選択して、詳細パネルの「Spline Actor」変数のプルダウンから、BP_SplineActorを指定しましょう。
もしこの項目がない場合は、変数の「インスタンス編集可能」にチェックが入っているか確認してください。
これで「BP_SplineActor」の指定を行えました。
再度プレイして確認してみましょう。
今度はポイント0の座標が正しく表示されましたね。
ここまでくれば、あとは移動と組み合わせるだけです。
SplineからLocationを取得する
正しく参照を取得できるようになったところで、Spline の線上にある座標を取得する「Get Location at Distance Along Spline」ノードを使ってみましょう。
Splineの出力ピンからワイヤーを伸ばし、検索して配置しましょう。
このノードはSplineの「線の位置」に合わせたLocationを返します。
例えば Distance 0.0 ならば スプラインポイント 0 (始点)の座標ですね。
つまりSpline の長さそのものを入力すれば、最終地点のLocationを取得できることになります。
「Coodinate Space」を「World」に変更することもお忘れなく。
Spline の長さを取得する方法は、「Get Spline Length」を使います。
ためしに、足場アクタをSplineの最終地点に移動する処理を作ってみましょう。
Tickにある処理はノードを切断して実行されないようにしてください。
シミュレートで実行してみます。
Splineの先端にアクタが移動しましたね。
Spline Length の値 = Splineの先端位置
あとはこの方法を応用し、Tickでフレームごとに段階的にDistance値を増やしていけば足場アクタは、Splineの位置に合わせて動くようになるはずです。
数値の増加処理と移動を組み合わせる
では、Tickを使って、増加していく数値の処理を作りましょう。
この章の最初に学習した移動処理と同様に考えます。
図のように組んでみましょう。
「Float」型の変数「Current Distance」を作成しています。
増加させたい値を保存するために「Current Distance」としてFloat変数を作成し、そこに「Delta Seconds」を足して保存しなおしています。
増加する値を見てみるには、「PrintString」を接続してみましょう。
フレームごとに「Current Distance」の値が増えていることを確認できましたね。
先ほどのSpline処理の「Distance」ピンに接続してみましょう。
プレイして確認します。
移動はしているようですが、非常に遅いですね……。
「Delta Seconds」に数値を乗算して、加速しましょう。
うまく動いてくれましたね。乗算部分を変数にしておきましょう。
レベルに設置したあと個別に速度を変更できるよう、変数を「インスタンス編集可能」にしておきます。
アクタの移動状況を判断する
アクタがスタート地点に戻ってくる処理をつくります。
まずは、現在進んでいるのか戻っているのかを判定するフラグの変数をBoolean型で作成します。
「IsReturn」という変数を作成しました。
デフォルト値は「False」(偽)です(Trueなら戻っている状態と判断させる)。
現在進んでいるか戻っているかを判定する処理を「Tick」イベント内に作ります。
アクタが戻っているときは、Trueに処理が流れるとします。
その際には逆方向に移動するよう、移動処理に「-1」を乗算しましょう。
「Current Distance」の乗算演算子ノードの「+」を押すことでピンを追加できます。
図のように接続します。
移動処理は変わらないので分岐したノードをひとつにまとめます。
「Is Return」のデフォルト値を切り替えて動作を確認してみたいところですが、「True」にしても開始地点が0のままですので、そのままでは確認できません。
「Current Distance」のデフォルト値を変更して確認しても構いませんが、確認後は0に戻しておいてください。
アクタが終点(始点)に到着したか判断する
移動処理のあと、アクタが端に到達したかを判断します。
再び「Is Return」をブランチで分岐させます。
移動距離(長さ)は「Current Distance」変数に保存されていますので、それを使って比較します。
終点はSplineの「Get Spline Length」から取得できますね。
比較するブランチを接続しましょう。
対して始点の比較は「0」以下かどうかを分岐するだけですね。
「True」の処理は始点に到達したとき、「False」の処理は終点に到着したかを判断します。
それぞれ条件を満たしたら、「Is Return」フラグを切り替えるようにします。
変数のセットをつかって切り替えます。
保存・コンパイルしてプレイしてみましょう。
行ったりきたりするようになったでしょうか?
一連の処理の全体像はこちらです。
(補足)変数のウォッチ
現在どれくらい動いているか?を知りたいとき、簡単な方法として変数「Current Distance」を「Print String」で表示する方法がありますが、もうひとつ、「ウォッチ」という確認方法があります。
変数の文字部分を右クリックして見ましょう「この値をウォッチ」という項目を選びます。
図のようなフキダシが現れるはずです。
好きなピンにウォッチを設定できます。
もしピンが非アクティブ化している場合は、コンパイルなどを済まして確認してください。
ゲームを実行すると、ノードの流れとともに(動作に合わせて)、その変数の状態が確認できるはずです。処理の流れをみるときなどに便利な機能ですので、「Print String」と合わせて適宜使ってみてください。
レベルにアクタを配置して値を調整する
レベル上にアクタを設置して、移動アクタ側でSplineの参照とスピードを設定します。
今回はSplineの座標のみを参照していますので、角度などは気にしなくても大丈夫です。ひとまずざっくり配置してみました。調整しつつ組み込んでいきましょう。
処理によっては角度を指定することなども可能です。
動かして確認してください(図ではわかりやすくするためSplineの色を変えています)。
今回は以上です!
余裕があれば、アイデアを出して改良してみましょう。
たとえば、現状ではプレイヤーの状態に関わらず動き出しますので、プレイヤーが指定位置に移動したら動き出すように変更したり、あるいはプレイヤーが乗ったときだけ動くようにするとか、始点終点にきたら一瞬待機させてみるなどなど……。
またSplineには今回紹介したもの以外の機能もたくさんありますので、興味があれば調べてみてくださいね。
ちなみに今回の実装を見て、「Tick」を使わずに以前学習した「Set Timer by Event」を使っても同じような処理ができるのでは? と思うかもしれません。良い発想なのですが、実のところ「可能だが向いていない」実装なのでオススメできません。
というのも、「Set Actor Location」と「Set Timer by Event」などのタイマー関連は内部処理の関係か相性が悪く、キレイに動いているように見せるのが非常に難しいのです(カックカクになります)。
しかし、「Tickイベント」を使わない方法を考えるのはとても良いことです。
学習の始めで言われると使いづらくなるかもしれませんが、「Tick」処理は負荷の観点からあまり好まれず、重要or一時的or置き換えが効かない処理以外では極力使用しないで実装すべきだと考えている開発が多いのが実情です。
そのため、Tickに処理があっても、使わないタイミングでは「Tick機能そのものを切っておく」処理を入れることもしばしばです。
ブループリントの「クラスのデフォルト」から詳細パネルの「Start with Tick Enabled」を切ります。
…とはいえ、個人的には大規模開発でないかぎり、どちらでも良いかなとも思います。
いつか思い出せれば大丈夫。今はたくさん使って慣れておくことが重要です!
重要なのは、適宜どちらも使いこなせることかもしれませんね。
お疲れさまでした!
(補足)処理を工夫する
今回はTickや移動処理、切り替わりなど難しい処理が多く、ブランチにも慣れてもらいたいので、なるべく(個人的に)処理を追いやすい書き方にしているつもりです。
慣れてきたら部分的にカスタムイベントに分けたり、「Select」ノードを使ったブランチを減らす工夫をしてみるなど、さまざまな書き方にチャレンジしてみましょう。
短くしても、わかりづらくなってしまっては意味がありません。
処理が追いやすいようなノードの組み方を目指していきましょう!
プロジェクトのダウンロード