PaperSloth’s diary

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

UE4 EditConditionsについて

環境

UE4.23.0
Visual Studio Community2017

EditConditionsについて

EditConditions metadataはbool値によって任意のPropertyの編集を有効化/無効化する機能です。

例 : 水泳可能フラグが有効なキャラクターのみ水泳速度を設定する場合

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character")
bool IsSwim;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character", meta = (EditCondition = "IsSwim"))
FVector SwimVelocity;

フラグが無効な状態では速度を編集することができない
f:id:PaperSloth:20191012175849p:plain
f:id:PaperSloth:20191012175915p:plain

フラグが有効な状態のため、速度を編集することができる
f:id:PaperSloth:20191012180154p:plain
f:id:PaperSloth:20191012180201p:plain

このmetadata自体はUE4.23以前のバージョンからも使用できました。


しかし、UE4.23からはこのmetadataに簡単なbool式を割り当てることが可能となりました。
下記の「新機能: EditConditions メタデータの改善 (ベータ版)」を参照
www.unrealengine.com

例えばEnumとの比較やint値との比較などが可能です。
UE4.23以前のEditConditionsでは下記はエラーにはなりませんが認識されませんでした。

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character", meta = (EditCondition = "IsSwim == true"))
FVector SwimVelocity;


UE4.23からはbool式の評価ができるため、下記のような使い方ができます。
例 : 弾丸の種類がMissileの時だけホーミング性能を設定できる

UENUM(BlueprintType)
enum class EBulletType : uint8
{
    Beam,
    Missile,
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bullet")
EBulletType BulletType;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bullet",
         meta = (EditCondition = "BulletType == EBulletType::Missile"))
float HomingAcceleration;

弾丸の種類がビームの場合はHoming性能の変更はできない
f:id:PaperSloth:20191012181301p:plain
f:id:PaperSloth:20191012181320p:plain

弾丸の種類がミサイルの場合はHoming性能の変更ができる
f:id:PaperSloth:20191012181326p:plain
f:id:PaperSloth:20191012181516p:plain



Unreal C++のproperty向けのmetadataはほかにもたくさん便利な機能があります。
その中でもEditConditionを適切に設定することは他職種とのやり取りが多い、ある程度規模の大きなタイトルでは必須ではないかと思います。

以上です!

UE4 MaterialのHierarchyの紹介

環境

UE4.23.0

Hierarchyボタンについて

UE4.23からMaterial Editor, Material Instnce EditorにHierarchyボタンが追加されました。
Hierarychyボタンが追加されたのはMaterialで、Material Instanceには以前のバージョンからありました。
具体的なバージョンについては記憶していないです……
後日分かり次第追記いたします。

www.unrealengine.com

まずはMaterial Editorから
Third Person TemplateのGraymanのMaterialを見てみましょう。
f:id:PaperSloth:20191009234138p:plain

このMaterialはMaterial Instanceとして胸のロゴのMaterial Instnaceを持っていますね。
UE4.23から追加された[Hierarchy]ボタンを押すと
依存関係にある子のMaterial Instanceの一覧が表示されます。

複数子のMaterial Instanceがある場合はそれらが全て表示されます。
そこからMaterial Instanceを開くこととContent Browserから探すことができます。

あのマテリアルどっかで使ってたけど、どこだったかなって時に便利ですね。


続いてMaterial Instanceの方を見ていきましょう。
先程同様にHierarchyボタンが追加されています。
Material Instanceには以前のバージョンからありました!
ここからさらに子のMaterial Instanceや親のMaterial, Material Instanceを探すことができます。
f:id:PaperSloth:20191009234626p:plain


簡単な機能紹介でしたが、ちょっと便利になりましたね!

UE4 1フレーム処理をスキップさせる方法

環境

UE4.23.0

処理を1フレームスキップさせる方法

結論からいきましょう。
DelayノードをDuration 0で配置するだけで実装ができます。

実験その1 Sequence

Sequenceノードは上から順に実行されていくものでしたね。
ここではThen 0で1フレーム後に"Delay"と出力するのと
Then 1でDelayなしで"Execute"と出力される2つのノードがあります。

結果としてはThen 0の実行ピンが先に実行されますが
Delayを挟んでいるため
出力結果としては"Execute"が表示されてから"Delay"が表示されます。

実験その2 複数フレームの遅延は可能か


続いての実験はDelayノードを2つ挟んだ場合にどうなるかです。

この場合は"Execute" -> "Delay" -> "DelayDelay"の順で表示されます。

先程同様にSequenceノードは上から処理していきますが
Delayを挟んでいるため、最初の処理はこのフレームでは実行されないためです。
複数使用した場合はその個数分のフレームを遅らせることができるようです。
※2つまでしかテストしていないため最大幾つかは不明です

ただし、複数フレーム遅延させてノードを組むのは極力避けてほしいです。
処理が大変追いにくくなり、開発終盤になってからバグの原因だったということにもなりかねません。
もしも、このフレームスキップを使用するのであれば1フレーム以下として
使用する際も分かりやすいような命名やコメント等に色を付けるなど工夫してもらえればと思います。

Delayの仕組み

Delayノードの中身は下記に処理が書かれています。
Engine\Source\Runtime\Engine\Private\KismetSystemLibrary.cpp
以下にDelayのコードを載せておきます。

void UKismetSystemLibrary::Delay(UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo )
{
    if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
    {
        FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
        if (LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
        {
            LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
        }
    }
}

Delayノードが呼び出されると
1. FLatentActionManagerを取得
2. FLatentActionManager内に重複したFObjectActionsが登録されていないかチェック
3. FDelayActionを作成
3. FLatentActionManagerにFDelayActionを登録
というような流れになっています。

Delayノードが呼び出されると処理が直ぐに実行されるわけではなく
FLatentActionManagerに登録だけを行っているということが分かりましたね。


ここがちゃんと追えていなくて申し訳ないのですが
FLatentActionManager内に登録された処理がこのフレームで呼び出されていないため
1フレーム遅延して実行されるのではないかと考えています。

そのため、厳密には1フレームの実行ではなく
「1フレーム遅延して実行されているように見える」というのが正しい可能性もあります。
この辺りは後日確認をしてから追記しようと思います。

また、余談ですが
「RetriggerableDelay」の実装は「Delay」とほぼ同一の実装になっています。
Delayではアクションが登録されていれば何も処理を行わず終了しますが
RetriggerableDelayでは登録されている場合はDurationを更新するだけです。

FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
FDelayAction* Action = LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID);
if (Action == nullptr)
{
    LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
}
else
{
    // アクションが登録されていた場合はタイマーを上書きする
    Action->TimeRemaining = Duration;
}

以上です!

追記
UE5ではこっちを使った方がいいかも?という記事を書きました。
papersloth.hatenablog.com

UE4 Delay nodeと実行順序について

環境 : UE4.23.0

目次

Delayの挙動のよくある勘違いについて

Delayを挟んだはずなのに何故か処理が遅れて実行されないという経験をした人のためにありがちな実行順序の勘違いについて書き記す

ケース1 (Delay)

まずは下図のようなノードを組んだとしよう。
「DelayEvent」は1秒待ってから「Delay」と表示する
「PrintEvent」は呼び出されたら直ぐに「Print」と表示する
f:id:PaperSloth:20190930211938p:plain


実行結果として
1秒後にDelayと表示されてからPrintと表示されると思った人もいるかもしれないが、実際はPrintが表示された1秒後にDelayと表示される
f:id:PaperSloth:20190930212720p:plain
ここではBeginPlayの後に「DelayEvent」を呼び出して直ぐに「PrintEvent」を呼び出している
CustomEventは前の処理が終了するまで待つような機能は特にないため
「DelayEvent」の終了を待たずに「PrintEvent」が先に処理される
今回はCustomEventで説明するがFunctionの場合も同様である
※FunctionではLatent nodeが使えないため、今回無関係

ケース2 (Delay + Seqeuence)

下図のようにSequenceを挟んだ場合も同様の結果となる
f:id:PaperSloth:20190930212555p:plain
SequenceはThen * と表記されたOutput pinに繋がれた処理を上から順に実行するが
終了結果を待つような処理にはなっていないため
実行結果は先程と同じ結果になる

処理を遅らせたい場合 -> Macroとして処理する

「DelayEvent」の処理をMacroにする
f:id:PaperSloth:20190930214457p:plain

「DelayEvent」の時とは異なり「DelayMacro」には
Delay node同様の時計マークが付く
Latent nodeとして認識されていることが確認できた
f:id:PaperSloth:20190930214610p:plain

実行結果としては
1秒後にDelayと表示されてからPrintと表示される
f:id:PaperSloth:20190930215730p:plain


さてこれで意図した通りの結果となるわけだが
Delayを加えたMacroの運用には注意してほしい

例えば他の作業メンバーが意図せずDelay付きのMacroをどこかで呼び出した場合に
ゲーム仕様と異なり、何故か数秒遅延する謎の処理が生まれてしまうケースがある
5秒など長ければ直ぐに問題に気付けて良いが
0.1秒などの短い遅延の場合は気付きにくく意図しない不具合が生まれるかもしれない

UE4 Includeファイルが認識されない場合の対処法

環境

UE4.23.0
Visual Studio 2017 Community

Includeファイルがエラーになるケース/対処法

例えばキャラクターの生成時にNiagaraEffectをSpawnしたいケース
下記のようなコードを書いてみます。

// SandboxCharacter.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Niagara/Classes/NiagaraSystem.h"
#include "SandboxCharacter.generated.h"

UCLASS()
class UE4SANDBOX_API ASandboxCharacter : public ACharacter
{
	GENERATED_BODY()
public:
    ASandboxCharacter();
protected:
    virtual void BeginPlay() override;
public:	
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character|FX")
    UNiagaraSystem* CharacterSpawnFX;
// SandboxCharacter.cpp
#include "SandboxCharacter.h"
#include "Niagara/Public/NiagaraFunctionLibrary.h"

void ASandboxCharacter::BeginPlay()
{
    Super::BeginPlay();

    if (CharacterSpawnFX->IsValid())
    {
        UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, CharacterSpawnFX, GetActorLocation(), GetActorRotation(), true);
    }
}

上記コードを記述した際に
SandboxCharacter.h 内の
#include "Niagara/Classes/NiagaraSystem.h"
SandboxCharacter.cpp内の
#include "Niagara/Public/NiagaraFunctionLibrary.h"
2つがエラーになるのではないでしょうか。

エラーの原因/解決方法

エラーの原因としてはNiagara ModuleがProjectに紐付いていないことにあります。
Moduleについては下記が参考になります。
[UE4] モジュールについて|株式会社ヒストリア


解決方法としてはProjectに使用するModuleを紐付けてやればよいです。
①Moduleの追加

// (ProjectName).Build.cs
// 例では UE4Sandbox
using UnrealBuildTool;

public class UE4Sandbox : ModuleRules
{
    public UE4Sandbox(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", /*追加*/"Niagara" });
    }
}

②ProjectのSolutionの更新
Solutionの更新には下記のようなやり方があります。

  • Project起動後 -> File -> Reflesh (IDE Name) Project

f:id:PaperSloth:20190923083226p:plain

  • Explorer -> (ProjectName).uproject上で右クリック -> Generate (IDE Name) project files

f:id:PaperSloth:20190923083456p:plain

まとめ

以上で無事にInclude時のエラーが解決できたのではないでしょうか。

Moduleの追加忘れは初期の頃にハマりやすい罠なので注意が必要です。
SequencerやNiagaraやChaosなどの関数を呼び出す際はModuleの追加忘れに気を付けましょう。
また、Plugin作成時にはPlugin側のBuild.csへのModule追加が必要になります。
ProjectとPluginは別々のBuild.csが作成されるため、追加忘れが発生することがあります。


しかし、いざModuleを追加しようとした際にModule名がわからず追加ができないという時もあるでしょう。
そういった時にはAPI Referenceから検索するとよいです。
関数名を検索するとAPI ReferenceにModule名とIncludeパス/ファイル名の記載があります。
今回のNiagaraの場合

- References
  Module名 : Niagara
  Include path : /Engine/Plugins/FX/Niagara/Source/Niagara/Public/NiagaraFunctionLibrary.h
  Include file : NiagaraFunctionLibrary.h

また、クラスの継承関係の記載もあるため大変有用です。
積極的にAPI Referenceを使用していきましょう。
UNiagaraFunctionLibrary | Unreal Engine Documentation

これらを駆使してエラーをひとつひとつ解決していきましょう。
f:id:PaperSloth:20190923084123p:plain

UE4 Oculus Go, Questの開発について

概要

UE4のOculusGoとOculus Quest向けのTemplateをgithubに公開しています。

- Oculus Go(UE4.19 - UE4.22)
GitHub - PaperSloth/UE4OculusGoTemplate: OculusGo Teloport Example


- Oculus Quest(UE4.22)
GitHub - PaperSloth/UE4OculusQuestTemplate: Oculus Quest VR Template

GoとQuestは基本的に設定が同じなので、Goのセットアップをした人はすんなりと開発を始めることができます。
今回は上記TemplateをDownloadした前提で進めます。

Oculus Go/Quest向けのセットアップ

1. Oculus Go/Questを開発者モードにする
スマホのOculusのアプリを開いて
設定 -> HMDを選択 -> その他の設定 -> 開発者モード から変更が可能です。

開発者モードの有効化は調べてすぐに出てくるため、もし分からなければ別途検索をしてください。

2. CodeWorks for Android のインストール
Engine/Extras/AndroidWorks/Win64/
CodeWorksforAndroid-1R7u1-windows.exe を起動して手順に沿ってインストールを行ってください。

詳細については公式のAndroidのドキュメントに記載があります。
ドキュメントではCodeWorksforAndroid-1R6u1-windows.exe ですが
使用したいエンジンバージョンのものであれば基本的に問題はありません。
Unreal Engine | 1. Android 向けゲーム開発に必要な設定

必須のSDKとしてはAndroid 4.4.2(API 19)は必須です。
Oculus Go/QuestのどちらもAPI 19を利用します。
f:id:PaperSloth:20190617221815p:plain

Project側の設定を変更する

DownloadしたTemplateを起動し、Project Settingsを開きます。

- Platfoms -> Androidから
APK Packaging -> Configure Nowを押下
f:id:PaperSloth:20190617223242p:plain

Google Play Services -> Configure Nowを押下
f:id:PaperSloth:20190617223426p:plain

どちらも「Platform files are writeable」になっていることを確認
f:id:PaperSloth:20190617223524p:plain

SDKの各種pathを通す
f:id:PaperSloth:20190617223812p:plain

- android-sdk-windows
- android-ndk-r14b
- apache-ant-1.8.2
- jdk 1.8.0_77
上記4つへのpathが通っていることを確認してください。

Templateでの設定は以上になります。
新規にプロジェクトを作った場合は他にも設定が必要です。
GoもQuestもGear VRのドキュメントが一番参考になります。
Samsung Gear VR UE 4 のクイックスタート | Unreal Engine ドキュメント

HMDと接続して動かしてみる

Oculus Go/QuestとPCをUSBで接続します。

Androidの設定が無事にできていれば、LaunchからQuestが選択できます。
Oculus Goの場合はPacificという名前です。
f:id:PaperSloth:20190617225019p:plain

Launchし、エラーがなければ一先ず動かして開発を行うことができます。
ようこそ!UE4でのVR開発の世界へ!

HMDをUSB接続せずにadbを用いてリモートで開発する

adbのセットアップが完了している人は下記で行なえます。
> adb shell ip addr show wlan0
でip addressを確認して
> adb tcpip 5555
USBを抜く
> adb connect (ip address):5555
> adb devices
で接続を確認できればUSBで接続をしていなくてもLaunchで実行できます。


では順に見ていきましょう。
まずはadb(Android Debug Bridge)への環境変数を設定する

adb(Android Debug Bridge)へのpathは
\NVPACK\android-sdk-windows\platform-tools になります。
Android SDKをインストールする際にNVIDIA CodeWorks for Android 1R7 Component Managerをインストールしたpathです。

環境変数のPathにこの\NVPACK\android-sdk-windows\platform-toolsへのpathを追加します。
コントロールパネル -> システムとセキュリティ -> システム ->
システムの詳細設定 -> システムのプロパティ -> 環境変数 から編集を行います。

Pathにplatform-toolsへのpathを追加して、PCを再起動します。
f:id:PaperSloth:20190617230349p:plain


再起動をしたあとは
adbへのpathが通っているかを確認します。

Win + Rでcmdと入力するかWindowsメニューからコマンドプロンプトを起動します。
adbと入力して、pathが通っているかを確認します。
> adb
Pathが通っていれば
Android Debug Bridge Version 1.0.xx とadbの使い方のヘルプが表示されます。
f:id:PaperSloth:20190617231651p:plain


HMDとPCをUSBで接続済のことを確認した上で
コマンドプロンプトから下記を入力します。
> adb shell ip addr show wlan0
inet 192.168.xxx.xxx が表示されます。
これがHMD側のIP Addressになります。
f:id:PaperSloth:20190617231354p:plain


次に任意のポートでTCP/IP 接続を行えるようにします。
今回は無難に5555のポートを使用します。
設定できるポートは5555 - 5585だった気がします。
> adb tcpip 5555
TCP/IP接続のリッスン状態にしても特に何もログが出ません。
ここでHMDとPCを繋いでいるUSBを抜きます。


続いて先程取得したIP AddressをもとにHMDに接続を試みます。
> adb connect (IP Address):5555
connected to (IP Address):5555 と表示されれば接続成功です。

念のために再度確認をしておきます。
> adb devices
List of devices attached
(IP Address):5555 device

adb devicesで接続中のデバイス一覧を表示し、接続が行えているかを確認します。
f:id:PaperSloth:20190617232624p:plain


再びUE4 Editorに戻ってUSBを繋いでいない状態でもLaunchできることを確認しましょう。
Launch DeviceがQuest(IP Address):5555 になっていれば成功です。
f:id:PaperSloth:20190617233154p:plain



以上でUSB接続しなくてもVR開発が行えるようになりますね!
とっても快適です。
f:id:PaperSloth:20190617233656j:plain

なお、再度USB接続を行った場合はリモートセッションが解除されてしまうため
再度上記の手順でリモートで開発が行えるようにする必要があります。

UE4 Niagaraリンク集

  • 概要

いくつかNiagaraの素晴らしい情報が増えてきたので、まとめました。
まとめただけです。
私の書いた古いNiagara記事へのアクセスが多く
それではこれから学ぶ人にとってあまりにも不便だと思ったので書きました。

  • 目次

公式ドキュメント

以前はドキュメントもなく、手探り状態でしたが
今ではドキュメントを読むだけで基本的な動きが作れます。

  • Sprite (CPU / GPU)
  • Mesh
  • Ribbon
  • Particle Light

この辺が表現できるようになっています。
ナイアガラ エディタ | Unreal Engine ドキュメント


Content Example

バージョンは最新バージョンのものを起動することを推奨します。
古いバージョンと新しいバージョンで内容が変わることが多いためです。
Niagaraのサンプルについては
・Contents/Maps/Niagara 以下にあります。

今度は実際に動いているデータが見れるので
ドキュメントより直感的だと思います。
後半は少し複雑ですが、序盤のCurl Noiseを使ったサンプルなんかは
サクッと作れる割に派手で面白いです。
f:id:PaperSloth:20190608195320p:plain

カニミソの備忘録

カニパンチさんのブログです。
crabpunch.hatenablog.com
入門から応用まで書かれていて
1つ1つの記事が初めて触る際にも、ちょうどいいボリューム感で非常に分かりやすいです。
私の過去記事の完全上位互換なので、極力こちらに誘導したいと思っていました。
ドキュメント読んでContent Example見たけどよく分からないって人でも
安心して1歩ずつ進めるのでオススメです。

Unreal Fest登壇スライド(新しいエフェクトツール、Niagaraを楽しもう! ~Niagara作例のブレイクダウン~)

池田さんの登壇されたスライドです。

www.slideshare.net

Project Data(Forums)
UNREAL FEST EAST 2018のNiagara講演で使用したプロジェクトの公開について - Unreal Engine Forums

Forums経由でProject Dataを公開していただいています。
スライド内で分かりやすく仕組みが解説されているので、
ドキュメント見ただけではイマイチピンとこなかったという人でも
なんとなく仕組みとかフローが見えてくるのではないでしょうか。
Expressionで値を入力する部分もあって、Houdiniユーザーは馴染みやすそうですね。
Blueprintとの連携も含んでおり、BPユーザーにはすごく嬉しい内容になっていると思います。

UIデザイナーのためのUE4 Niagara入門

WidgetNiagaraの連携を行った数少ない例です。
Labo AquariaさんのCGWorldへの寄稿内容です。
cgworld.jp


GitHub
GitHub - Aquariaue4/UE4Projects

GitHubに公開していただいているので、手元で動作確認をすることができます。
RenderTargetに描画してNiagaraで参照するというのは
先程の池田さんの作例とも共通する部分ですが
こちらはWidget Blueprintとの連携を行っているのが面白いところですね。
詳しくはCGWorldの記事を見ていただけると良いかと思います。

Houdiniとの連携について

まだ情報が少ないと感じる部分です。
SideFXのForumsに投稿してくれている方がいるので、そこが一番参考になるかと思います。
support.borndigital.co.jp
まずよくある誤解としてHoudini EngineとHoudini Niagaraは別のものです。

Houdini EngineはHoudiniで作成したHoudini Digital Asset(HDA)との連携を提供します。
Houdini NiagaraはHoudiniで作成したhcsv(CSV形式)として出力したデータを
UE4にImportしてNiagaraの中で使用するためのものです。

Houdiniとの連携の仕組みとしてはHoudini Niagara Data Interfaceを使用して
Houdiniで事前に計算したposition, velocity, normal, color, id...etc を
Niagaraに渡すことによってHoudini上での動作をNiagara上で再現することが可能です。


今はどうか分かりませんが
以前に検証した際はHoudini Niagaraを有効にした場合に通常のcsv Importが
Houdiniのパラメーターとして認識されてしまいData Tableが作れなかったです。
都度Pluginを有効化/無効化するのは手間ですね。


UE4.22で試したところ、csvcsvとして認識し、hcsvはhcsvとして正しく認識されるようになっていました。

色々書いたんですが、Houdiniとの連携についてはまだ手元で試せていません。

まとめ

以上で各種リンクのご紹介は終わりです。

学習フローのオススメとしては
Document -> カニミソ -> Content Example -> Fest -> CGWorld -> Houdini
この順番が良いと思います。

Documentでまずは仕組みを理解する
カニミソで動かしながら仕組みを理解する
Content Exampleで入門から応用まで触れてみる
池田さんのProjectでBlueprint連携、応用について知る
Labo AquariaさんのProjectでさらに別の応用を知る
余力があったらHoudini試してみてほしい(個人的願望)

あと余談ですがC++Niagaraを使用する場合はBuild.csのDependencyModuleNamesにNiagaraを追加しましょうね(よく忘れる)