情報は力ではない

UE4 とか Blender とか。

UE4 + ARCore でデプスシャドウを実装してみた。

概要

今、UE4 + ARCore でアプリケーションを作ってます。 現実世界に Gray ちゃんを召喚して好きなポーズをつけることができるアプリを目指してます。

キャラクターを Spawn したときに、現実世界に落とす影を、丸影じゃなくてキャストシャドウを使いたいなぁと思ったので、やってみました。 少し C++ を使ってます。

まだ荒いですが、こんな感じです。

f:id:masahiro8080:20181125192750p:plain

環境

Version
Unreal Engine 4.21.0
Android 9 (Pixel 3 XL)

方針

影を落とすのにどういう方法があるのかわからなかったので調べてたのですが @ruyo_h さんの MToon の記事をたまたま見つけました。

qiita.com

この記事の方法が使えそうだったので、実装してみることにしました。

ざっくり説明すると、光源方向に配置した SceneCaptureComponent2D を使ってシャドウマップを作成し、影を落とす地面のマテリアルで事前に作成したシャドウマップを見ながら地面メッシュの各ピクセルが影なのかどうかを決めていくという方法です。

実装

設定

Mobile HDR にチェックを入れています。 また、Build は ES 3.1 を使用しています。

f:id:masahiro8080:20181202220504p:plain f:id:masahiro8080:20181202220416p:plain

シャドウマップの準備

まずはじめにシャドウマップの準備をします。 シャドウマップには Render Target を使用します。Render Target を作成し、名前は RT_ShadowMap としておきます。 作成した Render Target の設定は次のようにしました。

f:id:masahiro8080:20181129204303p:plain

後で説明する SceneCaptureComponent2D の設定で、Render Target のアルファチャンネルに深度値を格納するために Render Target Format を RTF RGBA16f にしています。

次に SceneCaptureComponent2D の設定をしていきます。 Pawn を作成し、SceneCaptureComponent2D を追加します。 この Pawn には、影を落とすための地面メッシュも同時に追加しておきます。

f:id:masahiro8080:20181129205249p:plain

SceneCaptureComponent2D は次の画像のように設定しておきます。 SceneCaptureComponent2D の X 軸正方向が影を落としたいメッシュを向くようにします。

f:id:masahiro8080:20181129225831p:plain

入力した箇所は具体的には以下の項目です。

Property Value
Projection Type Orhographic
Ortho Width 256
Texture Target RT_ShadowMap
Capture Source Scene Color (HDR) in RGB, Scene Depth in A

Capture Source を Scene Color (HDR) in RGB, Scene Depth in A に設定することで、RT_ShadowMap のアルファチャンネルに SceneCaptureComponent2D からの深度値が入っているはずです。

以上で、シャドウマップの設定ができました。

シャドウマップの確認

確認のために次のようなマテリアルを作って確認してみました。

f:id:masahiro8080:20181129231001p:plain

RT_ShadowMap を設定した Texture Sample ノードのアルファ値を 1000 で割った値を Base Color につないでいるだけです。 RT_ShadowMap のアルファに格納されている深度値は cm 単位で入っていると思われます。 Base Color に入れる値を 0 ~ 1 の間の値にするために、ある程度大きめの値で割っています。

作成した Pawn と、上で作成したマテリアルを設定した Plane Static Mesh をマップに配置しました。 Plane Static Mesh にシャドウマップが表示されていることが確認できました。

f:id:masahiro8080:20181129232119p:plain

影を落とす地面メッシュのマテリアル

次に、作成したシャドウマップを影を落とすための地面メッシュにマッピングさせるためのマテリアルを作成します。 そのためには、影を落とすための地面メッシュの各ピクセルが、作成したシャドウマップのどこに対応するかを求める必要があります。 その変換を行うために、影を落とすための地面メッシュの各ピクセルのワールド座標から SceneCaptureComponent2D のローカル座標への射影変換行列が必要です。 この射影変換行列を取得するために、UBPFL_GrayAR という Blueprint Function Library を C++ で作成し、次のような C++ のコードを準備しました。

UBPFL_GrayAR.h

public:
    UFUNCTION(BlueprintCallable, Category = "GrayAR")
    static TArray<FLinearColor> getInverseMatrix(const FTransform& transform);

UBPFL_GrayAR.cpp

TArray<FLinearColor> UBPFL_GrayAR::getInverseMatrix(const FTransform& transform)
{
    TArray<FLinearColor> Ret;
    Ret.SetNum(4);

    FMatrix InverseMatrix = transform.ToMatrixWithScale().Inverse();
    for (int i = 0; i < 4; i++) {
        FLinearColor color;
        color.R = InverseMatrix.M[i][0];
        color.G = InverseMatrix.M[i][1];
        color.B = InverseMatrix.M[i][2];
        color.A = InverseMatrix.M[i][3];
        Ret[i] = color;
    }
    return Ret;
}

これは、取得した Transform から、射影変換行列の逆行列を返す関数です。 射影変換行列の逆行列は、 TArray として返却しています。

この関数から返された値を次の Blueprint のようにマテリアルのパラメータに渡して使用します。

f:id:masahiro8080:20181202182109p:plain

影を落とす地面メッシュのマテリアルは次のようにしています。

このマテリアルは @ruyo_h さんの記事のマテリアルをほぼそのまま使用しています。

f:id:masahiro8080:20181202181607p:plain

使用している custom ノードも次のような感じです。

入力したパラメータから射影変換行列を作り、メッシュの各ピクセルの絶対座標を 射影変換行列と掛け算することで、メッシュの各ピクセルの絶対座標を SceneCaptureComponent2D のローカル座標に変換しています。

float4x4 m;

m = float4x4(
  float4(r1.x, r1.y, r1.z, r1.w),
  float4(r2.x, r2.y, r2.z, r2.w),
  float4(r3.x, r3.y, r3.z, r3.w),
  float4(r4.x, r4.y, r4.z, r4.w));

float4 t = mul(position, m);

return t / t.w;

最後に地面メッシュに作成したマテリアルを設定し、確認を影が落ちていることを確認します。

f:id:masahiro8080:20181202213118p:plain

Android 上でもデプスシャドウが落ちていることが確認できました。

f:id:masahiro8080:20181202220255p:plain

補足

当初は @ruyo_h さんの記事にあるように RTF R32f を使用し、R チャンネルに深度値を設定するようにしていたのですが Android 上でシャドウマップが表示されませんでした。

次の画像は Render Target Format: RTF R32f で Capture Source: Scene Depth in R を使用して Android 上で実行したものです。

f:id:masahiro8080:20181202215134p:plain

次の画像は Render Target Format: RTF RGBA16f で Capture Source: Scene Color (HDR) in RGB, Scene Depth in A を使用して Android 上で実行したものです。 (Plane Static Mesh の角度がおかしいですが)

f:id:masahiro8080:20181202215106p:plain

実装に自分の間違いもあるかもしれないのですが、RTF R32f だとシャドウマップが表示されなかったので、今回は RTF RGBA16f で実装しました。

さいごに

モバイル上で実行するには負荷が高いのかもしれませんが、AR 上で影ができたのでとりあえずは満足です。

雑な説明になってしまいましたが、以上です。

NavMeshBoundsVolume で NavMesh がオーバーレイされないときの調整方法

f:id:masahiro8080:20180728121903j:plain

画像みたいに階段に NavMesh がオーバーレイされていないときの調整の仕方をよくわすれるので備忘録として書いておく。

Version

version
Unreal Engine 4.19

方法

Project Settings で Engine の中から Navigation Mesh を選択する。Generation の中の Cell Height の大きさを大きくする。

f:id:masahiro8080:20180728122444j:plain

大きくすると、ちゃんと NavMesh がオーバーレイされる。

f:id:masahiro8080:20180728122534j:plain

敵の頭上にある HP ゲージの作り方

今回は UE4 で、敵の頭上に HP ゲージを配置する方法。

f:id:masahiro8080:20180613223132j:plain

色々な方が説明されているかと思いますが、自分の備忘録として書いておきます。 自分は次のサイトの手順を参考にしました。

How can i make a floating health bar on enemy? - UE4 AnswerHub

Version
Unreal Engine 4.19.2

テンプレートは ThirdPerson Template を使用します。

HP ゲージのウィジェット作成

まずはじめに HP ゲージを表示するためのウィジェットを作成します。

コンテンツブラウザで右クリックをし、User Interface から Widget を作成します。ここでは名前を WBP_HealthBar としておきます。作成した WBP_HealthBar を開きます。 Palette から Progress Bar を検索し、Visual Designer にドラッグします。この Progress Bar が HP ゲージとなります。

次に、Details タブで Progress Bar の設定を編集します。 Anchors は画面中央のものを選択します。 Position や Size は次の画像のようにします。 Appearance の Fill Color and Opacity で色を緑っぽくしておきます。

f:id:masahiro8080:20180613221541j:plain

外部から指定した値に応じて Progress Bar の表示割合を変えるために Progress の Percent にバインディングを作成します。 Progress からPercent の Bind を選択し、Create Binding を選択します。 すると、Designer モードから Graph モードに切り替わります。Functions に関数が作成されているので、関数の名前を UpdateHealth としておきます。

Variables に変数を作成し、型を Float で名前を HealthRatio としておきます。 UpdateHealth の実装は次の画像のようにします。設定された HealthRatio を返すだけの関数です。 これで、HealthRatio の値に応じて Progress Bar の表示が変わるようになります。

f:id:masahiro8080:20180613221835j:plain

以上で、WBP_HealthBar の作成は終わりです。 次に、HP ゲージを持つコンポーネントを作成します。

敵の作成

コンテンツブラウザで Blueprint Class を選択し、Character を選択します。 ここでは名前を BP_TestEnemy としておきます。BP_TestEnemy を開きます。 Components タブで Mesh を選択し、Details タブの中にある Mesh Comonent の Mesh に SK_Mannequin を設定します。 少し高い位置にマネキンができると思うので、下げておきます。また Animation の Anim Class に ThirdPerson_AnimBP を設定しておきます。  また、Rotation の Z を -90 にしてマネキンが Arrow Component と同じ方向を向くようにしておきます。

Components タブの Add Component から Widget Component を選択します。名前は HealthBarWidget にしておきます。 Details タブの User InterfaceWidget Class オプションに WBP_HealthBar に設定すると、先ほど作成した HP ゲージが表示されるかと思います。 追加した HealthBarWidget をマネキンの頭上に来るように移動します。

f:id:masahiro8080:20180613223354j:plain

あとは HP に応じて HP ゲージが増減するように処理を追加していきます。

BP_TestEnemy に HP の概念がないので Variables に Health と MaxHealth をそれぞれ Float で追加します。 Health、MaxHealth の Default Value は共に 100.0 に設定します。 また後の処理で使うために WBP_HealthBar 型の変数も追加しておきます。

Event Graph タブに移動し、Begin Play Event ノードを次のように実装します。 ここでは、表示される HP ゲージが初期では最大になるように Health Ratio を 1.0 に設定しています。 また、後の処理で使用するために WBP_HealthBar 変数に WBP_HealthBar を設定しています。 

f:id:masahiro8080:20180613222415j:plain

次にダメージを食らったら、HP ゲージが減る処理を記述していきます。 Event Graph 上で右クリックを押し、Any Damage を選択します。 Any Damage Event は次のように実装します。 Sequence から伸びる Then 0 で HP を減らし、Then 1 で HP ゲージの増減を設定しています。

f:id:masahiro8080:20180613222618j:plain

HP ゲージの実装は終わったのですが、今のままだと HP ゲージがカメラの方向を向いてくれないので HP ゲージが常にカメラの方向を向くようにします。

Tick Event ノードを次のように実装します。 これは、HP ゲージが常にカメラの方を向くように設定しています。

f:id:masahiro8080:20180613222748j:plain

以上で、HP ゲージの処理は終わりです。

確認

最後に確認のために、BP_TestEnemy をマップに配置します。 そして、Modes タブから Pain Causing Volume を BP_TestEnemy と同じ場所に配置します。Details タブの Damage Per Sec を 10.0 に設定します。 

f:id:masahiro8080:20180613223903j:plain

Play を行うと、HP ゲージが減っているのがわかると思います。 

以上が敵の頭上の HP ゲージの作成方法です。 ダメージを食らった分だけ HP が減るアニメーションの実装については、自分はまだやってないのですが、例えば、次を見るとよくわかるかと思います。

プログレスバーのアニメーション。 - UE4 AnswerHub

敵に攻撃したり、敵が倒れるアニメーションをつけると、よりゲームっぽくなるかと思います。

今回はここまでです。ありがとうございました。

攻撃の当たり判定の実装方法

攻撃の当たり判定を実装したかったので、自分が行った実装方法を記述しておきます。

この記事で説明するのは次の動画のように、相手に攻撃して、ダメージを与えるところまでです。 わかりづらいですが、左上に与えたダメージを表示しています。


UE4 攻撃当たり判定処理

Unreal Engine
Version 4.19.2

攻撃の当たり判定の実装方法

攻撃の当たり判定の実装を Paragon の Shinbi を使って実装していきます。 理由は、Shinbi のアセットには、すでにコンボの実装が行われており、 あとは当たり判定のロジックを追加するだけで良いので、楽だからです。

実装をするにあたり、Third Person Template でプロジェクトを作成し、 マーケットプレイスから Paragon: Shinbi を入手して、プロジェクトに追加しておきます。

剣に Collision を追加する

まずはじめに Shinbi が持つ剣にコリジョンを追加します。

コンテンツブラウザから ShinbiPlayerCharacter を探し、開きます。 Components タブから Mesh を選択し、AddComponent ボタンを押して Box Collision を追加します。 名前は SwordCollision にしました。 SwordCollision を選択し、Details タブを見ると Parent Socket という項目があります。 ここに Socket を指定すると、SwordCollision がその Socket に追従するようになります。それでは、 SwordCollision に指定する Socket を探したいと思います。コンテンツブラウザから Shinbi_Skelton を探し、開きます。 weapon_r ボーンの下に Sword ソケットがあり、これが指定する Socket にちょうど良さそうです。

ShinbiPlayerCharacter に戻り、Components タブで SwordCollision を選択し、 Details タブの Parent Socket に Sword と入力します。虫眼鏡アイコンを追加して、検索するのが良いかと思います。 あとは、大きさと位置を調整します。今回はこのような感じにしました。

f:id:masahiro8080:20180526111836j:plain

ダメージ処理の実装

次に、相手にダメージを与える処理を実装していきます。

ShinbiPlayerCharacter の Event Graph を開き、右クリックを押し、Custom Event を追加します。 名前は AttackBegin/End としました(カウチナイトのサンプルから拝借しました)。実装は次の画像のようにしました。

f:id:masahiro8080:20180526122458p:plain

AttackBegin/End イベントは Attacking という bool 値を Input に持ちます。これは、攻撃中かどうかを表すフラグです。 Attacking が true の場合は、Box Collision の Collision を Enabled にし、当たり判定イベントを起こします。 false の場合は、No Collision にし、当たり判定イベントを起こさないようにします。 次に、SwordCollision の OnComponentBeginOverlap イベントを追加しています。 これは SwordCollision がオーバーラップした際に呼ばれます。SwordCollision の Collision Preset は何も指定していなければ OverlapAllDynamic となっているかと思います(Detail タブから確認できます)。 ですので、全てのオブジェクトとオーバーラップイベントを起こします。 オーバーラップした相手(OtherActor) が Self(自分自身) でない場合に Apply Damage ノードを使用して相手にダメージを与えるように実装しました。 SwordCollision は全てのオブジェクトをオーバーラップイベントを起こすので、自分自身ともオーバーラップイベントを起こしてしまいます。 ですので、自分自身とオーバーラップした際にはダメージ処理が走らないようにしています。 ApplyDamage ノードで指定する与えるダメージ(Base Damage)は固定でもよかったのですが、とりあえず関数化しておきました。 Base Damage 変数に乱数を足した値を返すような関数です。

f:id:masahiro8080:20180526122539p:plain

最後に、Begin Play イベントにも次のような処理を記述しておきます。 これは、初期状態では SwordCollision は Collision Enabled 状態なので、No Collision にし、攻撃時のみ Collision Enabled になるようにしています。

f:id:masahiro8080:20180526122711p:plain

ここまでで相手にダメージを与える処理ができました。

アニメーションからダメージ処理を呼ぶための実装

次に、攻撃アニメーションに合わせて先ほど実装した AttackBegin/End を呼ぶようにしたいと思います。 そのために、Shinbi の攻撃アニメーションを探します。 ShinbiPlayerCharacter の Event Graph に Combo System Setup とコメントされた箇所があり、ここに 3 つ Play Anim Montage があります。 この Play Anim Montage に設定されいる Animation Montage が攻撃のアニメーションであり、上から順に 1, 2, 3 コンボ目のアニメーションです。

f:id:masahiro8080:20180526122728p:plain

これらのアニメーションに当たり判定開始と終了を示す Notify を追加していきます。

1 コンボ目の Animation Montage である PrimaryMelee_B_Slow_Montage を開き、Notifies Area を見ます。 Notify Track を右クリックし、New Notify を押します。名前は AttackStart にします。 この追加した Notify を当たり判定開始のフレームに移動します。今回はだいたい 8 フレーム目に移動しました。 同様の手順で、名前が AttackEnd の Notify を 12 フレーム目に移動しました。

次に 2 コンボ目の Animation Montage である PrimaryMelee_C_Slow_Montage を開き、先ほどと同じように Notifies Area にある Notify Track を右クリックします。 一度作成した Notify は新規に作成する必要はなく、Custom 欄に追加されている AttackStart と AttackEnd を Notify Track に追加し、 当たり判定開始位置と終了位置にそれぞれ移動します。3 コンボ目の PrimaryMelee_D_Slow_Montage も同様の手順で Notify を追加します。

これで、アニメーションブループリントで当たり判定開始と終了を検知できるようになりました。

次に、Shinbi_AnimBlueprint の Event Graph を開きます。 Event Graph 上で右クリックし、Attack と入力すると、AnimNotify_AttackStart Event と AnimNotify_AttackEnd Event が公開されています。 これらのイベントはアニメーション再生時に、先ほど設定した AttackStart Notify や AttackEnd Notify のフレームを通過した際に呼び出されます。 Event Graph にこれら Event を配置し、図のようにノードを配置します。 Try Get Pawn Owner ノードでこの AnimInstance を設定している Pawn を取得し、ShinbiPlayerCharacter にキャストします。 その後、ShinbiPlayerCharacter で実装した Attack Begin/End ノードを呼び出します。当たり判定開始時は Attacking を true に、 当たり判定終了時は Attacking を false にしています。

f:id:masahiro8080:20180526122920p:plain

ここまでで Shinbi が攻撃アニメーションをすると剣に当たった敵にダメージを与えることができるようになりました。 次にダメージが敵に与えることができているかの確認をしていきます。

ダメージ処理の確認

まず、World Settings を開きます。Game Mode 欄にある、Default Pawn Class を ShinbiPlayerCharacter にします。 これで、ゲーム開始時のキャラクターが Shinbi になります。

f:id:masahiro8080:20180526123004j:plain

次に、敵となる Character を配置します。 コンテンツブラウザから ThirdPersonCharacter を探します。今回は、この Character を敵とします。 ThirdPersonCharacter の Event Graph を開き、右クリックを押し、Any Damage ノードを配置します。 このノードは Apply Damage 等でダメージを与えられた際に呼び出されます。 ダメージ処理の確認のために、Print String ノードでダメージを表示できるようにしておきます。 (Draw Debug String ノードを使ってもよかったのですが、4.19 では、Position に設定した場所に値が表示されないようです。)

f:id:masahiro8080:20180526123055p:plain

あとは、この ThirdPersonCharacter をマップに配置し、ゲームを開始します。 左クリックを押すことで Shinbi は攻撃できます。 配置した ThirdPersonCharacter に近づき、攻撃すると、左上に与えたダメージが表示されると思います。

これでダメージ処理の確認ができました。

終わりに

今回はこれで終わりですが、敵に HP を設定して、死を実装したり、ダメージを受けた際のヒットリアクションを追加したり、 攻撃が当たると同時に音を出してみたり、敵の HP バーを表示して残り HP が表示されるようにしたり、AI を実装して、敵との戦闘ができるようにしたりと 色々と拡張ができると思います。今回の説明がゲーム作成の助けになれば嬉しいです。

次の動画は、ダメージをくらうとヒットリアクションをし、HP が 0 になったら、敵が倒れる実装を追加してみたやつ。 この記事では、ヒットリアクションと倒れる機能まで説明しなかったので、そのうちちゃんと説明したい。

髪の毛作るときに見たチュートリアルサイトとかのまとめ

ここのとこずっと hair cards を使って髪の毛を作ってます。

f:id:masahiro8080:20180313213307j:plain

そのとき調べたサイトをまとめておく。一部 hair cards 関係ないものもあるけど。

チュートリアル動画

Real time hair tutorial

有料のチュートリアル動画。テクスチャ作成から配置までの動画です。 テクスチャはパーティクルを使って作成しているみたいです。 動画だけでなく、髪の毛のモデルや Marmoset の scene もついているようです。 自分は買ってまだ途中までしか見れていないです。

Blender Hair Tutorial Part 1 (styling the hair)

www.youtube.com

パーティクル使って髪の毛を作るチュートリアル動画。

asian girl realtime hair tutorial

www.youtube.com

hair cards を使ってるチュートリアル動画。 パーティクルから hair テクスチャを作成して配置してる。 音声なし。

Blender 3d Tutorial: Hair Creation For Games Part 1

www.youtube.com

hair card で作成しているチュートリアル動画。

記事

Character - polycount

polycouont の wiki にあるキャラクターの項目。なんとなくキャラクターについてまとまっている感がある。 キャラクターのサンプルへのリンクもあったりする。

Creating realistic hair with textured planes - Уроки 3ds Max

テクスチャから配置までを一応説明してくれてるサイト。 hair card を交差させるとダメとか、hair card を離れておくのはダメとか書いてる。 多分、3ds max での話だと思う。

Tutorial: Real-time fur & hair - Sketchfab Blog

skcetchfab にあるチュートリアル。記事ではモンスター?の体毛をサンプルにしてるけど 髪の毛も基本同じだと思う。

How do I create real-time hair for games? | 3D Artist - Animation, Models, Inspiration & Advice | 3DArtist Magazine

hair cards のチュートリアル記事。 内容はまだよんでないのでわかってない。

また増やすかも

とりあえずここまで。

仮の眼球テクスチャの作成方法 (Cycles Render)

以前、Blender Render で仮の眼球テクスチャの作り方を書いた。

massa8080.hatenablog.com

この記事を書いたときは Cycles Render での仮の眼球テクスチャの作り方がわからなかったのだけれど 今、作り方を考えてみると案外簡単なことに気づいたので、メモをしておく。

Version

Blender 2.79

作り方

今回はグローバル座標の Y 軸負の方向を向く眼球を作成します。ノードとその出力結果は次図です。

f:id:masahiro8080:20180114121934p:plain

順に説明すると、Texture Coordinate の generated からリンクを伸ばし、Mapping につなげます。 Mapping の Rotation の Y を 90 度に設定します。この Y の Rotation を変更するのは そのままノードをつなげていくと X 軸方向にグラデーションがかかるからです。 また Y なのは、ローカル座標で回転させるからです。 Gradient Texture と Color Ramp を繋ぎます。 最後に Diffuse BSDF につないで出力です。

以上です。

所感

虹彩とかも入れてみたいけど、わからなかったのでそれはまたの機会にします。 もしかしたらテクスチャ作ったほうが早いかもしれないけど。

Substance Painter で生成したテクスチャの Blender への設定方法 (Node Wrangler)

ちょくちょく使うのだけれど、毎回使い方を忘れるので自分用にメモ。

このメモの内容は、BlendxJP2 での和牛先生のスライドの 6. Substance Painter から Blender へ に書いていることと同様の内容です。

www.slideshare.net

Version

Blender 2.79

手順

  1. Substance Painter でテクスチャを作成します。
  2. Blender で Node Wrangler アドオンを追加します。 f:id:masahiro8080:20180113115116p:plain
  3. ノードエディタで Principle BSDF を作成します。 f:id:masahiro8080:20180113120059p:plain
  4. Principle BSDF を選択した状態で Ctrl + Shift + T でテクスチャを選択。複数選択可能。
  5. ノードが組みあがります。 f:id:masahiro8080:20180113120027p:plain

参考

Blender Addon Review: Node Wrangler(ノード使い必須) – CGrad Project Extensions:2.6/Py/Scripts/Nodes/Nodes Efficiency Tools - BlenderWiki