PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE4 Voiceer4Uを公開しました

環境

UE4.25.4

概要

Voiceer4UというPluginを公開しました。
github.com

Voiceerとは
ねぎぽよ氏が公開しているUnity向けのEditor拡張で
様々なEditor操作を行う度にかわいい声で開発を応援してくれるものです。
github.com

Voiceer4Uとは
Voiceerがいいなと思ったので、UE4版を作ってみました。
(Voiceer4Uという命名を使用してよいか否かはねぎぽよ氏に確認を取りました)
現状まだまだ機能不足で本家Voiceerにはまだまだ及ばず
Compile/Package化/Launchの開始/成功/失敗のみしか対応していませんが
かわいい声で開発を応援してくれるUE4向けのPluginです。

Demo動画 :


Compile関連の対応なので、Blueprint Onlyの開発者の方々には恩恵が少ないため
その辺りは今後のアップデートで対応できればと考えております。

使い方についてはGithubの方を見ていただければです。

現状の問題点

一日でガガッと作ったものなので、verion 0.1.0でまだまだ機能不足と実装上の問題を抱えています。
1. Engine側のAssetに変更が入ってしまう
Module初期化時に実行してEngine側のAssetに対して手を入れており
完全な元データの復元というのが行えていないため
Sound Cue内のNodeの移動等で差分が発生してしまいます

2. Pluginを導入したProjectと同一のVersionのUE4 Editorを開くと問題が起こりうる
A. Pluginを導入したProject
B. Pluginを導入していない同一Engine VersionのProject
Engine側のAssetに変更が入っているためA. を開いた状態でB. を開いて作業をしている場合に
A. のProjectに変更が入り、アクセス中なので正しく保存が行われないといったケースが発生してしまいます

今後のアップデートでやりたいこと

1. PIE時等のEditor操作時の音声に対応
2. 先に述べた問題点への対応の検討

余力があったらやりたいこと
1. Folder名 / File名の規約の緩和
現在Folder名のみならずFile名も規定のものにする必要があり、この辺が不便です
使う側のユーザーがこの辺りを気にすることなくできればいいなと検討中

2. 設定用のGUIの用意
現状はContent Browserと簡素なProject Settingsの設定オプションしかないため
設定しやすく変更が行いやすいように調整していければと思います

3. Editorの再起動なしでも変更可能にする
これは単純にGUIを用意してAPIを呼び出すだけだと思いますので
難しくはないんじゃないかと思いつつ、単純にめんどくさいというのが大きいですね

おまけ

実装をしていて面白かった点
1. アセット保存用のAPI
以下の関数を叩くだけで変更したAssetの内容を保存することができました。
他にもDialog有りのバージョンも用意されていたので、Editor拡張なんかでも使い勝手が良さそうだと感じました。

bool UEditorLoadingAndSavingUtils::SavePackages(const TArray<UPackage*>& PackagesToSave, bool bOnlyDirty)

2. Editor終了時のCallback関数
以下のDelegateを使用するだけでEditor終了時のCallback関数を登録できました。
現状はこのAPIを使用してEditor終了時にEngine標準の音データに初期化しています。

FSimpleMulticastDelegate FEditorDelegates::OnShutdownPostPackagesSaved

3. USoundNode, UEdGraphNodeの利用
通常はAnimationやSoundでも自作NodeをC++で定義して呼び出すことはよくあると思いますが
今回はC++側からNodeを操作するという少し珍しい実装をすることになりました。
というのも、Moduleの開始時に既存Assetに手を加えているためEditorが開く前段階で処理をしているからです。


上記3点を含めて、具体的な実装についてはPlugin側のCodeを見ていただければですが
なんとなくで作り始めたわりに学びが多くいい経験になりました
少しEngineに慣れてきた方にはPlugin開発はオススメです。

UE4 KeyConfigの作成、Keyの変更/追加/削除について

環境

UE4.25.3
Windows 10 Pro

概要

以前にこんな記事を書いてKeyConfig用のサンプルを添えたPluginを作成しました。
papersloth.hatenablog.com

ですが、エンジンの標準機能でBlueprintのみでもKeyの再設定/追加/削除を行う方法があるため、今回はそちらを紹介します。

Action/Axis Mappingsの簡単なおさらい

まずはProjectSettingsのAction Mappings/Axis Mappingsについての簡単なおさらい。
「Edit -> Project Settings」からProject Setteingsを開きます。
f:id:PaperSloth:20201011170853p:plain

その中の
「Engine/Input」の中で各種Action/AxisのMappingを行うことができます。
Action名/Axis名に対してMouse / Keyboard /Gamepad等のボタンやスティック等を割り当てることで
簡単にゲームの入力に対する操作の定義が可能です。
f:id:PaperSloth:20201011171004p:plain

これらはProject Settingsで登録しておき、ゲーム中での変更方法が分からずKey Configの作成で苦労されたという方も多いのではないかと思います。
これらを簡単にゲーム中で変更可能なのが今回紹介するInput Settingsの各ノードになります。

Input Settingsについて

UE4の標準のノードでKey関連の変更を行う際は「UInputSettings」classを使用します。
このクラスはUObject継承のため、Level上に配置することはできません。

Blueprintで取得するには「Get Input Settings」ノードを使用します。
f:id:PaperSloth:20201011170015p:plain

Input Settingsに対してBlueprintから呼び出し可能なノードは以下になります。
f:id:PaperSloth:20201011170126p:plain

名前から察するにProject SettingsのInputで設定した
Action Mappings/Axis Mappingsに対して何かしらの操作が行えそうですね。

もう少し詳細を見ていきましょう。

Input Settingsからアクセス可能なノードの基本的な使い方について

まずはAction Mappingsに対して操作可能なノードが以下になります。
f:id:PaperSloth:20201011172919p:plain

続いてAxis Mappingsに対して操作可能なノードが以下になります。
f:id:PaperSloth:20201011173038p:plain

コメントでも簡単な説明をしてありますが、Actionに対してもAxisに対しても同じようなノードで操作することが可能ですね。
Add Action/Axis Mappings, Remove Action/Axis Mappingsについての説明をしていきますが
その前に引数の「Key Mappings」についての説明を行います。
Actionの場合は「Input ActionKeyMappings」構造体を使用し
Axisの場合は「InputAxisKeyMappings」構造体を使用します。

まずはActionの方から解説していきます。
構造体を分割するかMakeして構造体を引数として設定可能な形式に変換/作成します。
f:id:PaperSloth:20201011174724p:plain

構造体の中で重要になってくるのが、「Action Name」と「Key」になります。
f:id:PaperSloth:20201011174756p:plain

Actionとして登録/削除/変更したい名前と、そのActionに対するKeyを設定する部分になります。
例えば「Jump」に対して「Space Key」を割り当てたい場合は以下のようになります。
f:id:PaperSloth:20201011175012p:plain

構造体のメンバ変数として4つのbooleanがありますが、これらはそれぞれ
Shift + KeyやCtrl + KeyやAlt + KeyやCmd + Keyの設定になります。
使うことはほぼないでしょうが、「Shift + Ctrl + A」のような複数のKeyを押す操作にも対応しています。

そして、これらの操作をEditor上で実行すると即時Project Settingsにも反映されるようになっています。


続いてAxis側の解説になります。
こちらは先程よりもう少しシンプルな構造体になっています。
f:id:PaperSloth:20201011180401p:plain
Axis名とScale値とKeyの3つを登録するだけです。

例えば前進の移動にGamepadの左スティックのY軸を割り当てたい場合は以下のようになります。
f:id:PaperSloth:20201011180534p:plain

こちらもAction同様にEditor上で実行するとProject Settingsに即時反映されます。


最後にAction / Axis双方で共通する引数の「Force Rebuild Keymaps」フラグについてです。
こちらを有効化している場合は変更を行った際にゲーム内で即時反映されますが
フラグを折っている場合にはProject Settingsには登録されますが、入力はまだ反映されません。
内部的な話をするとこちらが有効になっている場合はInput Settingsの内容が「UPlayerInput」に反映されるようになっています。

Key Config画面で変更中は未反映のまま登録を行い、最後に一括で反映する等でよいと思います。
そういった場合に使用するのが「Force Rebuild Keymaps」ノードになります。
f:id:PaperSloth:20201011181027p:plain


最後にもう1つだけ紹介していないノードがあります。
「Save Key Mappings」というノードです。
f:id:PaperSloth:20201011181211p:plain

コメントに書いた内容そのままですが、(ProjectRoot)/Saved/Config/Windows/Input.ini に更新内容を上書きします。
Editorで実行している際にはあまり意識することがないかもしれませんが
実際にPackage化してゲーム公開後にはこのノードを使用することを勧めます。
これでKey Configで保存した内容が次回ゲーム起動時にも反映されるようになります。

Package化した後のGame中で使用した場合にはこの変更内容は
(PackageRoot)/Saved/Config/WindowsNoEditor/Input.ini に保存されるようになります。

まとめ

これでKey Config作成に必要な一通りのノードが紹介できたかと思います。
実際にKey Configを作成する際にはGamepadとKeyboardの割当を分けるなど色々と必要な機能があるかと思います。

ある程度は最初に紹介したPluginの組み方を参考に回避できる部分もあるかもしれません。
最低限あった方が良いサンプルとして載せておくと、Keyの構造体からは
入力内容がGamepadかKeyboardかMouseかを判別するノードがありますので、実装の際にはこちらを使用する等して処理を分けるとよいでしょう。
f:id:PaperSloth:20201011181726p:plain

他にもMarketplaceで公開されているアセットを探して購入してみてもいいかもしれませんね。
最後にInput Settingsから呼び出し可能な全ノードを公開しておきます。
f:id:PaperSloth:20201011181825p:plain

以上です!

UE4 NiagaraのLifetimeを無制限にする方法

環境

UE4.25.3

結論

説明が長くなってしまったので、最初に結論を書いておきます。
1.
「Particle State」Moduleの「Kill Particles When Lifetime Has Elapsed」フラグをOffにして
同Moduleの「Loop Particles Lifetime」にチェックを入れるだけです。

2.
「Particle State」Moduleの「Kill Particles When Lifetime Has Elapsed」フラグをOffにして
Lifetimeに「Normalized LoopAge」を割り当てる

3. 「Initialize Particle」と「Particle State」を使用しない
f:id:PaperSloth:20201008210315p:plain

概要

Niagara Emitterを作成した際にLifetime無制限のEmitter作成方法が分からず、困ったので調べてみました。
まずはCascadeのおさらいから。
CascadeではLifetimeを使用しないだけで対応が可能でした。
f:id:PaperSloth:20201008190855p:plain

では、Niagaraではどうなるかというと
Initialize Particle Moduleで「Lifetime」のチェックを外しても削除されてしまいます。

BaseとなるNiagara Emitterの作成

今回はemitterのTemplateとして「Fountain」を使用して作成していきます。
f:id:PaperSloth:20201008191257p:plain

名前は「NS_Inifite」としておきました。
まずは一度だけ生成されればよいため、「SpawnRate」を無効化して
「Spawn Burst Instantaneous」を追加して
「Spawn Count」を「1」に設定します。
f:id:PaperSloth:20201008191550p:plain

続いて、「Emitter State」を選択して
「Loop Behaviour」を「Once」に変更して
「Loop Duration Mode」を「Infinite」に変更します。
f:id:PaperSloth:20201008191532p:plain

また、確認のために移動は不要ですので
「Add Velocity in Cone」と「Gravity Force」を無効にします。
f:id:PaperSloth:20201008191752p:plain

次にLifetimeの設定が不要ですので
「Initialize Particle」の「Lifetime」のチェックを外して無効化します。
f:id:PaperSloth:20201008191900p:plain

このままではLifetimeが無制限であっても透明度が更新されて見えなくなりますので
「Scale Color」も無効にしておきます。
f:id:PaperSloth:20201008192237p:plain

Cascade風にいけば、ここまでの設定でLifetime無制限で消えないエフェクトが作成できそうなんですがこのままではまだ消えてしまいます。

ここからLifetimeを無制限にするための方法を3通り紹介していきます。

Niagara でのLifetime無制限の設定方法

まず、設定を変更する前にLevel配置 or SpawnするためにNiagara Systemを作成しておきましょう。
「NS_Inifnite」を選択して右クリックして「Create Niagara System」を選択してください。
作成されたNiagara Systemは「NE_Infinite」にrenameしました。
f:id:PaperSloth:20201008194716p:plain

0. よくある間違い

まずはよくある間違いから。
Lifetimeに大きな値を設定するというものです。
もちろん、これで済むケースもあるかもしれませんが、あまりオススメはできませんね。
結局のところこの設定時間を経過した場合はParticle が消えてしまいますね。
f:id:PaperSloth:20201008194928p:plain

1. Loop Particles Lifetimeを使用する

ここからようやくLifetimeを無制限にする項目になります。
まず「Particle State」を選択して
「Kill Particles When Lifetime Has Elapsed」のフラグを無効にします。
f:id:PaperSloth:20201008195202p:plain

続いて同Module内の
「Loop Particles Lifetime」にチェックを入れます。
これでLifetimeがLoopしてParticle が消えることがなくなります。
f:id:PaperSloth:20201008195323p:plain

動かないのもあれなので、適当に動きを与えました。
これで消えることはないです。
f:id:PaperSloth:20201008205210g:plain

2. NormalizedLoopAgeを使用する

次の方法はNormalizedLoopAgeを使用する方法です。

「Initialize Particle」を選択して「Lifetime」に「NormalizedLoopAge」を指定します。
f:id:PaperSloth:20201008205501p:plain

これだけではまだ不完全で、先程同様に
「Particle State」の「Kill Particles When Lifetime Has Elapsed」のフラグを無効にします。
f:id:PaperSloth:20201008205706p:plain

設定はこの2つだけです。
f:id:PaperSloth:20201008205937g:plain

3. Particle Stateを使用しない

1, 2の設定ではParticle StateのKill Particles...フラグを無効することで対応してきました。
3つ目の方法ではParticle State自体を使用しないというものです。

ひとまず「Particle State」を無効化します。
すると下記のように「Initialize Particle」でエラーが出てしまいます。
f:id:PaperSloth:20201008210214p:plain

エラーを解決するにはParticle Stateが必要ということですね。
ですが、今回は「Initialize Particle」自体も無効化してしまいましょう。
f:id:PaperSloth:20201008210506p:plain

さて、これでエラーはなくなって
Lifetimeの制限もなくなりました。

ですが、これではInitialize Particleで設定していたColorやSprite Size等の情報も無効化されてしまいます。

そのため、「Set new or existing parameter directly」を使用して各Parameterを初期化していきます。
まずは「Particle Spawn」に「Set new or existing parameter directly」を追加します。
f:id:PaperSloth:20201008210720p:plain

f:id:PaperSloth:20201008210802p:plain

これで各Parameterの初期化が行えるようになりました。
「Set Parameters」を選択して右上の「+」アイコンから初期化するParameterを追加していきます。
まずは「Color」を追加しました。
f:id:PaperSloth:20201008211012p:plain

これで初期色情報を設定することができるようになりました。
f:id:PaperSloth:20201008211108p:plain

次にSprite Sizeを設定したいので
「Sprite Size」を追加します。
f:id:PaperSloth:20201008211149p:plain

とりあえず10 x 10程度にしておきました。
f:id:PaperSloth:20201008211237p:plain

このように「Set Parameters」Moduleでは複数のParameterの設定を行うことが可能です。
f:id:PaperSloth:20201008211341p:plain

回転は今回関係ないため、Sprite Rotationは未設定です。

以上で3つ目のLifetime設定方法とSet Parameterの設定紹介が終わりです。
f:id:PaperSloth:20201008211534g:plain

まとめ

Lifetimeを無制限にする方法の設定方法については以上になります。
ついでで紹介したSet Parameterですが、こちらも何気に便利なModuleで結構重宝しています。

実運用する上では極力Levelに配置するのは避けて
必要なタイミングでSpawnして不要になったらDestroyするのがベストではあると思います。

調べてみるとLifetimeを大きな値にしよう!という紹介が多かったため、調べてみました。
何かしら役に立てば幸いです。

以上!よきNiagaraライフを!
f:id:PaperSloth:20201008212052g:plain

UE4 Chaos Niagaraを使ってみよう

環境

UE4.26.0 Preview 1
※まだPreviewのエンジンバージョンですので実プロダクトでの採用は非推奨です。
また、今回の記事では作成中に何度かエンジンがクラッシュすることがありました。
Previewの醍醐味(?)ですね。

概要

前回の記事ではStatic MeshをChaosのGeometryCollectionActorに置換して破壊するところまでを作成しました。
papersloth.hatenablog.com

今回はその続きです。
破壊されたところからエフェクトが発生するようにしていきます。

Niagaraを使用しますので、前準備として「Niagara」のPluginが有効になっていることを確認しましょう。
f:id:PaperSloth:20200926174314p:plain
余談ですが、UE4.26から追加される「Cascade To Niagara Converter」もどこまで上手くConvertしてくれるのか気になるところですね。


まずは現段階でNiagaraに追加されているChaos関連のModuleを見ていきましょう。
ChaosNiagaraのPlugin内には「ApplyChaosData」と「SpawnFromChaos」の2つのmoduleがあります。
f:id:PaperSloth:20200926171727p:plain
ApplyChaosDataはParticle Spawnから使用できて
SpawnFromChaosはEmitter Updateから使用できることが確認できました
f:id:PaperSloth:20200926172009p:plain
f:id:PaperSloth:20200926172051p:plain

他にも「ChaosDestrutionData」と「ChaosDestructionEvent」があり、これらはChaosNiagaraのPlugin内の
「NiagaraDataInterfaceChaosDestrution」内に定義されています。
f:id:PaperSloth:20200926173634p:plain
f:id:PaperSloth:20200926173739p:plain
f:id:PaperSloth:20200926173804p:plain
この2つはNiagaraEmitterに追加するModuleではなく
NiagaraSystem内のNiagara Overview Node内のSystem Settingsから使用します。
f:id:PaperSloth:20200926173840p:plain

Niagaraで煙のエフェクトを作成

まずはChaosと紐付けるためのNiagara Emitter, Niagara Systemを作成します。
Content Browser上で右クリックして「FX -> Niagara Emitter」を選択します。
f:id:PaperSloth:20200926174048p:plain

既存のTemplateからEmitterの作成を選択して
f:id:PaperSloth:20200926174152p:plain

「Omnidirectional Burst」をベースに作成していきます。
f:id:PaperSloth:20200926164554p:plain

名前は「NE_Smoke」としました
f:id:PaperSloth:20200926164653p:plain

煙のMaterialは「Starter Content」に含まれている「M_smoke_subUV」を使用します。
NE_SmokeのRender -> Sprite RendererのMaterialをM_smoke_subUVに更新します。
f:id:PaperSloth:20200926164808p:plain

SubUVを含んだMaterialですので、同じくSprite Renderer内の
「Sub Image Size」を「X: 8.0, Y: 8.0」に設定して「Sub UV Blending Enabled」にチェックを入れます。
f:id:PaperSloth:20200926170753p:plain

続いて「Particle Spawn」に「Sub UVAnimation」Moduleを追加します。
f:id:PaperSloth:20200926171440p:plain

このままでは煙が小さいため、サイズを変更します。
強引なやり方ですが「Particle Update」の「Scale Sprite Size」の
「Scale Curve」を「10.0」にします。
これで10倍のサイズの煙になったと思います。
f:id:PaperSloth:20200926175052p:plain

また、重力がかかって徐々に落下するエフェクトになっていますが
こちらは今回不要ですので「Particle Update」の「Gravity Force」を削除します。
※Gravity Force Moduleを選択してDeleteキーでも可
f:id:PaperSloth:20200926175154p:plain

最後に「Thumbnail」を押下してアイコンの更新を行った後に
「Apply」で変更を適用して保存して煙のエフェクトのベースは完成です。
f:id:PaperSloth:20200926175322p:plain

NiagaraとChaosの連携

※以降の手順でChaosとの連携周りでエンジンがやや不安定です。
前置きが長くなりましたがようやくChaosとの連携に入ります。

まずは先程作成したNiagara Emitterですが、そのままではLevelに配置ができないため
「NE_Smoke」を選択して右クリックで「Niagar System」を作成します。
f:id:PaperSloth:20200926175904p:plain

名前は「NS_Smoke」としました。
f:id:PaperSloth:20200926180013p:plain

NS_Smokeを開いてChaosとの連携のためのModuleを追加していきます。
まずは「Emitter Update」に「Spawn from Chaos」を追加します。
f:id:PaperSloth:20200926180112p:plain

Chaosをベースにエフェクトの生成処理を行うため、不要になった
「Spawn Burst Instantaneous」を無効化します。
(Moduleの右のチェックボックスのチェックを外す)
f:id:PaperSloth:20200926180239p:plain

Niagara Systemからは大元のNiagara EmitterのModuleを削除することはできないため
不要な場合は無効化することで対応します。
Niagara SysmteからはEmitterに対してModuleの追加とPropertyの変更のみが可能です。

次にChaosとの連携のために必要なModuleとして
「Particle Spawn」に対して「Apply Chaos Data」を追加します。
f:id:PaperSloth:20200926180440p:plain

Niagaraでは「Spawn from Chaos」か「Apply Chaos Data」のModuleを追加した時点で
自動的にNiagara Overview Nodeの「User Parameters」内に
「Chaos Destruction Data」が追加されます。
f:id:PaperSloth:20200926181009p:plain

ひとまずここまでの作業でNiagaraとChaosの連携に関しての動作確認は可能です。
クラッシュが怖いので、一度保存してから
GC_Chair」がLevelに存在することを確認した上で
作成した「NS_Smoke」をLevelに配置してみましょう。

以下のように衝突時に煙のエフェクトが発生することが確認できれば成功です。
f:id:PaperSloth:20200926182012g:plain


ここまでで動作することの確認が出来ましたが
ChaosとNiagaraの両方をLevel上に配置していると管理しにくいためBlueprintにまとめてしまおうと思います。

ChaosとNiagaraをまとめたBlueprintの作成

新規に「Blueprint Class」を作成します
f:id:PaperSloth:20200926182318p:plain

親Classには「Actor」を選択します。
f:id:PaperSloth:20200926182352p:plain

名前は長いですが「BP_LinkingChaosAndNiagara」としました。
f:id:PaperSloth:20200926182552p:plain

Blueprintを開くと新規に2つのComponentを追加します。
「Geometry Collection」Componentと
f:id:PaperSloth:20200926182650p:plain
Niagara Particle System」Componentです。
f:id:PaperSloth:20200926182723p:plain

「Geometry Collection」を選択して「Rest Collection」に
GC_Chair」を設定します。
f:id:PaperSloth:20200926182855p:plain

続いて「Niagara」を選択して「Niagara System Asset」に
「NS_Smoke」を設定します。
f:id:PaperSloth:20200926182940p:plain

あとは先程同様に「BP_LinkingChaosAndNiagara」のBlueprintをLevelに配置して動作することを確認しましょう。


次にエフェクトの発生方法や発生数の調整についてです。
再び「NS_Smoke」を開いて作業を行っていきます。
「Chaos Destruction Data」ではNiagaraとChaosの連携周りの様々なパラメーターを編集することが可能です。
例えば
「Solver -> Solver Data -> Data Source」ではエフェクトの生成に関するオプションの設定ができます
f:id:PaperSloth:20200926181532p:plain

Collision Data
f:id:PaperSloth:20200926191211g:plain
Breaking Data
f:id:PaperSloth:20200926191107g:plain
Trailing Data
f:id:PaperSloth:20200926191009g:plain

まとめ

長くなりましたが、以上です。
Chaosに関しては私自身まだまだ分からないことだらけですが、コミュニティ全体で情報を出し合って盛り上げていければと思います。
今回の作例でクラッシュする部分についても設定ミスによる原因がいくつかありそうな気もしますので
気付いた点などがあればTwitter等でリプやDMなどを投げていただければと思います。

UE4 新しい物理破壊システムChaos Destructionを使ってみよう

環境

UE4.26.0 Preview1
エンジンビルドなしでようやくChaosが使えるようになりました。
とはいえ、Preview環境ですのでまだまだ不安定な部分はあるかもしれません。

概要

Chaos自体はUE4.23以降でもエンジンビルドをすることで利用は可能です。
エンジンビルドとはいってもTarget.csを少しいじる程度ですので、複雑なことはありません。
詳細は以下のdocをご覧ください。
Chaos Destruction Overview | Unreal Engine Documentation

さて、そんなChaosですがUE4.26からは本格的に利用できるようになりそうです。
Unreal Engine 4.26 Preview 1 がリリースされました - Unreal Engine

UE4.26.0 Preview 1の概要は以下から。
Unreal Engine 4.26 Preview - Unreal Engine Forums

Chaos関連だけでもいくつかの記述がありますね。
・Chaos Ragdoll/Physical Animation (Beta).
・Chaos RBAN(Chaos Rigid Body Animation Nodes).
・Chaos Cloth.
・Chaos Vehicle (Experimental).
従来のPhysXからChaosがエンジンのデフォルト機能となることや
機能の更新されたRagdollやClothシュミレーションに加えて、新しく追加されたChaos Vehicleなど。
ワークフローに大きな変更を与えずに使用できるというのも大変嬉しい限りですね。

それでは早速Chaosを使ってみましょう。

Chaosで破壊可能なオブジェクトを用意する

まずはChaos関連のPluginが有効になっていることを確認します。
f:id:PaperSloth:20200926150852p:plain

このうち、Chaos CachingとChaosVehicleはデフォルトでは無効ですが今回は使用しないためそのままで問題ないです。

まずは破壊するためのStatic MeshをLevel上に配置します。
今回は「Starter Content」に含まれている「SM_Chiar」を使用します。
f:id:PaperSloth:20200926155744p:plain

次に「Modes」を「Fracture」に切り替えます。
Shift + 6でも可能です。
f:id:PaperSloth:20200926151533p:plain

Fracture Modeが有効になった状態でLevel上の「SM_Chair」を選択して
Fractureの「New」を押下します。
f:id:PaperSloth:20200926151644p:plain

するとGeometry Collectionの保存ダイアログが開きますので適当な場所に保存します。
名前は「GC_Chair」としました。
f:id:PaperSloth:20200926155844p:plain

ここまでの作業を行うとLevel上のStaticMeshがGeometryCollectionActorに置換されます。
f:id:PaperSloth:20200926155917p:plain
また、今回は単一のStaticMeshからGeometryCollectionを作成しましたが
Level上の複数のStaticMeshを選択してから作成することで
複数のStaticMeshが登録された1つのGeometryCollectionを作成することも可能です。

続いてVoronoi Fractureの作成を行います。
いくつか種類がありますが、今回は「Uniform Voronoi Fracture」を使用します。
f:id:PaperSloth:20200926152438p:plain

Uniformを選択した状態だと「Fracture」Mode Panelの表示が以下のようになりますので
Level上の「GC_Chair」を選択した状態で「Fracture」を押下することで適用が可能です。
また「Uniform Voronoi」のmin/maxの値で分割数を変更することが出来ます。
f:id:PaperSloth:20200926154549p:plain

するとFractureのOutlinerのGC_Chair.Geometry.../SM_Chairだったところに
SM_Chair_0, 1...19が追加されます。
f:id:PaperSloth:20200926160024p:plain

分割度合いの確認のために「GC_Chiar」を選択した状態で
「Explode」の値をスライドして確認してみましょう。
f:id:PaperSloth:20200926160106g:plain
また、「Colors」を有効化している場合は以下のような見た目になります。
f:id:PaperSloth:20200926160132g:plain


実際にPlayしてみると破壊されることが確認できました。
f:id:PaperSloth:20200926161117g:plain

また、GeometryCollectionのMaterialかLevel上のGeometryCollectionActorのMarerialで
断面部分のMaterialを変更することが可能です。
f:id:PaperSloth:20200926161736p:plain
f:id:PaperSloth:20200926161749p:plain

以上で作成完了です!

追記 Implicit Typeについて (2020/09/07)

Geometry Collection内のImplicit Typeについて
GC_Chair」を開くとChaos Physicsについての設定項目がいくつかあります。
その中でもコリジョン構造を初期化するための種類として「Implicit Type」があります。
f:id:PaperSloth:20200927083706p:plain
大きく分けて2種類あり、Analytic Collision VolumesとLevel Set Volumesの2種類に分けることができます。
Analytic Collision VolumesはBox, Sphere, Capsuleの3種類あり
BoxはBounding Boxと同様の方法で剛体をラップされており
Sphere, Capsuleは剛体に収まるように配置されるようです。
CapsuleUE4.26.0 Preview 1段階でも非サポートでエディターがクラッシュします。
Boxの挙動
f:id:PaperSloth:20200927084021g:plain
Sphereの挙動
f:id:PaperSloth:20200927084121g:plain

最後にLevel Set VolumeですがこちらはAnalytic Collision Volumesより正確で
Min/Max Level Set Volumeからパフォーマンス調整が可能なようです。
また、Level Setを使用する場合は「Collision Type」を「Particle-Implicit」に設定する必要があります
f:id:PaperSloth:20200927084730p:plain
以下Level Set Volumeの説明をドキュメントより引用

Voxel化されたGridを使用して剛体をサンプリングし、Geometryの符号付きDistance Fieldを生成します。

Level Setの挙動
f:id:PaperSloth:20200927084524g:plain

UE4 ダメージの差分だけ徐々に減るHPゲージの実装

環境

UE4.25.3

概要

こういうやつを作ります
(録画に失敗して緑のノイズが入っちゃいましたが、イメージは伝わるかと)
f:id:PaperSloth:20200924200027g:plain

HPゲージの実装方法については前々回の記事を参照いただければです。
papersloth.hatenablog.com

Widgetの実装

Materialを使ってメインカラー、差分ダメージカラーを表現することも可能ですが
今回は一番実装が楽な「Progress Bar」を2つ使う方法で実装していきます。

Widgetを開いて前面の緑の「Progress Bar」と背景の赤い「Progress Bar」を用意します。
f:id:PaperSloth:20200924200350p:plain

前面の緑のProgress Bar「HPProgressBar」では背景が不要なので「Style -> Background Image -> Tint のAlpha(A)」を0.0にして
緑色にしたいので、「Fill Color and Opacity」を「0, 1, 0, 1」に設定します。
f:id:PaperSloth:20200924200632p:plain

続いて背景の赤いProgress Bar「DeltaHPProgressBar」では「Fill Color and Opacity」を「1, 0, 0, 1」に設定します。
f:id:PaperSloth:20200924200817p:plain

次にWidgetのBlueprint Graphに切り替えて
前面のProgress Bar更新用のイベント「UpdateProgress」と
背景のProgress Bar更新用のイベント「UpdateDeltaProgress」を用意します。
f:id:PaperSloth:20200924200931p:plain

HPを持ったキャラクターのBlueprintの実装

まずは変数を3つ用意します。
1. 最大HPを設定した「MaxHealth」
2. 差分HPを保持する「DeltaHealth」
3. 現在のHPを表す「Health」

今回の例では「MaxHealth」を「100」にしています。
f:id:PaperSloth:20200924201355p:plain
3つの変数にはそれぞれ同じ値を与えて初期化しますが
値を調整した時に都度全てを更新していては手間なので「MaxHealth」にだけ初期値を設定して
他の2つには同一の値で初期化するようにします。
f:id:PaperSloth:20200924201443p:plain

次にダメージ処理を組んでいきます。
解説に入る前に全体像を貼っておきます。
なお、今回はノード量が多くなったため、本質とは関係ない敵のHPが0になった時の死亡処理は省いてあります。
f:id:PaperSloth:20200924204455p:plain

ダメージ処理のほとんどは前々回の記事で組んだものをそのまま流用して拡張してもらえれば早いですが
今回も同じように組んでいきます。

先ずは通常のダメージを受けた際の残りHPの計算と前面のProgress Barの更新処理です。
f:id:PaperSloth:20200924202150p:plain

現HPからダメージを引いて、最大HPからの割合を求めてProgress Barを更新しています。

次に差分ダメージのアニメーション処理を行いますが
処理が横長になってしまうため「Sequence」ノードを追加して下に流します。
あるいはCustom Eventを作成したり関数を作成して処理を分割するのもよいでしょう。
f:id:PaperSloth:20200924202422p:plain

アニメーションをさせるにあたって
最初に、ダメージアニメーションを開始するまで待つ処理を組みます。
これがないと以下のような挙動になってしまうためです。

以下の実装は2つ問題があります。
1. ダメージアニメーションがいきなり開始される
2. ダメージを受けた時に赤いバーが伸びている
f:id:PaperSloth:20200924202718g:plain

これら2つの問題を回避するため、ダメージアニメーション開始までの待ち時間を設けます。
新規に「AnimationWaitTime」という変数を追加します。
今は「1.0」を設定しているため、最後にダメージを受けてから1秒後にアニメーションが開始するようになります。
f:id:PaperSloth:20200924203005p:plain

続いて先程の「Sequence」ノードから「Retriggerable Delay」を呼び出します。
f:id:PaperSloth:20200924203049p:plain

通常のDelayノードでは攻撃が連続ヒットした場合に
最初にダメージを受けてから 1秒後にアニメーションが開始されてしまいます。
それを回避するため、呼び出される度にタイマーがリセットされる「Retriggerable Delay」を使用しています。
以下はDelayの場合の挙動
f:id:PaperSloth:20200924203415g:plain


次に実際のアニメーション処理です。
まずは「Timeline」ノードを追加します。
設定内容は「Length」が「1.0」で
Keyは2つ打って「Time/Value 0.0」と「Time/Value 1.0」です。
つまり、1秒間のアニメーション処理で値が0 から 1に増えるようなカーブを作成しています。
アニメーション時間を変更したい場合はLengthと2つ目のKeyのTimeを調整してやればよいです。
f:id:PaperSloth:20200924203611p:plain
f:id:PaperSloth:20200924203820p:plain


最後にこのタイムラインに沿って背景のProgress Barを更新するだけです。
f:id:PaperSloth:20200924204011p:plain

「Lerp(線形補間)」を使用しているため、タイムラインは0 - 1でアニメーションするように設定しました。
まずはLerpで「DeltaHealth」から「Health」に補間するようにしていますが
「DeltaHealth」にはダメージを受ける前のHPを設定しています。
ゲーム開始時であれば最大HPが、ダメージアニメーション終了後は残りHPをそれぞれセットしています。

あとは通常のHP同様に最大HPとの割合を求めてProgress Barに反映するだけです。

以上で最初に貼った完成イメージのような挙動が組めたのではないでしょうか。

まとめ

今回はダメージを受けてからTimelineによってアニメーションさせる方法でしたが
他にもTickとInterp Toを使用する方法など、いくつかありますが
個人的にはこの方法が特に管理しやすいと感じています。

アニメーション開始までの待ち時間(Animation Wait Time)と
アニメーション再生時間のTimelineの2つだけでそこそこ調整がしやすいためです。

以上です!

UE4 大雑把に画面内に特定のActorが入っているかを判定

環境

UE4.25.3

概要

画面内/カメラ内に特定のActorが入っているかどうかを判定したいケースは大変多いと思います。
今回はいずれも大雑把なものなので、正確に判定が必要な場合は別の実装方法を検討された方が良いかもしれません。
実運用に耐えうるかどうかはケースバイケースだと思いますが、何かしら役に立てば幸いです。

それぞれのcaseでメリット / デメリットがあります。
f:id:PaperSloth:20200924041120p:plain
遮蔽物判定は別途、カメラからActorに対してRayを飛ばす等して判定するのが無難かなという気もします。

今回はせっかくなので前回作ったHPバーの表示 / 非表示で動作確認を行います。
papersloth.hatenablog.com


画面内判定の実装方法

case1 直近に描画されたかどうかで判定(正確さ : 更新頻度による)

f:id:PaperSloth:20200924033336g:plain

Was Recently Rendered ノードを利用
メリット:遮蔽物を考慮してくれる
     最も手軽に実装ができる
デメリット:遮蔽物を無視するオプションがない
      更新頻度次第では処理負荷がかかるかも(要プロファイリング)
HPバーの表示 / 非表示で組んだノードはこれだけです。
この例ではこのActorが直近0.2秒以内に描画されたかどうかで判定を行っています。
f:id:PaperSloth:20200924033411p:plain

case2 カメラの位置/向きとActorの位置を元に特定に閾値内かどうかで判定(正確さ : そこそこ大雑把)

f:id:PaperSloth:20200924034612g:plain

gifの例では意図的に画面の端のほうで少し映っている時にも判定を抜けるようにしています。
この辺はゲームによりけりで要調整かなと思います。

Camera位置/向きとActorの位置を元にDotProductを利用
dotの結果は敵が正面にいる場合は1.0、真横では両サイドどちらでも0.0、真後ろにいる場合は-1.0
つまり、0以下の場合は真横よりも後ろにいるということもチェック出来る
メリット:描画判定をある程度調節ができる
     画面内だけでなく、後ろにいるか等の判定もできる
デメリット:遮蔽物を考慮しない
      case1 ほど正確ではない
f:id:PaperSloth:20200924034202p:plain

また、閾値である程度の調整が効いて使いまわしもしやすいため
Blueprint Function Libraryなどにまとめておいてもよいでしょう。
f:id:PaperSloth:20200924040559p:plain
f:id:PaperSloth:20200924040504p:plain
f:id:PaperSloth:20200924040523p:plain

case3 Screen座標変換時のノードで判定(正確さ : 最も大雑把)

Project, Project World to Screen, Convert World Location To Screen Locationノードを利用
f:id:PaperSloth:20200924041757g:plain

画面外にいる場合でも判定が抜けているのが分かるかと思います。
大凡左右の半分以上から離れていないと画面内として判定されてしまいますね。
そのため、画面内かどうかというよりは敵が前面にいるかどうかくらいの判定に使うのが無難だと思います。
例えば、ロックオン対象の敵を最低限前面にいる敵に絞りたい場合とかでしょうか。
メリット:前面にいるかどうかの判定ができる
デメリット:画面内にいるかどうかの判定としては弱い

実装は至ってシンプルです。
Convert World Location To Screen Locationでも
Project World to Screentでも
(HUD only)ProjectのZ > 0でも
いずれも結果は同じになるかと思います。
f:id:PaperSloth:20200924042115p:plain
f:id:PaperSloth:20200924042313p:plain
f:id:PaperSloth:20200924042422p:plain

まとめ

いずれも用途が別のものとしてケースバイケースで使うのが良いのではないかと思います。
何かしら参考になる部分があれば幸いです。

参考資料

今回紹介していない角度を用いた実装方法も紹介されています。
case1, case3の一部はこちらでも紹介されている方法です。
アクターがカメラに映っているかを判別したい - UE4 AnswerHub