情報は力ではない

UE4 とか Blender とか。

GameplayAbility を最初に使用した際にゲームが止まるのを修正する方法

GameplayAbility を使っているプロジェクトで開発していると、エディタの起動後、ゲームをプレイして初めて GameplayAbility を使用した場合に一瞬から数秒ゲームが固まることがあります。

多分、GameplayAbility の使用時に何かを探索しているのかなとか考えてたのですが、解決方法が、AnswerHub に書いてました。

https://answers.unrealengine.com/questions/820453/gameplay-ability-long-lag-on-first-use.html

DefaultGame.ini に GameplayCue のあるフォルダを指定してあげると解決できるみたいです。

 [/Script/GameplayAbilities.AbilitySystemGlobals]
+GameplayCueNotifyPaths="GameplayCue があるフォルダのパス"

GameplayAbility を最初に使用する際に、CueManager が作成されるらしく、その処理の中でプロジェクト内の GameplayCue を探し出し、Cue をプールする処理があるそうです。 上記の GameplayCueNotifyPaths は、どこのフォルダを探索するかを指定する設定のようです。 GameplayCueNotifyPaths のデフォルトは /Game/ なので、指定しないとプロジェクト全体を探索してしまうため、ゲームが固まることがあるみたいです。 なので、上記のように GameplayCueNotifyPaths を指定してあげることで解決します。

ちなみに、親切にもログにちゃんと書いてました。

LogAbilitySystem: Warning: No GameplayCueNotifyPaths were specified in DefaultGame.ini under [/Script/GameplayAbilities.AbilitySystemGlobals]. Falling back to using all of /Game/. This may be slow on large projects. Consider specifying which paths are to be searched.

ちゃんとログは読まなきゃですね。

今回は以上です。

GameplayEffect で継続ダメージ処理をやってみる。

最近、GameplayEffect について調べてます。少しわかってきたので、簡単なサンプルを作ってみたいと思います。今回は毒状態みたいな継続ダメージ処理を GameplayEffect で実装してみたいと思います。

前提

  • Unreal Engine 4.22.3 を使用しています。
  • AttributeSet の実装や AbilitySystemComponent の実装については記述しません。
  • AttributeSet には Health という HP を表す Attribute で設定されていると仮定します。

やりたいこと

今回実装してみるものの仕様はざっくり以下になります。 * 毒状態のような継続ダメージ処理 * 継続する期間は 15 秒間 * 5 秒おきにダメージを喰らう * 継続ダメージが有効中は、パーティクルを発生させる * ダメージ時は、パーティクルを発生させる

下2つの仕様は GameplayEffect が有効かどうかを視覚的に確認したいがために追加しました。

それでは、実装していきます。

GameplayEffect の作成

まず、GameplayEffect のブループリントを作成します。

Blueprint Class から GameplayEffect クラスを選択し、GameplayEffect のブループリントを作成します。

f:id:masahiro8080:20190707132044p:plain

作成した GameplayEffect の Class Defaults で設定をしていきます。 今回は GameplayEffectPeriodDisplay を使用します。

Gameplay Effect カテゴリの設定

まず、Gameplay Effect カテゴリの設定をしていきます。 以下、Gameplay Effect は GE と省略します。

f:id:masahiro8080:20190707131851p:plain

GE の有効時間を一定期間に設定するために、Duration PolicyHas Duration にします。

次に、有効時間を設定するために、Duration Magnitude の項目を設定していきます。 Magnitude Calculation Type は値をどのように設定するかに関する項目です。 今回はそのまま float 値で 15 秒と設定したいので、Scalable Float に設定し、 Scalable Float Magnitude に 15.0 と設定します。 これにより、この GE を起動してから 15 秒間の間、この GE は有効状態になります。

次にダメージ処理のための設定を行います。 Modifiers は、指定した Attribute をどのように更新するかを設定します。 今回は GE が適用される毎に Health を 50.0 ずつ減らすようにしてみました。 そのため、Attribute には AttributeSet の Health、Modifier Op には Add を設定しています (DefaultAttributeSet はこのサンプル用に作成しておいた AttributeSet クラスの名前です)。 これにより GE の実行時に Health の値に -50.0 を追加してくれます。

Period カテゴリの設定

次に Period カテゴリの設定を行います。 ここでは、何秒おきにこの GE を実行するかを設定できます。

f:id:masahiro8080:20190707133055p:plain

今回は 5 秒おきに GE を実行するので、Period に 5.0 を設定しました。 また、GE を起動直後はダメージを発生させたくないので、Execute Periodic Effect on Application のチェックを外しておきました。 この項目を有効にしておくと、GE が起動した際にもダメージが発生することになります。

Display カテゴリの設定

最後に Display カテゴリを設定していきます。 Display カテゴリはエフェクトやサウンドのようなものを設定するために存在する項目のようです。 といっても実際に設定するのは、主にタグかと思います。

f:id:masahiro8080:20190707133418p:plain

Gameplay CuesGameplay Cue TagsEffect.Poison というタグを作成し、設定しました。 これを設定しておくことで、この GE が起動または実行されたときに、このタグに紐づいた Gameplay Cue が処理します。

以上で、GE の設定は終わりです。次に Gameply Cue の実装をしていきます。 Gameplay Cue では、GE が起動したときやダメージ処理が実行されたときのエフェクト処理を記述していきます。

Gameplay Cue の作成

Blueprint Class から GameplayEffect クラスを選択し、GameplayCueNotify_Actor のブループリントを作成します。

f:id:masahiro8080:20190707133708p:plain

作成した Cue の Class Defaults の Gameplay Cue カテゴリの Gameplay Cue TagDisplay で設定したタグを設定します。 これで先ほどの GE が起動された場合にこの Cue が紐づくようになります。

f:id:masahiro8080:20190707133748p:plain

次にエフェクトの発生処理を記述していきます。

まず初めに、OnActive 関数をオーバーライドし、次のように実装します。 OnActivate 関数は、この Cue に紐づいた GE が起動した場合に呼ばれます。 ここでは、GE が有効中であることがわかるように GE が有効中の間常にエフェクトが発生するように P_Smoke を発生させるようにしました。あとで消せるように Emitter Smoke 変数でパーティクルを保持しています。

f:id:masahiro8080:20190707134020p:plain

次に OnExecute 関数をオーバーライドし、次のように実装します。 OnExecute 関数は GE が実行されるたびに呼ばれます。今回のサンプルの場合は ダメージ処理が発生するたびに呼ばれることになります。 ここでは、GE が実行されるたびに P_Explosion を発生するようにしました。

f:id:masahiro8080:20190707134232p:plain

最後に OnRemove 関数をオーバーライドし、次のように実装します。 OnRemove 関数は GE が削除される際に呼ばれる関数です。 ここで GE が削除されるときの後処理を記述しています。

準備は以上です。最後に動作確認をしていきます。

動作確認

レベルブループリントで、キャラクターに GE を付与し、GE を実行するようにします。

f:id:masahiro8080:20190707134612p:plain

キャラクターの Health が減っているかどうかを確認するために TIck イベントを次のように実装しました。 GetHealth 関数や GetMaxHealth 関数は、今回説明しませんが、 キャラクターが持つ AttributeSet から Health と MaxHealth の値を取り出すような処理を C++ 側で記述しています。

f:id:masahiro8080:20190707134811p:plain

実行すると次のような感じです。 GE が起動すると煙が上がり、約5秒おきに爆発エフェクトが発生することがわかると思います。 また、非常に見づらいですが、左上の Debug String から Health が 50 ずつ減っていることも確認できるかと思います。

youtu.be

駆け足で説明してきましたが、このような感じです。 まだまだ GameplayEffect について調べているので、ほかに使い方がわかれば新しく記事を書くつもりです。

今回は以上です。

UE4 で自動テストを試してみる。

この記事は Unreal Engine 4 (UE4) Advent Calendar 2018 の19日目の記事です。
昨日は、@AziO さんの UE4のバグ報告をして修正してもらおう! でした。

今回は UE4 の自動テストをやってみようと思います。やり方の手順は公式のドキュメントが詳しいです。
Unreal Engine | 自動化システムの概要

他にも色々な方が記事を上げてくださっています。例えば次のようなものがあります。
[UE4] コマンドから自動テストを実行してみる|株式会社ヒストリア
[ #UE4 ]Blueprintでユニットテスト・機能テスト - Qiita

今回は自動テストの実行と作成方法を見たあとに、簡単な AI に対するテストをやってみたいと思います。

テストの実行方法

自動テストはプラグインなので、Plugin を有効にし、エディタを再起動をする必要があります。

f:id:masahiro8080:20181218004219p:plain

再起動が終わったら Window から Test Automation を選択し、Session Frontend を開きます。 Automation タブのチェックボックスにチェック入れ、Start Tests ボタンを押すとテストが開始します。

f:id:masahiro8080:20181218004328p:plain

テストの結果は Automation タブで確認できます。 緑になっていたらそのテストは成功。赤は失敗を意味します。またオレンジ色のものは、警告を表しています。

f:id:masahiro8080:20181218004350p:plain

テストの実行方法がわかったので、次に自分でテストを作成して、そのテストを実行していきたいと思います。

テストの実装方法

テストは C++ でもブループリントでも実装できますが、今回はブループリントで実装していきます。

まず、FunctionalTest クラスのブループリントを作成します。ブループリントの名前は BP_FirstTest としました。

f:id:masahiro8080:20181218004408p:plain

次に、Evnet Graph タブで次のように最初のテストを書いてみました。

f:id:masahiro8080:20181218004437p:plain

Start Test イベントノードはテスト実行時に呼ばれるイベントです。 Finish Test ノードは、テスト結果を設定します。テスト結果を Test Result ピンに、テスト終了時にログに残したいメッセージをMessage ピンに設定します。

今回作ったテストは無条件にテストが成功するテストです。
テストを作成したので、作成したテストをテスト用のマップに配置します。

f:id:masahiro8080:20181218004504p:plain

再び Session Frontend を開き、Automation タブの Project から作成したテスト (BP_FirstTest) にチェックを入れ、Start Tests を実行します。 作成したテストが表示されていない場合は Refresh Tests ボタンを押すと表示されると思います。

Start Tests を押すと、Standalone のプレビュー画面が開くと思いますが一旦閉じて、Session Frontend の画面を開き、テスト結果を見るとテストが成功していることが確認できると思います。

f:id:masahiro8080:20181218004423p:plain

ここまでで、テストの作成、実行が出来るようになったかと思います。
自動テストの基本は以上です。以下は、簡単な AI に対してテストを実践してみようと思います。

簡単な AI のテスト

今回は AI の一つの動作をテストを作成しながら、実装していきたいと思います。

作成する AI について

ここで作成する AI の処理は、次のようなものです。

  • 視界内のプレイヤーの位置まで移動する

視界の距離等を決めていない雑な仕様ですが、この処理を行う AI をテストと一緒に作っていきたいと思います。

テストの作成と実行

AI とテストの実装を行う前に、プレイヤーとテスト対象の敵のブループリントを準備します。名前はそれぞれ BP_Player、BP_Enemy としました。 中身は両方とも Character ブループリントの Skeltal Mesh に SK_Mannequin を設定したものになっています。
しかし、それだけだとマップに配置した際に、どっちがプレイヤーなのか敵なのかわかりづらくなるため、頭上にテキストで印をつけています。

下の画像はテスト対象の敵のブループリントです。同じ感じでプレイヤーのブループリントも作成しています。

f:id:masahiro8080:20181218005833p:plain

プレイヤーとテスト対象の敵を準備したので、次に新しくテストを作成していきます。 名前は BP_MoveToEnemyTest として作成します。

まず、変数からです。 マップに配置したテスト対象の敵とプレイヤーをテストで使用したいので、BP_Enemy 型の変数と BP_Player 型の変数を作成しています。

変数

変数名
TargetAI BP_Enemy
Player BP_Player

次にイベントグラフです。 テストの内容としては、テスト開始一定時間後にプレイヤー(Player)とテスト対象の敵(TargetAI)の距離がしきい値以下であるかどうかを確認するようなテストにしています。

イベントグラフ

f:id:masahiro8080:20181218005547p:plain

今回のテスト用にマップ MoveToPlayerTestMap を作成し、 BP_MoveToPlayerTest を配置します。

マップにプレイヤーとテスト対象の敵を配置します。テスト対象の敵の視界内にプレイヤーが入るように配置します。 また、BP_MoveToPlayerTest の TargetAI、Player にマップ上の BP_Enemy、BP_Player をそれぞれ設定しておきます。
今回はビヘイビアツリーの MoveTo を使用するので、Nav Mesh Bounds Volume もマップに配置しておきます。

f:id:masahiro8080:20181218011017p:plain

ここまで出来たら、一度テストを実行してみます。
もちろん、ただ単に Character をマップに配置しただけで、何も実装していないのでテストは失敗するはずです。

f:id:masahiro8080:20181218011523p:plain

期待通り、テストが失敗しました。
次にテストが成功するように AI を実装していきます。

AI の実装

ビヘイビアツリー (BT_Enemy) とブラックボード (BB_Enemy) を用意し、次のように実装しました。
ブラックボードに設定されたプレイヤーまで MoveTo で移動するような処理にしています。

ビヘイビアツリー

f:id:masahiro8080:20181218231251p:plain

ブラックボード

f:id:masahiro8080:20181218231302p:plain

次に敵用の AIController (BP_EnemyAIController) を作成し、AIPerception をコンポーネントに追加します。

f:id:masahiro8080:20181218231440p:plain

AIPerception に視界の設定をします。値については初期値のままです。

f:id:masahiro8080:20181218233420p:plain

BP_EnemyAIController のイベントグラフは次のようにしました。
BeginPlay イベントでは、作成したビヘイビアツリーとブラックボードを使用する設定をしています。 OnPerceptionUpdated イベントでは、BP_Player が視界内にいるかどうかを確認して、視界内にいればブラックボードに Player を設定しています。

f:id:masahiro8080:20181218231517p:plain f:id:masahiro8080:20181218233505p:plain

最後に BP_Enemy の AIController に BP_EnemyAIController を設定します。

テストの再実行

ここまで実装が出来たら、もう一度テストを実行してみます。

f:id:masahiro8080:20181218233309p:plain

テストの成功が確認できました。
これで敵がプレイヤーの位置まで移動することが確認できました。

もう一つだけテストを追加

ここで、視界外のプレイヤーは追わないのかどうかが気になったので、次のようなテストを作成しました。
先ほどのテストとほぼ同じですが、変数にテスト対象の敵と初期位置に配置した Target Point を設定して、 テスト対象の敵が最初の位置から動いていないことを確認するテストにしています。

f:id:masahiro8080:20181218234253p:plain

テスト用のマップにはテスト対象の敵の背後にプレイヤーを配置してみました。

f:id:masahiro8080:20181218234423p:plain

これでテストを実行してみます。

f:id:masahiro8080:20181218234522p:plain

2件ともテストが成功したので、視界内のプレイヤーまで移動するという処理はとりあえず実装できたように思います。

さいごに

今回は自動テストのやり方を簡単に見ていきました。

今回実装したようなテストは、雑すぎてテストではないような気がしますが、テストの実装方法の雰囲気がわかれば幸いです。

個人的に自動テストは好きな機能で、テストがあることである程度安心して開発していくことが出来ます。

自分はゲーム会社で働いていないのでゲームにおけるテストというものがどういうものなのかわかっていません。

ゲーム会社、特に UE4 を使ってる会社、またサークルや個人開発者の方がどういうテストをしているのかに自分は興味があるので、テストに関する情報がたくさんあると嬉しいなと思います。

また、今回の記事で自動テストに挑戦する人が増えると幸いです。

今回は以上です。

明日は @ayumax さんの Unreal.js についての記事です!楽しみ!

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 になったら、敵が倒れる実装を追加してみたやつ。 この記事では、ヒットリアクションと倒れる機能まで説明しなかったので、そのうちちゃんと説明したい。