PaperSloth’s diary

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

UE4 個人開発メモ

最終更新日:2017/12/03

概要

 個人開発でのUE4のEditor Preference, Project Settings等のメモ
 随時更新していきます。
 基本はWindowsPC向けの内容。
 家庭用ゲーム機、アーケードゲーム、モバイル、XR(AR / VR)でも役立つ内容も含みます。
 各項目には確認時のバージョンを記載しています。
 バージョンが古かったり新しかったりすると
 項目がなくなったり、場所や名前が変わったりしている可能性があります。

環境

・Platform : Windows
・Unreal Engine4.17.2~
Visual Studio Community 2015


Project Settings

Windowタイトルの変更方法(UE4.17.2)

 WindowタイトルとはPC向けに作成した際のWidnowの左上に出るタイトルです。
f:id:PaperSloth:20171130003303p:plain

 何も指定していない場合はProject名が表示されます。

 設定は下図のProject - Description - Displayed
 「Project Displayed Title」から変更できます。
f:id:PaperSloth:20171130004532p:plain

 試しに"Example Window Name"と入れてみました。
f:id:PaperSloth:20171130004635p:plain

Application名の変更方法(UE4.17.2)

 上記Windowsタイトルとよく間違えやすいのがこれです。
 設定は下図のProject - Description - About
 「Project Name」から変更できます。
 デフォルトではUE4Gameと入っています。
f:id:PaperSloth:20171130005438p:plain

 よくWindowタイトルを変更しようとしますが、ここを変えても上手くいきません。
 この名前はタスクマネージャー等で表示されるアプリの名前です。
f:id:PaperSloth:20171130005618p:plain

 ちょっと図がわかりにくいのですがProject Nameがあって、その下にWindowTitleがくるようになっています。
 編集をしていない場合だと、UE4Gameの下にProject名がくるようになっています。
f:id:PaperSloth:20171130010129p:plain

シェーダーモデルの制限によるメモリ容量削減(UE4.17.2)

 設定は下図のPlatforms - Windows
 「Targeted RHIs」から変更できます。

 UE4Windows向けに作成している場合
 デフォルトのRHI(Rendering Hardward Interface)はDX10, DX11が有効になっています。
f:id:PaperSloth:20171130220108p:plain
 これをDX11(ShaderModel5)のみにすることで不要なシェーダーが減り、パッケージ容量を削減できます。
 ThirdPersonTemplate等で試してみてもほぼ全く変わらないのですが
 規模が大きく、マテリアルが増えるほど効果が現れやすい部分です。

 試しに自分のゲームで比較してみた結果
 DX10, 11だと242KBのシェーダー
f:id:PaperSloth:20171130221048p:plain
 DX11のみだと124KBと約半分の容量になりました。
f:id:PaperSloth:20171130221057p:plain

 このように数百KB程度だと微々たる差ですが、これが数百、数千のマテリアルとなると話が変わってきます。
 また、シェーダーも1つで数百MBだと半分ほどの容量になるのは大きな変化ですね。

パッケージング関連

 項目が多いので、細かく分けます。
 設定箇所はProject - Packaging以下のものになります。

PakFileの使用(UE4.17.2)

 「Use Pak File」にチェックを入れると有効になります。
 説明の前に図で見てもらうと分かりやすいと思います。
 Pak Fileを使用した場合
f:id:PaperSloth:20171130222234p:plain
 中には.pakの形式でContent以下のAssetが1つのデータに圧縮されています。

 Pak Fileを使用しない場合
f:id:PaperSloth:20171130222333p:plain

 Pakファイルの使用は容量の削減になり
 転送ファイル数が減るので配布時のダウンロードも早くなり
 ロードも早くなるため使用することを推奨します。
 容量の参考までに自分のプロジェクトで使用した場合の容量を載せておきます。
 使用した場合  :532MB
 使用しない場合 :786MB

パッケージの圧縮(4.17.2)

 「Create compressed cooked packages」にチェックを入れると有効になります。
 f:id:PaperSloth:20171203220605p:plain
 なお、このオプションの効果を有効にするにはUse Pak Fileにチェックを入れておく必要があります。
 容量の参考までに自分のプロジェクトで使用した場合の容量を載せておきます。
 使用した場合  :547MB
 使用しない場合 :950MB

 さて、パッケージの圧縮はDLで配信するPC向け等にはオススメですが、
 一部プラットフォームは注意が必要です。
 PlayStation 4の場合
  PlayStation 4 の圧縮と重複し、ファイル サイズは減らずにロード時間が長くなってしまいます。
 Nintendo Switchの場合
  ロード時間が早くなる場合と遅くなる場合があります。
 詳しくは下部の参考資料のリンクを見てください。


Editor Preference

Blutilityの有効化(UE4.17.2)

 BlutilitはBlueprintをEditor上で呼び出すことでお手軽にEditor拡張が出来る機能です。
 UE4.17.2現在は実験的な機能として提供されています。
 この機能に関しては不安定というよりは、機能がまだ足りないため実験的な機能なのだと思います。

 設定は下図のEditor Preference - General - Experimental - Tools
 「Editor Utility Blueprints(Blutility)」から有効に出来ます。
 有効にした際のEditorの再起動は不要です。
f:id:PaperSloth:20171130011354p:plain

EQS(Environment Querying System)の有効化(UE4.17.2)

 EQSはAIが目的に応じた位置を探す時に使われる機能です。
 この位置は動的に生成され、その中から最もスコアの高い位置を選択します。

 こちらも実験的な機能として提供されていますが
 EpicGamesの開発タイトルである「Robo Recall」でも採用されており、信頼性は高いです。

 設定は下図のEditor Preference - General - Experimental - AI
 「Environment Querying System」から有効に出来ます。
 有効にした際のEditorの再起動は不要です。
f:id:PaperSloth:20171130012353p:plain

参考資料

シェーダーモデルの制限によるメモリ容量削減
バイキング様のスライド63ページ付近に該当箇所の説明があります。
マジシャンズデッド ポストモーテム ~マテリアル編~ (株式会社Byking: 鈴木孝司様、成相真治様) #UE4DD

パッケージング関連
パッケージの圧縮によるプラットフォームごとの注意点等詳しくはドキュメントに載っています。
Unreal Engine | プロジェクトのパッケージ化

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を追加しましょうね(よく忘れる)

UE4 Blueprintで2次元配列を作成する方法

環境

UE4.22.2

Blueprintでの2次元配列の作成方法

先に結論だけを書くと

  • 配列を持った構造体を用意
  • その構造体を配列で定義

上記の2ステップでできます。


コードで示すと下記になります。

USTRUCT(BlueprintType)
struct FIntArrayElement
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game")
	TArray<int32> IntArray;
};

UCLASS()
class PROJECT_API AExampleActor : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game")
	TArray<FIntArrayElement> TwoDimensionIntArray;
};

f:id:PaperSloth:20190531013637p:plain



これぐらいであれば、Blueprintでも表現可能です。

下図のようにArray(配列のメンバー)をもった構造体を定義します。
構造体の作成方法はContent Browserから
Blueprints -> Structure を選択です

次の手順でおそらく警告が出ると思うので、1つ以上の要素を追加しておきます。
f:id:PaperSloth:20190531014219p:plain


次に2次元配列をもたせたいBlueprintに変数を追加します。
その際にVariable Typeを先程作成した構造体に変更し
Arrayに変更します。

すると下図のように配列の中に配列を持った2次元配列を作ることができます。
f:id:PaperSloth:20190531014328p:plain


通常のままでは構造体になっており、使用できないため
構造体の出力ピンから構造体を分割するか
Break (構造体名)で構造体を分割して使用します。
f:id:PaperSloth:20190531014803p:plain


個人的には極力多次元配列を避けて開発したいのですが
どうしても必要という方もいると思ったので書きました。

以上です。

UE4 Unreal C++でのEnumとStringの変換について

環境

UE4.20.3
Visual Studio 2017 Community

Unreal C++でのenumについてはこちら
papersloth.hatenablog.com

Enumから文字列への変換

先ずは列挙型の宣言と定義から。

UENUM(BlueprintType)
enum class EConnectionType : uint8
{
    Multicast,
    Unicast,
    NUM    UMETA(Hidden)
};

class PROJECT_API AExampleActor: public AGameModeBase
{
	GENERATED_BODY()

protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Enum")
	EConnectionType ConnectionType;
}

続いてBlueprint上でのノードの変換がこちら
f:id:PaperSloth:20190514231350p:plain


Blueprintでは上図のようにEnumからStringにCastしてくれます。
しかし、C++側ではこの機能は標準で実装されていないようです。
私が知らないだけという可能性が非常に高いですが。

今回の例は結構強引な型変換を行って実装しているため
既存機能で補えたり、より良い書き方があれば教えてください。


EnumからStringへの変換についてです。
今回は説明のためにエラー処理を省略しているため
実際に使う際はエラーチェックを行うことを推奨します。

FString AExampleActor::EnumToString(const EConnectionType Type, const FString& EnumName)
{
    UEnum* const Enum = FindObject<UEnum>(ANY_PACKAGE, *EnumName);
    return Enum->GetNameStringByIndex(static_cast<int32>(Type));
}

この書き方で動作はしますがEnumNameを都度渡したり
列挙型自体を引数に渡したりと汎用性は低いです。

文字列からEnumへの変換

特に使う場面も浮かびませんが、ついでなので書いておきます。

EConnectionType AExampleActor::StringToEnum(const FString& TypeName, const FString& EnumName)
{
    UEnum* const Enum = FindObject<UEnum>(ANY_PACKAGE, *EnumName);
    int64 Value = Enum->GetValueByName(FName(*TypeName));
    return static_cast<EConnectionType>(Value);
}


上記2つの関数はUFUNCTIONマクロを付けてBlueprint上でも使用できますが
Blueprintでは
Enum -> FString は既に用意されています。
FString -> Enum は最初に書きましたが、特に使う場面が浮かびません。


以上、こういう書き方もありますよという紹介でした。

UE4 PluginのHotReloadについて

環境

UE4.22.1
Visual Studio 2017 Community

概要

通常のProject同様にEditorからCompileボタンを押してHotReload!
というわけにはいかず悩んだ人もいるかもしれないと思ったのでメモ書きを残しておきます。

PluginのHotReloadについて

まず、EditorにあるCompileボタンでHotReloadが行われるのはGame Projectのコードのみです。

Pluginは別の手順でRecompileを行う必要があります。
Window -> DeveloperTools -> Modules を選択してModules Windowを開きます。
f:id:PaperSloth:20190507224617p:plain

あとはそのWindowからHotReloadを行いたいPluginを探してRecompileボタンを押すだけです。
f:id:PaperSloth:20190507224737p:plain


Compile Complete!
f:id:PaperSloth:20190507225143p:plain

UE4 JsonFileの読み込みについて

環境

UE4.20.3
Visual Studio 2017 Community
Windows10

概要

とりあえずでこのリポジトリに突っ込んでるJsonLoad関数を文字に書き起こしただけの備忘録です。
github.com

UE4のゲーム実行時のオプションやプロパティ等を外部ファイルで編集したいとか
用途は何でもいいんですが、JsonFileを使いたい時があるのでLoaderのメモ書きを残しておきます。

JsonFileの読み込みについて

まずJsonLoaderを作成するにあたって、UE4で既存のModuleを利用するため参照の追加が必要です。
ProjectのBuild.csにJsonを追加します。

PublicDependencyModuleNames.AddRange(
    new string[]
    {
        "Core",
        "Json", // 追加
    }
);


続いて実際のLoad処理を書いていきますが、UE4側で既にJsonObject等の便利なクラスが用意されているので、そちらを使っていきます。
今回は雑把にBlueprintFunctionLibraryに書いていきます。



次に簡単なJsonを用意しました。
とりあえず一通り使うであろうbool, int, array, string, vector(float)が入っています。

// Example.json
{
    "isFullScreen": false,
    "windowWidth": 1280,
    "exampleArray": [
        {
            "exampleString": "Hello Json",
            "offset": {
                "x": 100.0,
                "y": 0.100,
                "z": 3.14
            }
        }
    ]
}


ここまでで一旦JsonObjectとjsonの作成ができました。
しかし、このままでは利用できないためJsonObjectから実際に値を取り出すコードを簡単に書いていきます。

#include "JsonFunctionLibrary.h"

// pathは (ProjectDir)/Json/Example など
bool AExampleActor::LoadJson(const FString& Path)
{
    const auto JsonObject = UPSJsonFunctionLibrary::LoadJsonObject(JsonFile);
    if (JsonObject.IsValid() == false)
    {
        return false;
    }

    MaxFps = static_cast<float>(JsonObject->GetNumberField("MaxFps"));
    IsFullScreen = JsonObject->GetBoolField("isFullScreen");
    WindowWidth = JsonObject->GetIntegerField("windowWidth");

    const auto ArrayField = JsonObject->GetArrayField("exampleArray");
    for (const auto Field : ArrayField)
    {
        const auto ObjectField = Field->AsObject();
        ExampleString = ObjectField->GetStringField("exampleString");

        auto const OffsetField = ObjectField->GetObjectField("offset");
        Offset.X = static_cast<float>(OffsetField->GetNumberField("x"));
        Offset.Y = static_cast<float>(OffsetField->GetNumberField("y"));
        Offset.Z = static_cast<float>(OffsetField->GetNumberField("z"));
    }
    return true;
}