GameProgrammar's Night

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

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のコードは、平気でグローバル変数を使います

UE4 C++でイベント/イベントディスパッチャーを作る

 UE C++には、デリゲートが用意されています。デリゲートとは簡単に説明しますと、メソッド・関数などを変数に保存して、任意のタイミングで呼び出せる仕組みです。詳しくはググってください。

docs.unrealengine.com

 このデリゲートの中に、BP上でイベント/イベントディスパッチャーとして扱われる仕組みがありますので紹介します。

イベントディスパッチャー

 良く使うであろうイベントディスパッチャーの実装から先に説明します。
 動的マルチキャストデリゲートを実装するとBP上ではイベントディスパッチャーとして扱われます。

docs.unrealengine.com

実装例

// 動的マルチキャストデリゲート(イベントディスパッチャー)の宣言
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyActorOnEventDispather);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UMyActorComponent : public UActorComponent
{
    GENERATED_BODY()

    virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override;

public:
   // 動的マルチキャストデリゲート(イベントディスパッチャー)の定義
   UPROPERTY(BlueprintAssignable, Category="MyActor")
   FMyActorOnEventDispather OnEventDispather;
};

void UMyActorComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction )
{
   OnEventDispather.Broadcast(); // イベントディスパッチャーをCallする
}

 DECLARE_DYNAMIC_MULTICAST_DELEGATEマクロを使って、イベントディスパッチャー用のクラスを作ります。()の中がクラス名となります。
 その作ったクラスのプロパティをクラス内で定義し、Assingnableに設定します。そうすることでBPで作ったイベントをバインドすることができます。
イベントディスパッチャーとして宣言した変数のBroadcastメソッドを呼ぶことが、イベントディスパッチャーをCallすることと同じことになります。

 BP上でMyActorComponentをActorに追加して、使った例は以下の通り。
f:id:katze_7514:20160522001504j:plain
 イベントディスパッチャーとして使用できています。

 また、イベントディスパッチャーで引数を取る場合は、引数ありの宣言マクロを使います。

// 1つ引数を取り、型がboolで引数名がbSuccess のイベントディスパッチャー宣言
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyActorOnEventDispather, bool, bSuccess);

 引数は8個まで取ることができます。引数の数を増やしたら、OneParamのOneの部分を引数の数の英語に変えます*1
 戻り値はBPのイベントディスパッチャー同様に扱えません。

イベント

 BPでのイベントに相当するのは、動的デリゲートです。デリゲートの中にイベントもありますが別ものです。名称がまぎらわしいですね。
docs.unrealengine.com

実装例

 実装の仕方は、動的マルチキャストとほぼ同じです。
 DECLARE_DYNAMIC_MULTICAST_DELEGATEマクロではなく、DECLARE_DYNAMIC_DELEGATEを使います。

// 動的デリゲート(イベント)の宣言
DECLARE_DYNAMIC_DELEGATE(FMyActorOnEvent);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UMyActorComponent : public UActorComponent
{
    GENERATED_BODY()

   // BPから動的デリゲート(イベント)を呼び出すメソッド
   UFUNCTION(BluprintCallable, Category="MyActor")
   void CallOnEvent();

public:
   // 動的デリゲート(イベント)の定義
   UPROPERTY(BlueprintReadWrite, Category="MyActor")
   FMyActorOnEvent OnEvent;
};

void UMyActorComponent::CallOnEvent()
{
   OnEvent.ExecuteIfBound();
}

 イベントディスパッチャー実装と違うのは、イベントとして宣言した変数にBlueprintAssignableが使えません。代わりに通常の変数と同じようにBluprintReadWrite・BlueprintReadOnlyなどを使います。
 また、イベントの呼び出しは、Execute・ExecuteIfBoundメソッドのどちらかを使いますが、それらのメソッドはBPから直接呼ぶことができませんので、BP上から呼べるようにメソッド(CallOnEventメソッド)を別途追加します。
 BlueprintReadWriteで宣言すると、BP上のイベントをバインドできるようになります。また、イベントディスパッチャーにバインドすることも可能です。

f:id:katze_7514:20160522012117j:plain

 引数を取る時も、イベントディスパッチャー実装と同じです。

DECLARE_DYNAMIC_DELEGATE_OneParam(FMyActorOnEvent, bool, bSuccess);

*1:2個ならTwoなど

UE4 UMGで入力を取得する

 UMG(UMGのBP)では、各Widgetごとにマウスイベントを取得することはできますが、通常のキーイベントを取得するこはできません。

マウスイベント キーイベント
f:id:katze_7514:20160507212145j:plain f:id:katze_7514:20160507212149j:plain

 
 そのため、UMGでメニューを作りキーで操作したい場合、いつも通りにはいきません。
 UMG上でキーを取るには、2つ方法があります。UMGのOnKey系関数をオーバーライドする方法とUMGの外から入力をもらう方法です。

UMGのOnKey系関数をオーバーライドする

f:id:katze_7514:20160507220341j:plain

 OnKeyUP/OnKeyDownをオーバーライドすると、キーアップ/キーダウンされると対応する関数が呼ばれます。

f:id:katze_7514:20160507220927j:plain

 対応するキーはInKeyEvent引数に入っています。それを実際のKey情報に変換(GetKeyノード)して、何のキーかを調べます(Equal(Key)ノード)。キーをUMGで利用した場合は、Return値にHandledノードを接続し、使わないキーだったらUnHandledノードを接続します。

 また、UMGのOnKey系関数を有効するには、UMGに入力モードがある必要があります。入力モードの切り替えは、SetInputModeUIOnlyノードです。AddToViewportノードを使って画面に表示してから呼びます。
 UMGを消す時は忘れずに入力モードをGameに戻す必要があります。Gameに戻すにはSetInputModeGameOnlyノードを呼びます。入力モードがUMGにあるままだと、UMGを消してもLevelやActorで入力が取れなくなりますので、忘れないようにしましょう。

f:id:katze_7514:20160508000710j:plain

 入力モードを持つことはフォーカスを得るという意味にもなるのですが、UE4.11からフォーカスを得られるかどうかのフラグが追加されましたので、そのフラグもオンにします。

f:id:katze_7514:20160530231433p:plain

 UMGとしては、こちらが正式な方法なのですが、一つ問題があります。OnKey系関数では、ActionMappingと連携ができません。OnKey系関数は対応するキーデータを素の状態でしか受け取れず、またキーデータからActionMappingに変換する方法もありません。

 ActionMappingと連携するためにも、UMGの外から入力を受け取るのを、私としてはお勧めします。

UMGの外から入力をもうらう

f:id:katze_7514:20160508003837j:plain

 UMGのBPに、操作用の関数/イベントを作成して、それをLevel/PlayerControllerから呼ぶ形にします。
 上図では省略していますが、UMG表示されているかのチェックなど、メニューを誤操作しないようにする判定を挟むのもお忘れずに。
 
 メニューはGamePauseをかけてから表示することが多いと思いますので、メニュー操作用のキーイベントは忘れずに「Execute when Paused」にチェックをいれておきましょう。

f:id:katze_7514:20160509155946j:plain