GameProgrammar's Night

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

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が計算されるので、この限りではありません。

UE4 BPで2次元配列を実現する

 BPは使えるデータ構造が少なく1次元配列しか使えません。1次元配列だけでも組むこと自体はもちろん可能ですが、せめて2次元配列ぐらいは欲しい所です。
 というわけで、BPで2次元配列を扱うためのTIPSを紹介します。

1次元配列を2次元配列のように扱うインデックス計算をする

 配列のインデックス計算を工夫して、実体は1次元配列でも2次元配列のようにアクセスできるようにします。
 2次元配列として扱いたい配列に対して、以下のようなマクロを用意します。

f:id:katze_7514:20160625131401j:plain

 2次元配列にアクセスするための2つのインデックスと配列あたりのサイズを受け取って、実際に格納されているインデックスを計算します。
 これを使って

f:id:katze_7514:20160625145450j:plain

 のように使います。疑似コードで書くと

float f = FloatArray[0][2];

のような感じになっています。

 この形で2次元配列を使う時は、AddやRemoveを使うとインデックスがズレてしまうので使うことはできません。必ずSetItemArrayElemノードを使ってインデックスを使用してアクセスするようにしてください。SetItemArrayElemを利用した専用のAdd/Removeマクロを作ると良いと思います。

BP構造体を利用する

  • 配列の配列は作れない
  • 構造体の配列は作れる
  • 構造体のメンバーに配列を作ることはできる

以上のことより、構造体を介すことて簡単に2次元以上の配列を作ることができます。

 配列をメンバーにした構造体を作ります。 
f:id:katze_7514:20160625140501j:plain

 その構造体を配列にします。
f:id:katze_7514:20160625140630j:plain

 二次元配列の完成です。アクセスは以下の通り

f:id:katze_7514:20160625140954j:plain

 ゲームのパラメータを扱うならば、構造体に名前も付けられますし、こちらの方が優れているかもしれません。
 状況に応じて使い分けてみてください。

まとめ

 2次元配列の実現の仕方でした。これらの話を拡張することで多次元配列を作ることも可能です。
 それにしても、BPのデータ構造の貧弱さは、本当にどうにかして欲しい所です……。Mapのエディタ対応はよ!

UE4 DistanceField フォントで遊ぶ

 UE4は、DistanceFieldを使ったフォント描画に対応しています。うちのブログでもかなり前ですが扱った話題でもあり、ちょっといじってみました。
 DistanceFieldを使ったフォントテクスチャの利点は、拡大に強かったり、ちょっとした文字装飾をシェーダーで書けることです。詳しくはググってみてください。

 ここでは、UE4でDistanceFieldフォントへの縁取りと、DistanceFieldFontTextureの作り方を紹介します。

縁取りマテリアル

 作ったマテリアルは以下の通りです。

f:id:katze_7514:20160616210438j:plain

 与えているパラメータは、Distance境界値(DistanceThreshold)、縁取りの色(BorderColor)、VertexColor、Fontテクスチャです。
 ifノードで、Distance境界値とFontテクスチャの値を比較して、縁取り部分かテキスト部分かを判定し、対応する色を接続しています。OpacityにつながっているLerpは文字の太さを決めています。

 下図のように描画されます。

f:id:katze_7514:20160616212405j:plain

DistanceFieldFontテクスチャの作り方

 UE4上でDistanceFieldFontの作り方はドキュメントにもEpicWikiにも無かったので、試行錯誤とソースから発掘してみました。以下の手順で作ることができます。

ベースとなるFontテクスチャを作る

 コンテンツブラウザ上で、フォントを作りフォントエディタを立ち上げます。
 詳細パネルのFontCacheTypeをofflineに変更します。

f:id:katze_7514:20160616214031j:plain

 ダイアログが出るのでYESをおして

f:id:katze_7514:20160616214312j:plain

 フォントを選択します。

f:id:katze_7514:20160616214224j:plain

 フォントテクスチャが生成されます。

f:id:katze_7514:20160616220121j:plain

Fontの設定をDistanceField対応にする

ImportOptions

 「詳細パネル」→「OfflineFont」→ImportOptions を開きます。

f:id:katze_7514:20160616214809j:plain

 緑枠で囲った部分が、DistanceFieldの設定です。
 UseDistanceFieldAlphaにチェックを入れます。他の2つのパラメータはDistanceを計算する範囲を示すパラメータです。必要に応じて変えてください。

テクスチャ設定

 テクスチャをクリックして選択状態にします。「ページ詳細」パネルにテクスチャ設定が表示されますので、CompressionをDistanceFieldFontにします。

f:id:katze_7514:20160616215324j:plain

DistanceFieldFontTextureを生成!

 メニューバーのAssetから、「Reimoprt NewFont...」を選ぶとDistanceFieldFontテクスチャが作られます。

f:id:katze_7514:20160616220053j:plain

 完成です。
 あとは、フォントテクスチャを要求されるところで、DistanceFieldFontテクスチャを設定すればDistanceFieldフォントとして描画されます。

 図ではDistanceFieldFontが大きくてテクスチャが2枚になってしまっています。これはテクスチャサイズを変更することで回避することができます。
 テクスチャサイズは、ImportOptionで設定できます。
f:id:katze_7514:20160616220520j:plain

 以上です。
 おもしろい文字装飾マテリアルができたら、ぜひ教えて下さい!

UE4 Configデータをお手軽に取得する

 UE4には、起動時オプション*1を扱えるようになるコンフィギュレーションという仕組みがあります。

docs.unrealengine.com

 基本的にC++で使うことが想定されており、クラスにUPROPERTY(config)などと設定することで自動保存復元もしてくれます。

 本当にちょっとしたものを使うのにいちいちクラスを作るのも面倒くさいので、直接取得する方法を紹介します。

 案の定*2、こういう便利オブジェクトはグローバルに存在していました。そのままずばりGConfigです。
 以下のように使います。

bool bBackground;

check(GConfig);
if(!GConfig->GetBool(TEXT("DirectInputPadPlugin"), TEXT("Background"), bBackground, GInputIni))
{
   bBackground=false;
}

 GConfigは、欲しい型で値をいれてくれるGet***というメソッド群がありますので、それらを使います。
 引数はすべて共通で、セクション名・キー名・値の入れ先の変数・値の入っているConfigファイル、と指定します。戻り値は、指定した値が存在した時はtrue、無い時はfalseになります。
 Configファイルは、カテゴリごとにやっぱりグローバル変数として存在してますので、必要なカテゴリの変数を使います。上記の例だと、入力カテゴリとなります。

 対応するコンフィギュレーションは以下のように書きます。

[DirectInputPadPlugin]
Background=true

*1:想定は起動時オプションですが、もちろん、他の用途にも使えます

*2:UE4のコードは、平気でグローバル変数を使います