情報は力ではない

UE4 とか Blender とか。

VEX でカスタム COP を作成してみる

VEX でカスタム COP 作れるということだったのでやってみた。自分用にまとめておきます。

※ 2020/4/25 追記 下記の「VEX」で記載している処理は自身の理解不足により誤った記載をしており、期待した結果になりませんでした。調査した後、訂正したいと思います。

基本

基本は VEX を .vfl ファイルに記述して vcc でコンパイルし HDA 化する。 今回は COP で使用できる HDA を作るので、メイン関数で指定するコンテキストは cop2 にしておく。演算の結果は RGBA 変数に格納する。

題材

今回練習として作成したのは、4つの画像からそれぞれ指定したチャンネル (RGBA) を取り出して、それぞれを異なるチャンネルに格納し、一つの画像にまとめるような HDA ファイルを作成してみました(一般にこういう処理がどういう名前で呼ばれているのか知りたい)

VEX

今回書いた VEX は以下です。

VEX でカスタム COP 作成

VEX の説明

そこまで難しいことは書いてないので簡単にしか説明は書かないですが Houdini の UI に見える部分はプラグマで設定して、処理は関数で実行という感じ。

ノードの名前とかパラメータとかはプラグマで設定しています。各プラグマの細かい説明は公式ドキュメントがそこそこ詳しいので今回は省きます。今回の題材は4つの画像が必要なので、opmaxinputs でインプットの数を4つに設定しています。choice を使用することで Houdini の UI でパラメータをリストから選択できるようにしています。

sample 関数はインプットの (IX、IY) から指定したチャンネルの値を取り出す関数です。useChannel の 0 ~ 3 がそれぞれ RGBA に対応しています。IX、IY はピクセルの位置を表すグローバル変数です。

sample_merge 関数がメイン関数でコンテキストに cop2 を指定しています。 インプットの4つの画像から指定したチャンネルの値を取り出して、それぞれ RGBA に値を格納しているだけです。

コンパイル

これをコンパイルして HDA にします。

vcc -l sample_merge.hda sample_merge.vfl

作成した HDA をインポートしたら HDA として使えます。下図は使用している図で、4つの画像の B チャンネルを1つの画像にまとめようとしているところです。

f:id:masahiro8080:20200418163605p:plain

今回は簡単ですが、これで終わり。

参考

Packaging についてのメモ

先日、第 13 回 UE4 ぷちコンに作品を応募しました。

historia.co.jp

6回ほど参加していて、今回初めて応募作品を公開しました。itch.io で公開しています。

https://0xuma.itch.io/butterfly-bloosom

初めてちゃんと Packaging したので、その際に実施したことを自分用のメモとして記載しておきます。間違いやこうしたほうがよい等があれば知りたいです。

基本的には以下を参照しました。

プロジェクトのパッケージ化 | Unreal Engine ドキュメント How to package the right way - reduce .pak size - Unreal Tournament Forums

Packaging の実施について

Packaging の実施自体は簡単で File > Package Project でどのプラットフォーム用に Packaging するかを選択します。画像は Windows 64-bit を選択している場面です。 f:id:masahiro8080:20200405153637p:plain

パッケージサイズの削減について

Packaging 自体は簡単にできるのですが、最初に作成したパッケージのサイズが 3 GiB 弱あり、配布するには大きすぎるので、パッケージサイズを削減していくことにしました(最終的には 700 MiB 程になりましたが、それでも大きい...)。

Project Settings での設定について

Project Settings で Project > Packaging を選択し、パッケージに関する設定を行います。

Project カテゴリ

  • Build Configuration は Shipping にしておく
  • Packaging 実施ごとにビルドを走らせるために Full Rebuild をチェックしておく
  • Include Debug Files にチェックが入ってないことを確認する

f:id:masahiro8080:20200405134933p:plain

Packaging カテゴリ

  • Use Pak File にチェックしてることを確認する

f:id:masahiro8080:20200405135354p:plain

Packaging カテゴリの▼を押下して、設定を開いて、さらに設定していく。

  • 指定したレベルのみ Packaging するために Cook only maps にチェックする。Packaging 対象のマップは Lists of maps to include in a packaged build で指定する
  • パッケージサイズを減らすために Create compressed cooked package にもチェックしておく
  • 削減の作業とは違うが、アセット以外のテキストファイル等を package 対象にしたい場合は Additional Non-Asset Directories to Package でディレクトリを指定する

f:id:masahiro8080:20200405135510p:plain

アセット関連

今回の場合、基本的にテクスチャが容量を食っていたので、テクスチャサイズの削減を実施しました。

テクスチャグループの設定

<Project Directory>/Config に DefaultDeviceProfiles.ini を作成し、テクスチャグループの設定を上書きする。今回はマーケットプレイスのテクスチャで 4K のものが多かったので World、World Normal Map の LODBias の設定を 1 に設定し、1段階テクスチャのサイズを下げました。その上でテクスチャアセット側の設定でよりサイズを下げていくという方法にしました。

[/Script/Engine.TextureLODSettings]
@TextureLODGroups=Group
+TextureLODGroups=(Group=TEXTUREGROUP_World,MinLODSize=1,MaxLODSize=8192,LODBias=1,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WorldNormalMap,MinLODSize=1,MaxLODSize=8192,LODBias=1,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)

テクスチャアセットの設定

Maximum Texture Size で最大サイズを設定します。 f:id:masahiro8080:20200405140849p:plain

ゲーム内で使用されるテクスチャのサイズは Details タブ上部の Max In-Game でわかります。Resource Size はおそらく pak 内でのサイズだと思います。 f:id:masahiro8080:20200405150318p:plain

ライトマップ関連

ライトマップの容量の減らし方はよくわかってないけど以下を実施しました。

  • Static Mesh 等の Light Map Resolution を調整する
  • World Settings で Packed Light and Shadow Map Texture Size を調整する(パッケージサイズの削減とは違う気がする)

その他

コンテンツブラウザでアセットを右クリック > Size Map... で、選択したアセットの容量を確認できます。画像はレベルの Size Map です。 f:id:masahiro8080:20200405151508p:plain

プラグイン関連

不要なプラグインは Enabled にチェックを入れないようにする

最後に

<project directory>/Saved/Cooked の下に pak に含まれる Cook 後の各アセットが配置されるので各アセットサイズを見ることができる。ls -l とかで容量を食ってるアセットを見つける。

GameplayEffect の Stacking と Overflow を使ってみる

この記事は、Unreal Engine 4 (UE4) Advent Calendar 2019 - Qiita 15 日目の記事です。

qiita.com

前日は @ka-s さんの「【UE4】ランドスケープをスタティックメッシュに変換する方法とハマったところの紹介 - Qiita」でした。ランドスケープもスタティックメッシュに変換できるんですね。知らなかったです。

今回は、Gameplay Ability System の要素の一つである GameplayEffect の Stacking と Overflow を使用して何か作っていきたいと思います。

前提

今回は UE4.23.1 で確認しています。 また AttributeSet には Health という Attribute が定義されている前提で話を進めます。

GameplayEffect とは

GameplayEffect は Gameplay Ability System の要素の一つで Attribute を変更するために使用するクラスです。

Gameplay Ability System については、おかずさんの ActionRPG についての資料がわかりやすいかと思います。

実際に試してみたい方はおかわりはくまいさんの記事を参考にするとよいと思います。

okawari-hakumai.hatenablog.com

Stacking と Overflow

GameplayEffect のパラメータには Stacking と Overflow というセクションがあります。
Stacking は同じ GameplayEffect のスタックに関する設定ができます。Overflow は GameplayEffect のスタックが上限に到達した際の設定ができます。

今回はこの機能を用いて、1度目の攻撃はエフェクトが生じるだけでダメージはないが、2度目の攻撃はダメージを受けるというような機能を作成していきたいと思います。

下の動画は完成イメージです。1度目はグレイマンの周りにエフェクトが発生するだけですが、2度目はダメージを受けるようになっています(敵の攻撃を作るのをサボったため、Q キーの押下で敵からの攻撃とみなしています。。

GameplayEffect の作成

今回は GameplayEffect を2つ作成します。1つは Stack させるための GameplayEffect。もう1つは Overflow した際に発動する GameplayEffect です。 まず作るのが簡単な Overflow した際に発動する GameplayEffect を作成していきます。

Content Browser で右クリックし Blueprint Class を選択し GameplayEffect を作成します。今回は GE_Frostbyte としておきました。

f:id:masahiro8080:20191208162306p:plain
GameplayEffect の作成

作成した GE_Frostbyte を開き Modifires セクションを設定していきます。
この GameplayEffect が発動したら対象にダメージを与えるように設定します。Modifiers セクションから Attribute に Health を設定し、Scalable Float Magnitude でダメージ量を設定します。
GE_Frostbyte はこれで終わりです。

f:id:masahiro8080:20191208162701p:plain
Modifiers セクションの設定

次に Stack する方の GameplayEffect を作成していきます。
同様に GameplayEffect を作成します。名前は GE_StackingFrostbyte にしておきます。作成した GE_StackingFrostbyte のパラメータを設定していきます。
Overflow セクションには Overflow Effects として先ほど作成した GE_Frostbyte を追加します。この Overflow セクションは Stack の最大量を超えた際に発動する GamepleyEffect を設定します。Stack の最大量を超えた際には再び Stack を空にしておきたいので Deny Overflow Application、Clear Stack on Overflow ともにチェックを入れます。

次に Display セクションですがここでは GameplayEffect が発動した際のエフェクトに関わる設定をします。後で氷っぽいエフェクトを追加したいのでそのための設定として Gameplay Cues を一つ追加し Gameplay Cue Tags に Effect.Frostbyte を追加します。追加するには Edit から Add New Gameplay Tag から追加します。

f:id:masahiro8080:20191208164628p:plain
Overflow セクションと Display セクション

次は Stacking セクションです。Stacking セクションでは Stack に関する設定を行っていきます。
Stacking Type は Aggregate by Source に設定します(この設定は Ability System Component が持つこの GameplayEffect のスタックカウントの追加の仕方を設定する項目だと思っているのですが、Aggregate by Target との違いが、正直なところよくわかっていません(すみません))。Stack Limit Count を 1 に設定しました。Stack Limit Count は Stack に積める最大個数です。この設定によりこの GameplayEffect が 2 つ Stack されると Overflow を起こします。

最後に Gameplay Effect セクションです。ここではこの GameplayEffect の期間に対する設定を行います。GameplayEffect の Stack 機能を使用するには、Duration Policy を Has Duration か Infinite に設定する必要があります。ここでは Duration Policy を Has Duration に設定し、Scalable Float Magnitute を 10.0 に設定しました。これによりこの GameplayEffect は 10 秒間 Stack に残ることになります。なので 10 秒間の間に敵の攻撃を受けてしまうとダメージということになります。10 秒を過ぎると Stack から削除されます。

f:id:masahiro8080:20191208165215p:plain
Stacking セクションと Gameplay Effect セクション

GE_StackingFrostbyte の設定は以上です。

次に先ほど Display セクションで設定したエフェクトに関する設定を行っていきます。

GameplayCue の作成

GameplayEffect が発動した際や削除された際の GameplayCue はエフェクトやサウンドに関する処理を司るクラスです。

Content Browser から Blueprint Class を選択し GameplayCueNotify_Actor を作成します。名前を GCN_Frostbyte としておきます。

f:id:masahiro8080:20191208165826p:plain
GameplayCueNotify_Actor の作成

GCN_Frostbyte を開き処理を記述していきます。 まず Functions の Override から On Active を選択し次のような処理を記載します。 My Target から Mesh を取り出し Mesh に Emitter を Spawn しています。Emitter Template には InfinityBlade のアセットで氷属性っぽいエフェクトを使用しています。Spawn Emitter Attached の戻り値を保持しているのは GameplayEffect が削除されたときに Emitter を削除するためです。

f:id:masahiro8080:20191208170055p:plain
On Active 関数

次に On Remove の処理を記述していきます。 GameplayEffect が削除された際に Emitter を消すだけの処理を記載しています。

f:id:masahiro8080:20191208170400p:plain
On Remove 関数

最後に Class Defaults の設定を行っていきます。 変更したのは Auto Destroy on Remove と Gameplay Cue Tag の2つです。 GameplayEffect が削除された際にこの GameplayCue も削除してほしいので Auto Destroy on Remove にチェックを入れています。Gameplay Cue Tag には Effect.Frostbyte を設定しています。これは GE_StackingFrostbyte の Display セクションで設定した値です。これを設定することにより GE_StackingFrostbyte が発動した際に GCN_Frostbyte も発動することになります。

f:id:masahiro8080:20191208170552p:plain
Class Defaults

以上ですべての設定は終わりです。

確認のためにレベルブループリントでプレイヤーに GE_StacingFrostbyte を Stack させる処理を記述していきます。 Player Pawn から Ability System Component を取得し ApplyGameplayEffectSpecToSelf で自分自身に GE_StackingFrostbyte を適用しています。

f:id:masahiro8080:20191208171014p:plain
GE_StackingFrostbyte の動作確認

以上により、この記事の最初に表示した動画のようになります。

おわりに

駆け足でしたが Stacking と Overflow の使い方の紹介でした。これを機に GameplayAbilitySystem に興味を持つ人が増えればよいなと思います。

以上です。

明日はがっちょ( ¨̮ )さんの「地形で遊ぼう!」です。地形の内容ということで楽しみです!

参考

Gameplay Attributes and Gameplay Effects | Unreal Engine Documentation GitHub - Pantong51/GASContent: Repo to gather all Gameplay Ability System content for UE4 GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 4's GameplayAbilitySystem plugin with a simple multiplayer sample project. UE4 GameplayAbility Pluginについてのメモ - Qiita

Procedural Foliage について

この記事は Unreal Engine 4 (UE4) #2 Advent Calendar 2019 の 9日目の記事です。

qiita.com

前日は、なんさんアーティストのためのマテリアル式にゅうもんでした。マテリアルで使用するベクターや Multiply、Add の意味がわかりやすく丁寧に書かれていて良い記事でした!

今回は Procedural Foliage について簡単に紹介しようと思います。 公式のクイックスタートもあるのでそちらも参照してみてください。

docs.unrealengine.com

この記事で使用している UE4 のバージョンは古いですが、4.22 です。
内容は次のような感じで進めていきます。

Procedural Foliage とは

設定したパラメータに基づいてプロシージャルに Static Mesh を配置できる機能です。
Modes パネルから選択できる Foliage ツールは手作業でレベルに Static Mesh を配置できる機能ですが、Procedural Foliage はボタン一つでプロシージャルに Static Mesh を配置できるので、サクッと森などの環境を作りたい場合に使用できるかと思います。

以下では Procedural Foliage の使い方を説明しながら森を作っていきたいと思います。

f:id:masahiro8080:20191207145817p:plain
今回作成した森

Procedural Foliage を使用する準備

Procedural Foliage は Experimental の機能なので Editor Preferences から Procedural Foliage を有効にする必要があります。
Editor Preferences を開き General から Experimental を選択し Procedural Foliage にチェックボックスにチェックを入れます。 Editor Preferences は Edit タブから開きます。

f:id:masahiro8080:20191130125026p:plain
Editor Preferences で Procedural Foliage を有効にする

以上で Procedural Foliage が使用可能になります。

Procedural Foliage 機能を使用した森の作成

Foliage Type の作成

まずはじめに Foliage ツールと同様に Foliage Type を作成します。Foliage Type は Foliage 機能で配置する Static Mesh に関する設定を行うクラスです。
Content Browser で右クリックし Miscellaneous から Foliage Type を選択します。森を作るために木の Static Mesh を配置したいので、名前は FT_Tree01 としておきます。

f:id:masahiro8080:20191130144132p:plain
Foliage Type の作成

作成した Foliage Type を開きます。一番上にある Mesh の項目に Procedural Foliage で配置したい Static Mesh を設定します。ここでは木の Static Mesh を設定しておきます。

f:id:masahiro8080:20191130183300p:plain
Mesh の項目に木の Static Mesh を設定

以上で Foliage Type に関する最低限の設定は終わりです。
他の項目は配置する Static Mesh の密度等の項目なのですが、先に Static Mesh の配置をし、その後で項目を設定していきたいと思います。

Procedural Foliage Spawner の作成

次は Procedural Foliage Spawner を作成します。Procedural Foliage Spawner は、設定したパラメータに基づいてプロシージャルに StaticMesh を配置してくれるクラスです。
Content Browser で右クリックし Miscellaneous から Procedural Foliage Spawner を選択します。森を作りたいので、名前は PFS_Forest としておきます。

f:id:masahiro8080:20191130140142p:plain
Procedural Foliage Spawner の作成

作成した Procedural Foliage Spawner を開きます。Foliage Types の + ボタンを押下し Foliage Type Object に先ほど作成した FT_Tree01 を設定します。Foliage Types は Procedural Foliage Spawner で配置する Static Mesh に関する設定です。

f:id:masahiro8080:20191130183551p:plain
Foliage Type Object に FT_Tree01 を設定

以上で Procedural Foliage Spawner の設定は終わりです。
他のパラメータについては公式リファレンス を参照してください。

Procedural Foliage による Static Mesh の配置

先ほど作成した Procedural Foliage Spawner をレベルに配置し、Static Mesh を配置します。

ちゃっと広いレベルを作成したかったのでランドスケープを使用しています。

f:id:masahiro8080:20191130183946p:plain
Landscape モードでランドスケープを作成

PFS_Forest をレベルに配置します。そのままでは小さいのでスケールを変更して大きな領域にしておきます。

f:id:masahiro8080:20191130184849p:plain
レベルに PFS_Forest を配置する

Static Mesh の配置には、PFS_Forest の Detail タブ内 Procedural Foliage カテゴリにある Resimulate ボタンを押下します。

f:id:masahiro8080:20191130185044p:plain
PFS_Forest の Procedural Foliage カテゴリ

すると Procedural Foliage Spawner の Foliage Types に設定した Static Mesh がレベル上に配置されます。

f:id:masahiro8080:20191130185204p:plain
Resimulate した結果

以上で Foliage Type に設定した Static Mesh を配置し、森を作成することが出来ました。

基本的な使い方は以上です。あとは求める背景に近づけるために、パラメータの調整や新しく Foliage Type を作成していきます。

Foliage Type の調整

配置した木の密集具合や木の大きさ等の調整は Foliage Type で行います。
Foliage Type のパラメータを変えて Procedural Foliage Spawner で Resimulate を繰り返し、求める背景に近づけていきます。

ここでは今回設定したパラメータの一部をざっくりと説明していきます。各パラメータに関しては Procedural Foliage Spawner 同様、公式リファレンス を参照してください。

Placement セクション

Placement セクションでは配置に関するパラメータを設定します。
垂直位置のばらつきや地面に対して垂直に Static Mesh を配置するか、ピッチの角度のばらつきを設定できます。今回は Z Offset で少し垂直位置が下がっている木があるようにしてみたり Random Pitch Angle で少し傾いた木があるように設定しました。

f:id:masahiro8080:20191207155626p:plain
Placement セクション

Collision セクション

Collision セクションでは配置する Foliage Type 同士の重なりに関するパラメータを設定します。
今から配置しようとしている Foliage Type の半径内と他の Foliage Type の半径内が重なった際、ルールや優先度によりどちらかが削除されることになりますが、その半径をここでは設定できます。半径は Collision Radius と Shade Radius の2つを設定できます。今回は Collision Radius を少し変更している程度であまり意味があって変更したわけではないです。

f:id:masahiro8080:20191207160241p:plain
Collision セクション

Clustering セクション

Clustering セクションでは密度や年齢に関するパラメータを設定します。
何世代まで配置するかや配置しようとする Static Mesh の密度、そのばらつきを設定できます。Num Steps は 0 だと最初の世代のみが配置されます。Initial Seed Density は種の密度を設定します。密にしたい場合は大きめの値を設定します。Spread Variance は種のばらつきを設定します。今回は、Num Steps を 5 に設定し、6世代まで配置できるようにしています。また Spread Variance を少し大きめに設定しています。

f:id:masahiro8080:20191207164817p:plain
Clustering セクション

Growth セクション

Growth セクションは年齢による成長に関するパラメータを設定します。
Collision セクション で設定した Shade Radius を無視できるようにしたり、最大の年齢を設定できます。Can Grow in Shade と Spawns in Shade をともに true にすることにより Shade Radius を無視できます。今回は、Max Age や Procedural Scale を変更してみています。

f:id:masahiro8080:20191207170259p:plain
Growth セクション

Instance Settings セクション

Instance Settings セクションでは配置された Static Mesh に関するパラメータを設定します。
Mesh を配置する際のおなじみの設定がほとんどかと思います。今回は Collision Presets を No Collision にしていますが、コリジョンが必要な場合はここで適切なコリジョンに設定します。

f:id:masahiro8080:20191207170730p:plain
Instance Settings セクション

複数の Foliage Type の設定と配置

木の Foliage Type を調整した結果、このような感じになりました(途中で Exponential Height Fog を配置しました)。

f:id:masahiro8080:20191207185605p:plain
途中結果

今は木だけですので、地面に草や石も生やしてみます。

今回は草用と石用の Procedural Foliage Spawner を新しく作成しました。草や石の Foliage Type を作成し、それぞれに Procedural Foliage Spawner に設定しています。これらも木の場合と同様にレベルに配置し、Resimulate します。

f:id:masahiro8080:20191207185150p:plain
草の Procedural Foliage Spawner

もしかしたら、一つの Procedural Foliage Spawner に木や草、石をまとめてもよいのかもしれないのですが納得のいく配置が出来なかったので今回は Procedural Foliage Spawner を複数に分けています。

Static Mesh の配置の抑制

ここまでで次のような感じになりました。
ここで画像の手前から真ん中の辺りに向かって道のようにしたいと思いました。その部分に配置されている花や草が邪魔なので、その部分には配置されないように設定します。

f:id:masahiro8080:20191207171800p:plain
ここまでで作成した森

Procedural Foliage Spawner を配置した範囲の中で Static Mesh を配置したくないという領域がある場合には Procedural Foliage Blocking Volume を使用します。Modes パネルで Procedural Foliage Blocking Volume を検索し Procedural Foliage Spawner の中で Static Mesh を配置したくない場所に配置します。

f:id:masahiro8080:20191201140706p:plain
Procedural Foliage Blocking Volume の配置

あとは Procedural Foliage Spawner 側で Resimulate を押下するだけです。押下すると Procedural Foliage Blocking Volume を配置した部分は Static Mesh が配置されなくなります。

f:id:masahiro8080:20191201140946p:plain
Procedural Foliage Blocking Volume 配置後の Resimulate 結果

この機能を使用し、道のような部分を作成しました。

f:id:masahiro8080:20191207172432p:plain
Procedural Foliage Blocking Volume を配置し、Resimulate

どことなく物足りなさを感じたので、倒れた木を一つ配置して(これは手作業)完成としました。

f:id:masahiro8080:20191207145817p:plain
完成

おわりに

以上で Procedural Foliage についての説明は終わりになります。
Procedural Foliage は、まだ Experimental の機能ということで今後どうなっていくのか楽しみです。 ここまでお読みいただいてありがとうございました。

明日は、@suihanki さんの「VRネタで何か一発」です。

参考

How To Create And Use Procedural Foliage Volumes - UE4 Tutorial Open World ツールのプロパティ リファレンス

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 についての記事です!楽しみ!