読者です 読者をやめる 読者になる 読者になる

GameProgrammar's Night

ゲームプログラム系の覚え書き

UE4 イベント・関数・マクロ・インターフェイスの違いと使い分け

qiita.com

この記事は、UE4 AdventCalender 18日目の記事です。

 BPには、イベント・関数・マクロ・インターフェイスと手続きの実装手段が豊富にあります。
 BPはオブジェクト指向言語ではありますが、オブジェクトより手続きを重視した思想になっている現れだと個人的に思っています。
 とはいえ、手続きの実装手段が多すぎて、結局どれで実装すれば良いのかが、わかりにくいというのも確かです。そこで、各手続きの機能的な違いと使い分けについて紹介したいと思います。

主な機能一覧表

イベント 関数 マクロ インターフェイス
入力ピン 1個 0~1個 0個以上 1個
出力ピン 1個 0~1個 0個以上 1個
引数 0個以上 0個以上 0個以上 0個以上
戻り値 なし 0個以上 0個以上 0個以上
アクセス制御 public public
procted
private
private public
オーバーライド 不可
ノード制限 なし あり なし 実装方式による
EventDispatcherへのbind 不可 不可
特徴 ネットワーク越しに呼べる いわゆるメソッドに一番近い Lispのマクロのような
Functorのような
ダックタイピング

手続き解説

イベント

 イベントは、BPにおける手続き実装の基本です。
 もっとも制限のない手続きのため、戻り値がいらないならイベントを使いましょう。

 イベントの大きな特徴は、ネットワーク越しに呼べることです。
docs.unrealengine.com

 自動でRPC機能がつくので、これだけで、ネットワークゲームがある程度作ることができます。 

関数

 戻り値が必要な時は、関数を使います。
 唯一アクセス制御をコントロールできますので、アクセス制御が必要な時も関数を使います。LatentノードやTimelineノードなど一部使えないノードが存在します。

 関数はPureフラグをオンにすることで実行ピンを省略することができます。値の取得のみで済む場合は、オンにすると良いでしょう。

 他のクラスベース言語経験者で、BPのことがよくわからない内は、関数で実装して、関数ではできないことはイベントにするというやり方でも良いと思います。

マクロ

 ノードをまとめて整理したり、部分的に使い回すのに使います。インライン化機能もついており、実行時にはノードが展開されます。
 最大の特徴は、複数の入出力ピンを使えることです。

f:id:katze_7514:20161217004936j:plain
 上記のマクロは、プレイヤーのPawnとの距離に応じて、出力ピンが実行されます。
 ちなみに「ノードの折りたたむ」はマクロにすると同じです。

 マクロはprivateになりますので継承先では使えません。継承先でも使いたい時は、イベント・関数として実装しなおすか、MacroLibraryを使います。ただ、MacroLibraryに実装すると逆に継承先でしか使えませんので、イベント・関数で実装にした方が良いかもしれません。
 また、マクロ内部ではブレイクポイントをおけませんので、はじめからマクロとして作るとデバッグが大変なこともあります。

 名前から、C言語系のプリプロセッサのマクロかなと思うかもしれませんが別物です。おそらくですが、Lispマクロを参考にしているのだと思います。
 Sequenceノードと組み合わせることで継続のようなこともできますし、マクロのローカル変数は実行終了後も値を維持しますのでFunctorのような挙動もします。
 マクロだけで1冊本が書けそうなぐらい様々なことがでできます。

インターフェイス

 インターフェイスは、キャストなしに呼ぶことができます。そのため、クラス間の依存度を下げるのに使います。テンプレートやジェネリクスのないBPでダックタイピングをするための手続きです。
 わかりやすい使い所は、Overlapイベントなどエンジンが用意しているイベントです。
f:id:katze_7514:20161217012833j:plain
 エンジンが用意するイベントは、引数がActorなど汎用的な型ですので、Interfaceにしておくと楽になります。

f:id:katze_7514:20161217011007j:plain

 インターフェイスの呼び出しは、右上に手紙マークがつきます。
 右クリックメニューから選ぶ時は、Messageとついてるのを選びます。
f:id:katze_7514:20161217012109j:plain

 クラス間の依存度下げることは、コンパイルが早くなるだけでなく、エディタのクラッシュ率やバグを踏む可能性も下げますので重要です。

 インターフェイスの実装は、戻り値がない場合はイベント、戻り値がある場合は関数として実装します。
 なお、インターフェイスはデフォルト実装が直接はできませんので、場合によっては他の手続きで実装した方が楽なこともあります。

明日は

 明日は、hima_zinnさんの「UE4のショートカットをカスタマイズして開発効率を上げる」です。

UE4 FloatingPawnMovement を使う

 FloatingPawnMovementとは、UE4が用意している移動を担うComponentの一つです。
 CharacterMovementのように状態(歩き、ジャンプ、しゃがみ)があるわけでもなく、設定したパラメータ通りに位置を変化させるだけのシンプルな実装のMovementComponentです。
 シンプルですが2Dゲームで使うには十分な機能を持っており、自分のゲームでの移動はFloatingPawnMovementをメインに使っています。
 

移動させる

f:id:katze_7514:20161026154832p:plain

 移動させる時は、AddInputVectorノードを使います。
 FloatingPawnMovementは、移動させたい方向のベクトルを与えることで移動します。

●X+方向なら(1.0, 0.0, 0.0)
f:id:katze_7514:20161026161546p:plain

●45度方向だったら(cos45, sin45, 0.0)
f:id:katze_7514:20161026161554p:plain

 方向ベクトルの長さは「0.0~1.0」です。1.0以上与えても1.0として扱われます。この長さは、次に説明する速度パラメータのスケール値として働きます。

 AddInputVectorは呼ぶ度に方向ベクトルが加算されて行きます。FloatingPawnMovementのTickが呼ばれるまでの加算結果を用いて移動処理が実行されます。
 これを利用することで、Pawnの外部からの影響を受ける特殊な移動処理を簡単に実現することもできます。

ある地点に引っ張られる

 青いエフェクトが出ている間、通常の移動処理に加えて、中央に移動する方向ベクトルをAddInputVectorすることで実現しています。

速度パラメータ

 MovementComponentでの移動は、与えたベクトル方向に加速していき、最大の速さに到達したら速さアップを止めます。ベクトルの入力が無くなったら減速します。
 移動の調整は、それらのパラメータをいじることで行います。

f:id:katze_7514:20161026150207p:plain

パラメータ名 意味(単位)
Max Speed 最大の速さ(unit/sec)
Acceleration 加速度(unit/sec)
Deceleration 減速度(unit/sec)
Turning Boost 方向転換した際の追加加速スケール

 単位は、unit/secです。2Dゲームで、PPUを1.0に設定している場合は、pixel/sec と思って大丈夫です。

 TurningBoostは、どれだけ素早く方向転換できるか、ということを示すパラメータです。TurningBoostの値が大きいほど素早く方向転換が行われ、小さいほどゆっくり方向転換します。動作としては、方向転換する時だけ、加速度がTurningBoost倍されます。

 デフォルトの値では、加速も減速も方向転換も、かなり素早く行う設定となっています。そのため、すぐに最大の速さになりますし、ピタっと静止できるし、瞬間的に方向転換します。

慣性のある移動(滑りながら移動)

 速度パラメータ設定の応用として、慣性のつく設定を紹介します。
 以下のように、最大の速さ以外のパラメータの値を大きく下げ、また加速より減速の値を低くします。

f:id:katze_7514:20161026180052p:plain

 すると次の動画のようになります。

youtu.be

移動制限パラメータ

 移動する平面を指定することができます。指定した平面以外へは動かなくなります。
 詳細パネルのPlanarMovementで設定します。
 
f:id:katze_7514:20161026154839p:plain

 図の設定だと、移動がXY平面のみとなりZ方向には動かなくなります。

パラメータ 意味
Constrain to Plane チェックを入れると平面に移動が制限される
Snap to Plane at Start Spawnした時から指定した平面に制限するか
Plane Constraint Axis Setting 移動制限したい平面の簡易設定。移動制限する平面の法線軸を指定。Customを選んだ場合は次の2項目を自分でいれる
Plane Constraint Normal 移動制限平面との法線ベクトル
Plane Constraint Origin 移動制限平面の法線ベクトルの原点

 ただし、Pawnに直接Locationを入れれば(AddWorldLocationなど)、動かない方向にも動きます。あくまで、FloatingPawnMovementによる移動が制限される設定となります。

Navigation

 FloatingPawnMovementは、Navigation移動に対応しています。AIControllerを持つPawnに持たせれば、MoveTo系ノードを使うことで、今まで紹介したパラメータに従ってNavigationします。

注意点

 FloatingPawnMovementは、AddInputVectorでベクトルが追加されなかった場合に減速する仕組み上、前フレームとの位置差分を速度ベクトルに変換する処理が入っています。
 そのため、直接Locationを入れて動かすことと併用している場合、想定してない移動になることがまれにあります。

 自分の場合は、移動制限せずに、描画順制御のためにZ方向の移動だけはSetWorldLocationで行っていた所、Z方向に速度が発生してしまいました。描画順がおかしくなったり、衝突するはずのコリジョンが当たらなくなったりしていました。

PlayStation4のコントーラをWindowsで使う時の開発側の注意点

 PS4がアップデートされて、PCからリモートプレイができるようになりました。
 それに合わせて、PS4コントローラをPCにUSB接続するだけで、ゲームパッドとして利用することができるようにもなりました。そのため、これからPCゲームをPS4コントローラで遊ぶ人が増えると思います。
 UEDirectInputPadPluginでPS4コントローラを使った所、キーコンフィグBPがちゃんと動かないと言われまして調査しました。

 その結果、開発側としては、考慮しておいた方が良い事項がありましたので、書いておきたいと思います。

PS4コントローラ

  • DirectInputで動作している
  • L2/R2ボタンは、回転軸とボタンの両方にマッピングされている
    • L2はX回転と7ボタン
    • R2はY回転と8ボタン
    • 回転は-1.0(押されてない)~1.0(一番下まで押し込む)の範囲の値が取得される*1

f:id:katze_7514:20160806154518p:plain

 1つのボタンで2つ値が返ってくるので、キーコンフィグを作る時にはちょっと頭にいれて置くと良いと思います。

 PS4コントローラが意図している所は理解できるんですが、どうせならXInputで動くようにしてくれれば良いのにね……。と思ったのですが、タッチパッドの情報を使うためにDirectInputにしているのかもしれません。PSボタンは13ボタン、タッチパッドボタンは14ボタンに割り当てられています。

*1:XInputのLT/RTは0.0~1.0

UE4 アクターの入力取得をコントロールする

docs.unrealengine.com
docs.unrealengine.com

 この辺りに書いてあることと独自調査を元にキー/パッド/マウス入力(以下単に入力)で悩まないようにするためのまとめとなっています。

入力を取得できるようにする設定

 あらゆるActorは適切な設定を行うことで、入力を取得できるようになります。

詳細パネル

f:id:katze_7514:20160719230556j:plain

 デフォルト設定は詳細パネルのInputの項目で行います。
 [AutoReceiveInput]の項目を入力取得したいPlayerIndexに設定することで、そのプレイヤーからの入力を取得することができます。一人用の場合、Player0にしておけばOKです。
 [Input Priority]については後述します。

BPノード

f:id:katze_7514:20160719230606j:plain

 実行中に入力取得の可否をコントロールする時は、上記のEnable/DisableInputを使います。
 名前通り、EnableInputは入力取得ができるようになり、DisableInputは入力取得ができなくなります。
 Actorピンに設定したいActorを接続します。
 PlayerControllerピンは入力を取得したいPlayerIndexを持つPlayerControllerを接続しますが、一人用の場合は何も接続しなくても大丈夫です。

入力取得の順序

 UE4の入力取得は、実はブロードキャストされておらず、決まった順番で入力情報が配送されていきます。あるアクターで入力が使われると、その後の順番になっているアクターは入力取得することができません。
 例えば、Wキーを取得するHogeアクターとFooアクターがあり、入力順序が、Hoge→Foo だった時、HogeでWキーを取得すると、FooではWキーの入力取得ができません。

 この入力順序は、詳細パネルの[InputPriority]でコントロールします。値が大きい方が先に入力を取得できます。
 ただし、いくつかのアクターは固定順序になっていますので、実際には以下の通りになります。

  1. 以下に無いActorがPriority降順
  2. PlayerControllerのComponent
  3. PlayerController
  4. [常にロード]のLevel
  5. PlayerPawnのComponent
  6. PlayerPawn

の順番に配送されていきます。
 PlayerPawnとはPlayerControllerにPossessされているPawnのことです。
 同一Priorityの時は、後から生成されたもの、後からEnableInputをしたアクターが優先度高くなります。

 なお、配送処理自体はPlayerControllerのTickタイミングで行われていますので、PlayerControllerのTickを停止するとすべての入力を取得できなくなりますので、ご注意ください。

BlockInput

 詳細パネルに[BlockInput]という項目がありました。同一名のBPノードもあります。
 入力配送処理を終えるフラグです。このフラグがONになっているアクターより優先度が低いアクターは入力が一切取得できなくなります。

 DisableInputノードは、入力取得順序から一時的に外れる処理となっていますので、優先度が低いアクターが入力取得ができなくなることはありません。

ConsumeInput/GamePause

f:id:katze_7514:20160509155946j:plain

 入力にまつわる重要な設定に、ConsumeInputとGamePauseがあります。
 ConsumeInputは、優先度の低いアクターに入力を渡さないかどうかの設定です。先ほど上げた例ですと、HogeアクターのWキー入力のConsumeInputフラグをOFFにすると、FooアクターでもWキーを取得できるようになります。
 アクターの入力はGamePauseの影響を受けますので、GamePauseをすると入力取得を行いません。しかし、メニュー操作などGamePause中でも入力を取得したい時は、[Execute when Paused]フラグをONにすることで、GamePause中でも取得されるようになります。

UE4 Navigationができない時に確認すること

 何度もNavigationができたりできなかったりして、いい加減嫌になったので本腰をいれて調べてみました。
 Navigation(static)が使えるのは以下の条件をすべて満たしている時になります。

  1. AIControllerを持つPawn
  2. UE4が用意したMovementComponentを持つPawn
    • CharacterMovement か FloatingPawnMovement
  3. NavMeshBoundsVolumeが「常にロード」になっているLevelに配置されている

以上です。
 サブレベルを使ってシーン遷移を実装していると、3番目の条件がかなり罠で、自分が悩まされていた原因でした。複数のNavMeshBoundsVolumeを切り替えながら使うということもできません。NavMeshBoundsVolumeがマップごとに変化する場合は、OpenLevelを使って遷移しないとダメなようです。
 なお、NavigationをDynamicにした場合はゲームが実行されてからNavMeshが計算されるので、この限りではありません。