【第16回】インタラクトするギミック①
概要
今回はプレイヤーがレバー(ハンドル)風の仕組みにインタラクトすると、その先にある足場を出現させるという処理をつくります。これまでの処理を発展させた仕組みなので、わからなくなったら以前の内容を見返して進めていきましょう。
このギミックの流れは次のようになります。
ギミックに必要なメッシュを用意する(移行機能を使ってみる)
適当なレバーとパドルのStatic Meshを作成しておきます。
このようにジオメトリブラシで作成しても構いません。
図のようにレバーハンドル部分と、土台部分の2つに分割してください。
さらにここでは、もうひとつの方法として、以前ダウンロードしておいたプロジェクト「UnrealLerningKitGames」から移行機能を使って(データを移行して)活用する方法も学んでおきましょう。
「移行機能」とは、プロジェクトから別のプロジェクトへアセット(データ)を移行させる機能で「他のプロジェクトに欲しいアセットがある」「プロジェクト内容を部分的にコピーして渡したい」ときなどに便利です。
移行元のプロジェクトを起動しましょう。
移行元は開いている必要がありますが、移行先はどちらでも大丈夫です。
念のため「すべて保存」からセーブしておきましょう。
注意点として、移行元と先でUE4のバージョンが一致していることが推奨されています。
移行元プロジェクトのコンテンツブラウザを表示させてください。
移行したいファイルを選びます。複数選択可能です。
ここでは「SM_Player _Lever_Base」「SM_Player_Lever_Handle」を選択しました。
右クリックからアセットアクションの「移行」を選んでください。
関連するアセットが一覧表示されました。これらが移行されます。
選択したメッシュ以外が選択されているように見えるかもしれませんが、これはメッシュに設定されているマテリアルやテクスチャが付随して移行されるためです。
問題なければ)「OK」を押しましょう。
「送り先コンテンツフォルダを選択」というウィンドウが表示されます。
移行先プロジェクトのフォルダにある「Content」フォルダを指定してください。
ここで他のフォルダを指定するとうまくいきません。
移行先のプロジェクトで正しく移行されたかを確認しましょう。
これで移行完了です! メッシュを開いて確認してみましょう。
移行に「レベル」を指定すると、レベルに関連するものすべてが移行できるので、ある程度プロジェクトを作り終わったら、使用するレベルを選択して移行するのもおすすめ。不要なアセットが排除された(容量が削減された)状態のプロジェクトを作成できます。ただし、一部の情報(プロジェクトの設定や、その中のインプイット情報)は移行されないことに注意してください。そちらは別途エクスポートして、移行先でインポートする必要があります。
素材データから使用したいという場合は、解凍したフォルダから該当のアセットを選んでフォルダにドラッグ&ドロップしてインポートします。
インポートオプションではさまざまな設定が可能ですが、とくに必要ない限り「メッシュ結合」オプションなどをOFFにする等は最初に覚えておきましょう。
ただしこの場合設定するマテリアルなどは別途自らで作成する必要があります。
なお、以降ではレバーハンドルとベース部分のアセットを移行した想定で作成を進めていきます。
移行しなくても実装内容は殆ど変わりませんので、好きな方法を選んでください。
確認用のレベルを作成する
確認用のレベルを作成しておきます。
ゲームモードも設定しておきましょう。
アクタを作成する
「追加/インポート」>「ブループリントクラス」>「Actor」で新規作成します。
ブループリントエディタを開きます。コンポーネントパネルからメッシュを設定しましょう。
わかりやすいよう、コンポーネントの名称を変更しています。
動かすのはレバー部分だけなので、メッシュが別になっていますね。
図のような親子関係にしておきましょう。
タイムライン演出を作成する
レバーハンドルが動く演出を作成しておきます。
ブループリントエディタを開いて、タイムラインを作成してください。
クリックしてタイムラインエディタを開きます。
カーブを作成してください。
今回は次のように設定します。
0秒:値0
1 秒:値1
開始が0、終了が1になっていれば、終了時間は適宜狭めても伸ばしても構いません。
レバーハンドルの演出を作成する
イベントグラフに戻ります。
コンポーネントパネルからレバーメッシュの参照をゲットしてください。
そこからワイヤーを伸ばし「Set Relative Rotation 」ノードを検索して配置します。
これはコンポーネント単位の相対座標で動かすノードです。
以前はワールド座標で動かす「Set Actor Rotation」というノードを使いましたね。
今回はアクタ全体ではなく、その一部だけを動かしたいので、「Relative」ノードを使います。
以前の補足項目で学習した「Set Rlative Location」の角度バージョンです。
相対の基準は、その親コンポーネントになります。
Update 実行ピンに接続します。
動かしたいのは、ピッチ値だけです。
「New Rotation」ピンからワイヤーを伸ばして「Make Rotator」を検索して配置します。
これで入力ピンを分割できました。
Lerp(線形補完)ノードを使う
変更したい「Roll」入力ピンからワイヤーを伸ばして「Lerp」と検索して配置しましょう。
これはLerp(線形補間)ノードと呼ばれるものです。
詳しい説明は省きますが、線形補間とは図のような二点間((x0, y0) (x1, y1)) にある任意のx座標に対応するyを計算することです(図はWikipediaより抜粋)。
ノードの入力AとBの値がグラフでいうXとYにあたり、Aplhaピンの入力(0.0~1.0)に合わせた値をReturn Valueで返します。たとえばAに0、Bに100を入力して、Aplhaに「0.5」を入れればReturn Valueからは50が返される、ということですね。
これとタイムラインのグラフから出力される値を組み合わせます。
ためしにA値とB値に数値を入れてみましょう。
A値が移動前、B値が移動後と想定します。
移行したメッシュの場合、A値-70、B値70付近でしょうか。
値は後から調整してください(変数化しておいて管理しやすくするのも良いですね)
確認のためBegin Play に接続します。
コンパイルして、ビューポートを表示させてください。
ツールバーの「シミュレーション」ボタンを押してみましょう。
「Begin Play」イベントで接続した処理のシミュレーションを確認することができるはずです。
シミュレーションを切って動きを調整してください。
調整したら、「Begin Play」と接続を切っておきます。
レバーのRoll初期値をタイムライン開始時の値である「-70」に変更しておきました。
レバースイッチのブループリントにコリジョンを設定する
次項でプレイヤーにコリジョンを設定し、レバーに触れているかを検知してイベントを発行する予定ですが、「移行」したメッシュにはコリジョンが無いためそのままでは検知できません。
メッシュエディタでメッシュ自体にコリジョンを設定する方法でも構いませんが、ここではコリジョンコンポーネントを付与しておいて、プレイヤー側の検知に使いましょう。
コンポーネントパネルから「Sphere Collision」を追加します。
適当にサイズを調整しておいてください。
呼び出すイベントを作成しておく
このタイムラインを呼び出すカスタムイベントを作成しておきます。
インプットを作成する
インタラクション用のインプットイベントを作成してください。
手順は以前の説明を参考にしましょう。
アクションに使用するキーはなんでも構いません。図では「E」キーを指定しました。
キャラクターにインタラクト用のコリジョンを追加する
キャラクターのブループリントを開きます。
インタラクト用のコリジョンを付与します。
この範囲に入ったときに、インタラクション(レバーハンドルの操作)をできるようにします。
コンポーネントパネルから「Sphere Collision」を追加してください。
詳細パネルから位置や大きさを調整しておきます。
親子関係も注意しましょう。RootかMeshの子にすると良いでしょう。
コリジョンでレバーハンドルに触れたことを検知する
コリジョンイベントをつくります。
コンポーネントパネルから「Sphere」コリジョンを選択します。
詳細パネルの「On Component Begin Overlap」を選択してイベントを配置しましょう。
「スイッチのそばから離れたとき」も検知したいですね。
「On Component End Overlap」も選択してください。
「Other Actor」から作成したギミックをCastして触れているかどうか分岐します。
Castノードの「As ~」ピンからは参照を取得できますね。
そこから参照の変数を作成してしまいましょう。
これでプレイヤーのコリジョンがレバーに触れた瞬間に参照が保存されるようになりました。
さらに「On Component End Overlap」時(コリジョンから離れたとき)に、その変数を空にする仕組みをつくります。空にするには、単純に入力しないセット変数を置くだけです。
「Print String」を使えば(変数が入っているかどうかの)状況がわかりやすいかもしれません。
確かめたいときには使ってみてください。
インプットアクションのイベントを作成する
インプット時に参照の変数からイベントを呼び出す仕組みをつくりましょう。
空いている場所にインプットアクションイベントを作成してください。
あとは、マイブループリントパネルから参照変数のゲットを配置して、タイムラインに接続されたイベントを呼び出すだけです。
しかしこのままでは、ひとつ問題があります。
変数の中身が「空」の状態のため、イベントを呼び出そうとするとエラーが出てしまうのです。
そこで、キーを押したとき参照が格納されていれば(コリジョンに入っていれば)実行されるように変更してみましょう。
参照変数からワイヤーを伸ばして「Is Valid」ノードを検索して配置します。
これは接続したオブジェクトが有効であれば「Is Valid」に実行処理が流れるノードです。
図のように「Is Valid」を挟んでからイベントを実行するようにしましょう。
確認用のレベルに配置してテストしましょう。
確認した問題を解決する
テストをすると、色々気になる点が見つかるかもしれません。
ざっと見ただけでも以下のような問題を確認しました。
- ジャンプ中や移動中でも実行できてしまう
- スイッチを実行中でも移動できてしまう
- 何度でもスイッチオンを実行できてしまう
他にも細かく挙げればきりがないかもしれませんが、ひとまずこれらを解決してみましょう。
ジャンプ中に実行できないようにする
簡単な方法は「落下中かどうか」で分岐する方法です。
これは以前、飛行機能を作るときに学習した「Is Falling」を使うのが簡単です。
これでジャンプ中は実行できなくなりました。
処理の視認性を考えて「ジャンプ中でなければTrue」という繋ぎ方にしてみましょう。
「Is Falling」ノードからワイヤーを伸ばし「Not Boolean」ノードを配置します。
これは、受け取った論理値を反対にして返すノードで「True」なら「Not」を返します。
すると、次のようになりました。わかりやすくなった気がします。
とくに必要なければ省いても構いません。
その他、「CharacterMovement」の「Is Walking」ノードで歩行中かどうかを判断する方法もあります。これは「Walking」(徒歩)状態かを判断して返すノードです。
スイッチ実行中は移動不可にする
スイッチを実行中には歩けないようにします。
まずは停止処理を作成しましょう。
「CharacterMovement」コンポーネントのゲットからワイヤーを伸ばします。
「Stop Movement Immediately」を設置してください。
これは即座に移動を停止させる処理を実行するノードです。
このノードを「Is Valid」で確認したあとに差し込みます。
さらに動作処理自体を受け付けなくしましょう。
「Set Movement Mode」ノードを呼び出して接続します。
これはキャラクター(Character Movement)の移動状態を指定するノードで、「New Movement」引数で指定したモードに変更されます。通常移動時は「Walking」が使われていますので、ここで「None」を指定しましょう。
これでキャラクターの移動状態は「None」(無し)になり、操作を受けつけなくなります。
ただしこのままでは、スイッチ実行後も移動できません。
移動開始を許可する「Set Movement Mode」ノードを配置しましょう。
実行後、適度に「Delay」をかけてから元に戻す方法でも良いのですが、使い勝手が良くありませんね。そこで、レバースイッチ側のブループリントから終了時に移動可能させる命令を送る方法をとってみましょう。
方法はさまざまですが、イベントディスパッチャーを学習しましたので、活用してみます。
レバースイッチ側のブループリントを開きます。
マイブループリントパネルからイベントディスパッチャーを作成してください。
そして、タイムラインのFinishedが実行される時に呼び出します。
つづいてプレイヤーキャラクターのブループリントエディタに戻ります。
コリジョンに触れたとき(On Component Begin Overlap)、イベントディスパッチャーをバインドします。カスタムイベントも作成して接続しましょう。
これで、タイムラインで「Finished」が実行されたときに呼ばれるイベントができましたね。
「Movement Mode」を「Walking」に戻す処理を実行させます。
New Movement Mode 引数に「Walking」を指定します。
バインドしたイベントは意図的に解除することもできます。
「On Component End Overlap」時に入れてみましょう。
「イベントのバインドを解除」ノードを呼び、解除したいイベントに接続します。
離れたときは結びつき(バインド)がなくなるようになりました。
このとき、ノードの実行順に注意してください。
次のように接続すると、存在しない「空にした変数」をターゲットにしてバインドを解除しようとするため、エラーになります。実行順は常に注意しましょう。
「Movement Mode」ノードは、設定を戻す処理がないと予期せぬ動きをしてしまいますので、注意して使用しましょう。
1度だけスイッチを実行できるようにする
レバースイッチに「スイッチが押されたか?」というフラグ変数を設定して、実行時に確認するようにして解決します。
マイブループリントパネルからBoolean変数を作成しましょう。名前はなんでも大丈夫です。
また、タイムラインが実行されたら変数を「True」にします。
ここでもコンパイルを忘れずに(別のブループリントから変数を参照できません)
フラグがOFFであれば実行する処理をつくりましょう。
プレイヤーのブループリントを開きます。
インプットアクション処理の「Stop Movement Immendiately」の前に変数を参照した分岐を作ります。
レバースイッチの参照変数からワイヤーを伸ばしてフラグ変数のゲットを検索して配置します。
このように別のブループリントからイベントだけでなく変数も呼び出すことが可能です。
「Not Boolean」ノードを使って「フラグ変数がTrueでなければTrue」という分岐にしました。
視認性として使っているので、もちろんこれを使わずFalseに接続しても構いません。
これで問題の修正が完了です。テストプレイを忘れずに。
今回は以上です!
次回、インプット動作(レバーハンドルのイベント)をトリガーにして、レベルにアクタを生成させる処理を作ります。
お疲れさまでした!
プロジェクトのダウンロード