UNREAL→LABØ

Unreal Engine4の情報をまとめます。

【第18回】配列で複数コンポーネントを同時に処理する

 

概要

当初想定していたレベルに使用するギミックはほぼ完成しました。

最後にクリアまでの演出として、レベルにあるアイテムをすべて取得するとゴール地点までのルートが作られるという仕組みを作りたいと思います。

f:id:miccak:20210519175034p:plain

「GameMode」のブループリント上でルールや進行状況を保存する方法や、各コンポーネントを配列にして、それぞれに対して処理を実行する方法などを学びます。

 

  

どこに変数を作成するか。

まずは、獲得したアイテムの個数情報を保存する仕組みを作りましょう。
保存には変数を使用しますが、さてこの変数はどこに作成するべきでしょうか?

 

プレイヤーキャラクターが取得するのであれば、キャラクターのブループリントに保存するのが直感的と思えますが、これはあまりオススメできません。たとえば「Destroy Actor」ノードなどでレベルから除外されると、合わせて中身に保存された変数も削除されてしまうからです。

再度生成しても、それは別のアクタですのでデフォルトの状態になります。
もちろん、アクタを削除しなければ良いやという話なのですが、生成・削除が容易なものに(プレイを通して使用する)変数を保存する設計は、芳しくありません。

影響しないブループリントはいくつかありますが、今回は「Game Mode」を使用して作成します。

よくご存知の方は、Player State や Game State といった場所が良いのではと思うかもしれませんが今回はもっとも基本的な方法にしました。

 

 

アイテムを獲得したときに処理を追加する

まずは「Game Mode」に保存する方法をためしてみましょう。

使用している「Game Mode」を開きましょう。

忘れている方はレベルのワールドセッティングを確認してください。

f:id:miccak:20210519181622p:plain

ここでは、「BP_MainGame」ですね。

開くと図のようになっていることを確認できます。
ブループリントグラフになにも書かれてないと開いたときにこのようになります。
「ブループリントエディタを開く」を選択して開きましょう。

f:id:miccak:20210519181704p:plain

 

マイブループリントパネルから数値を保存する変数を作成します。
個数なので整数を格納できる「Integer」型で良さそうです。

 
「CurrentPoints」には現在の獲得数。デフォルトは0にします。
「ClearPoints」にはクリアに必要なポイント数を指定します。「3」
に設定しました。
f:id:miccak:20210519182357p:plain
f:id:miccak:20210519182403p:plain

 

 

ポイントを加算する処理を作成する

Game Mode のイベントグラフ上にポイントを加算する処理をつくります。

f:id:miccak:20210519182756p:plain

 

「CurrentPoints」のゲット変数(現在値)に対して加算するだけの処理です。

今のところ、1個につき1ポイントという想定ですが、念のためポイントが増加しても問題ないように、加算するポイントを指定できるようにしてみましょう。

加算ノードの引数からワイヤーを伸ばしイベントグラフにドラッグ&ドロップします。

f:id:miccak:20210519183502g:plain

カスタムイベントに引数を設定することができました。
これで、任意の数値を指定して加算することができます。

 

詳細パネルからデフォルトの引数値を指定できます。1を指定しておきましょう。

f:id:miccak:20210519184311p:plain

 

確認用に「Print String」で表示するようにしておきます。

f:id:miccak:20210519183731p:plain

わかりやすくするために「Append」ノードを使ってStringを組み合わせています。

 

アイテム獲得時にこのイベントが呼ばれるようにしてみましょう。

 

 

アイテム取得のイベントグラフに追記する

アイテムのブループリントを開きます。

f:id:miccak:20210519183844p:plain

 

ゲームモードのイベントを呼んでみましょう。

「Get Game Mode」ノードを検索して配置します。
そこからワイヤーを伸ばして、作成したゲームモードでCastします。

f:id:miccak:20210519184030p:plain
f:id:miccak:20210519184104p:plain


「As ~」からはその中の処理を呼べますので、「Add Point」を呼び出しましょう。

f:id:miccak:20210519184431p:plain
f:id:miccak:20210519184436p:plain

 

 

確認用のレベルを作成して、テストしてみましょう。

確認用のレベルでは、ゲームモードも設定を忘れずに。
Game Modeの処理は、そのGame Modeがレベルに設定されているときのみ実行されます。

f:id:miccak:20210519184915p:plain

 

 

連動するブループリントを用意する

すべてのアイテムを獲得したときに出現する「ゴールまでの階段」をブループリントで作成します。

まず使用するメッシュを用意しましょう。
ここではいくつかのブラシをStatic Meshにして使用します。

 

使用するメッシュはブループリントへまとめて追加しますが、メッシュ個数が多いので、「マージ」という機能を使って複数のStatic Meshをひとつに結合しておきました。これは特に必須の作業ではありません。余裕があればためしてみてください。 

コリジョン設定済みの)結合したいメッシュを右クリックして「アクタをマージ」を選択して実行します。

f:id:miccak:20210520121342p:plain
f:id:miccak:20210520121522p:plain

オプション項目など詳しく知りたいときは公式のページを読んでみましょう。
docs.unrealengine.com

いくつかのモジュールをまとめて管理できるのでレベルデザインの際に役立ちますが、例外的なStatic Meshファイルが増えてしまいますし、プレイ時に余計なメモリを食ってしまうことになりますので程々にしておきましょう。

いくつかのメッシュをマージしておきました。

f:id:miccak:20210520144416p:plain

 

つづけて、新規Actorブループリントを作成します。

f:id:miccak:20210520132823p:plain

 

Static Mesh コンポーネントを設定してください。

(やや個数が多い気がするので、もう少しまとめてもよかったかもしれませんね)

f:id:miccak:20210520144012p:plain

 

 

各Static Mesh コンポーネントを、(Rootから見た)相対的なZ座標を上昇させる演出にします。
今回もタイムラインを使って動きを作ってもいいのですが、緩急のある動きは必要なさそうですし、「Tick」を使って移動処理を作成してみます。
また、この移動処理は(最終的な相対座標がが異なる)複数のStatic Meshコンポーネントに対して実行します。そこで、簡易的な「配列」変数を作りそこにコンポーネントを格納して処理してみることにします。配列変数を使ってみましょう。

実のところ、すでに「Get All Actor of Class」ノードで配列を受け取る処理は使っていますが、配列の変数化は初めてですね。

f:id:miccak:20210520145159p:plain

 

配列とは、複数の変数をひとつにまとめたものと考えてください。

f:id:miccak:20210520151750p:plain

たとえばIntegerの配列変数ならば、[0]番目に5、[1]番目に99、[2]番目に23など整数をまとめておくことができます。もちろん個別に取り出すことも可能です。

今回はコンポーネントの参照を入れておく変数を作成して使ってみましょう。

マイブループリントパネルから「Static Mesh Component」型の変数を作成します。
続けて、「変数の型」の隣にあるボタンをクリックし「■が9個連なった」マークを選択します。

f:id:miccak:20210520152411p:plain
f:id:miccak:20210520152624p:plain


コンパイルしておきます。これで変数が配列化されました。

 

デフォルト値が通常の変数と違いますね。複数個登録できるようになっています。
通常の変数と違い、このままではデフォルト値が存在しない状態です(配列が無い)。

 

そこで「Begin Play」イベントで配列を作成する処理を作ってみましょう。
変数をグラフにドラッグしてセットの変数を配置します。

f:id:miccak:20210520153810p:plain

 

このままでは、まだ空の状態ですので、入力ピンにコンポーネントの情報を渡します。コンポーネントパネルから、すべてのStatic Mesh コンポーネントのゲットを配置してください。

f:id:miccak:20210520154840p:plain

 

配列変数の入力ピンからワイヤーを伸ばして「Array」と検索します。
「配列を作成」という項目を設置しましょう。

f:id:miccak:20210520154856p:plain
f:id:miccak:20210520154913p:plain

 

「ピンを追加」して、それぞれのコンポーネントと接続します。

f:id:miccak:20210520154945p:plain

 

これでゲーム開始時に配列に各コンポーネントが格納されるようになりました。

ためしに、このうちひとつのコンポーネントを動かしてみましょう。

 

 

配列内のメッシュを動かす

「Tick」イベントのそばに、配列変数(Components)のゲットを配置します。
出力ピンからワイヤーを伸ばし「Get」と検索して「コピーを取得」を配置してください。

f:id:miccak:20210520154412p:plain
f:id:miccak:20210520154429p:plain
f:id:miccak:20210520154440p:plain


これは指定した順番の配列情報を取り出すノードですね。
0を指定すれば、1番目に格納した情報を取り出せます。

この場合は「Begin Play」イベントで代入したStatic Meshコンポーネント「Road1」です。

 

「Set Relative Location」ノードで相対位置を動かしてみましょう。
「Get」の出力ピンからワイヤーを伸ばして「Get Relative Location」を配置します。

f:id:miccak:20210520155339p:plain
f:id:miccak:20210520155348p:plain

 

「Get」の出力ピンからワイヤーを伸ばし「Relative Location」ノードを配置します。
これでコンポーネントの相対位置を取得できます。

f:id:miccak:20210520160718p:plain

 

位置を加算するため「Vector + Vector」ノードを配置。
さらに「Get」の出力ピンからワイヤーを伸ばして「Get Up Vector」ノードを配置します。

これはコンポーネントからの上方向(Z)へのベクトル情報を取得するノードです。
以前「Get Actor Up Vector」というノードを扱いましたが、それのコンポーネント用ですね。基本的に「Z:1.0」が入っていると考えておけばいいでしょう。

f:id:miccak:20210520161104p:plain


ノードを「Vector * Float」でつなぎます。速度となる値を入力しておきます。
さらに、Delta Secondsと乗算します。

f:id:miccak:20210520161237p:plain
f:id:miccak:20210520161330p:plain

図のようにTickから出力される「Delta Seconds」で線がごちゃつくときは、「Get World Delta Seconds」ノードでも同様の結果を得ることができます。参考までに。

f:id:miccak:20210520161452p:plain

 

それぞれ接続します。

f:id:miccak:20210520161538p:plain


確認用のレベルに配置して、シミュレーションプレイしてみましょう。

Getノードで1番目に指定したコンポーネントだけ上昇しだすはずです。

f:id:miccak:20210520161825p:plain

 

このままでは上昇し続けてしまうので、指定位置で止まるように「限界値」を設定します。

ブランチで分岐する方法が簡単そうですね。

「Relative Location」ノードを「Break Vector」で分割して、相対Z座標が指定値以下ならば実行するようにしました。

f:id:miccak:20210520162808p:plain

 

指定した位置(図では相対 Z:1500)で止まったことを確認しましょう。

 

では、この配列(「Get」の数値)すべてに処理を行っていきます。

配列すべてに処理を行うには、以前も少しだけ使用した「For Each Loop」を使うのが簡単ですね。

このノードを使うことで配列0から最後の配列まですべてに処理を反映させることができます。
LoopBody で、配列(Array Element)と順番(Array Index)を取得して処理します。
繋ぎなおしましょう。

f:id:miccak:20210520163605p:plain

 

あとはそれぞれのコンポーネントに合わせた「 Z 座標の限界値」を指定するだけですね。

格納する「Float 配列変数」を作成して、そこから数値を取得してみましょう。

マイブループリントパネルから新規変数を作成します。
「Float」変数を配列にします。

f:id:miccak:20210520164202p:plain

 

デフォルト値から値を設定しておきます。
「+」を押して配列情報を追加しましょう。

コンポーネントのZ座標の限界値(最終的に配置したいコンポーネントの高さ)を設定します。

f:id:miccak:20210520165506p:plain

図では、60ずつ上昇するよう値を設定しました。

 

Float配列変数とゲットを配置して、そこから「Get」ノードを繋ぎます。

f:id:miccak:20210520165800p:plain

 

「For Each Loop」ノードの「Array Index」から配列の順番が取得できますので、「Get」ノードに接続します。

f:id:miccak:20210520170014p:plain


確認してみましょう。

f:id:miccak:20210520170223g:plain

 

フラグ管理する

「Tick」イベントに接続されているので、このままでは開始早々階段が出現してしまいます。
フラグ変数を作ってそこで管理しましょう。

 

マイブループリントパネルから「Boolean」型の変数を新規作成します。

f:id:miccak:20210520170515p:plain

「Tick」イベント の最初で分岐させておきます。

f:id:miccak:20210520170633p:plain

 

あとは「GameMode」この変数をONにするだけですね。

「GameMode」を開きます。

f:id:miccak:20210520170748p:plain

 

カスタムイベント「Add Point」の最後で、「Current Points」と「Clear Points」を比較する分岐を配置します。「Clear Points」を超えていたら処理を行うようにしましょう。

f:id:miccak:20210520171318p:plain

Trueのとき、「Get All Actor of Class」ノードを配置して、このブループリントを検索します。
アクタはひとつしか置かない予定なので、「Get」ノードで1番目を取得します。

f:id:miccak:20210520171425p:plain

そこから変数を探してセット変数を置いて変更しましょう。

f:id:miccak:20210520171634g:plain

f:id:miccak:20210520171622p:plain

 

アイテムを指定個数(Clear Pointsの値)獲得したら処理が実行されることを確認しましょう。

 

 

演出が終わったらフラグをOFFにする

このままで完成としても良いのですが、アクタの上昇する演出が完了したらTick処理は不要になりますので、完了を確認したらフラグをOFFにする処理を作成してみましょう。

 

「For Each Loop」ノードのCompletedピンにブランチを接続します。
この出力は一連の処理が完了するたびに呼ばれます。

 

終わりの判断は「一番最後のメッシュが上限のZ座標に到達したら」でいいでしょう。
各配列変数のゲットを配置して、それぞれからワイヤーを伸ばして「Last Index」と検索して配置します。これは配列の最後の番地を取得できるノードです。

「Last Index」を使用しないと、コンポーネントの個数が増えたときに手動で定数値を入力しなおさなければならないので不便です。

 

あとは「Get」ノードと組み合わせて、最後のコンポーネント位置と比較して分岐します。
True 以降の処理で、フラグ変数をOFFに変更してください。

f:id:miccak:20210520172730p:plain

 

プレイして確認しましょう。

 

今回は以上です!

当初実装したかったギミックはほとんど作成できました。
今後は、機能の改修や追加を行いながらより「ゲームっぽさ」を出していく予定です。

 

お疲れさまでした!

 

(補足)演出開始まで隠しておく

本番では終了演出開始までアクタを雲などに紛らせておくかもしれませんが、現状は見えた状態で配置されていて少々気になります。

そこで、演出開始まで見た目を消して違和感を与えないようにしてみましょう。

また、間違って上に乗ってしまわないようコリジョンを消しておくことにします。

「Begin Play」イベントで「Set Actor Hidden In Game」ノードを使ってアクタを非表示にします。
そして、コンポーネントの配列が作成できたら「For Each Loop」ノードを使い、「Set Collision Enabled」ノードですべてのコンポーネントコリジョンをOFFにしておきます。

カスタムイベントを別途作成して、すべてONになる処理も作成します。

f:id:miccak:20210520175945p:plain

 

このカスタムイベントを「GameMode」上から呼び出しましょう。

f:id:miccak:20210520180232p:plain

 

(補足)Tick機能を切る

フラグ変数をOFFにする際、これ以上アクタのTick機能を使用する予定がないときは、「Set Actor Tick Enabled」ノードを使用することでActorのTick自体を切ってしまうという手もあります。

f:id:miccak:20210520173718p:plain

 

プロジェクトのダウンロード

drive.google.com