PaperSloth’s diary

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

UE4 Niagara入門

環境

UE4.19.0


概要

下記のShadowriverさんが作成してくださったチュートリアルを概ねなぞった内容になります。
動画の大凡52分までの手順をなぞることで同じものが作れます。
動画ではUE4.18で作成されているため、UIの配置や名前が異なっている箇所があります。
State of Niagara in Unreal Engine 4.18 - YouTube

CascadeからNiagaraへの移行ガイドも簡単にですが用意したのでこちらも見てください。
UE4 CascadeからNiagaraへ - PaperSloth’s diary



Niagaraを有効化する

UE4.19ではNiagaraはPluginとして提供されています。

PluginsのFXカテゴリの中にNiagaraがあるので、有効化することで利用できます。
Pluginを有効にした後はエディタの再起動が必要です。
f:id:PaperSloth:20180325175409p:plain


Emitterを作ってみる

Particleを作成するとなると先ずはEmitter(発生源)を作成するところからですね。

Niagara関連の項目はFX以下にあります。
f:id:PaperSloth:20180325175627p:plain


先ずはEmitterを作成してみましょう。
Niagara Emitterを作成すると下図のようなEditorが開きます。
f:id:PaperSloth:20180325175858p:plain

しかし、このままでは色等のパラメーターを変更しても変化がわからないため
Particle用のMaterialを割り当てます。
今回は下記のような簡単なMatarialを作成しました。
BlendMode : Translucent
Shading Model : Unlit
f:id:PaperSloth:20180325180748p:plain

これをNiagara EmitterのMaterialに割り当ててみましょう。
f:id:PaperSloth:20180325180927p:plain

結果は下図のようになります。
f:id:PaperSloth:20180325181025p:plain

Colorを変えたりして結果が反映されることを確認しましょう。
f:id:PaperSloth:20180325181124p:plain


EmitterのNodeGraphを使ってみる

次にNiagaraのNodeGraphを見ていきます。

Window > Selected Emitter Graphから開くことが出来ます。
f:id:PaperSloth:20180325182308p:plain
f:id:PaperSloth:20180325182422p:plain


先ずは動きを追加してみます。
Particle Update内の+ボタンを押下し、VelocityのAddVelocityを追加します。
f:id:PaperSloth:20180325182942p:plain

Emitter Graphを開くと新たにAdd Velocityが追加されていることが確認出来ます。
グラフのOutputはそれぞれ上から順に
・EmitterSpawn
・EmitterUpdate
・ParticleSpawn
・ParticleUpdate
Add VelocityはParticleUpdateのカテゴリなので、ParticleUpdateの末尾に追加されました。
f:id:PaperSloth:20180325183151p:plain

続いてAddVelocityノードをダブルクリックするとAddVelocityノード内の処理を見ることが出来ます。
f:id:PaperSloth:20180325183516p:plain


Niagara Module Scriptを作成する(移動の練習)

先程作成したAdd VelocityをNiagara Module Scriptとして別名で保存します。
f:id:PaperSloth:20180325183657p:plain
f:id:PaperSloth:20180325183922p:plain
f:id:PaperSloth:20180325184014p:plain

下図のように新規作成しても問題はありません。
f:id:PaperSloth:20180325184150p:plain

先ずはノード内の処理を一旦全て削除し
Parameter Map Getノードを呼び出します。
f:id:PaperSloth:20180325184358p:plain

続いてMapノードの+ボタンを押下して
ParticlePositionを取得します。
f:id:PaperSloth:20180325184536p:plain

取得した値に変更を加えるためにAddノードを呼び出します。
f:id:PaperSloth:20180325184724p:plain

ここまでで一旦Parameterの取得、セットまでを行ってしまいましょう。
f:id:PaperSloth:20180325184948p:plain

このままでは取得した値に何の変更も加えていないため、Vectorを追加します。
f:id:PaperSloth:20180325185121p:plain

Zに10をセットし、上方向への移動を加える処理が出来ました。
f:id:PaperSloth:20180325185802p:plain
f:id:PaperSloth:20180325185854p:plain

しかし、これでは変更が反映されないためEmitterのグラフを下図のように繋ぎ変えましょう。
f:id:PaperSloth:20180325190020p:plain

すると先程より早い速度でパーティクルが上に移動することが確認出来ます。
f:id:PaperSloth:20180325190048p:plain

試しにXYに乱数を与えたりして動きが変わることを確認しましょう。
f:id:PaperSloth:20180325190249p:plain


Niagara Module Scriptを作成する(Emitterからの入力)

DeltaTimeの取得
Tickノード等の引数にあるDeltaTimeの取得は下記のように CommonEngine~から取得できます。
f:id:PaperSloth:20180325190835p:plain

DeltaTimeを先程のVectorと掛け合わせると移動が緩やかになったのが確認できます。
f:id:PaperSloth:20180325191155p:plain

DeltaTimeには前のフレームと今のフレームの間の時間の差分が格納されています。
そのため、この値を掛け合わせることで処理落ちした場合や30fpsでも120fpsでも意図した通りに動作します。

さて、次にEmitterから入力できるパラメーターを追加しましょう。
Define Module Localで定義されたパラメーターを追加するとEmitterから値を更新することができるようです。
ここではVectorにSpeedという名前を付けました。
f:id:PaperSloth:20180325191943p:plain

試しにSpeedの値を足して動かしてみましょう。
f:id:PaperSloth:20180325192017p:plain

SpeedのXに5を入れてみるとX軸方向に移動していることが確認できますね。
f:id:PaperSloth:20180325192158p:plain

因みにUE4.19でNiagara Emitterを作成した場合は最初からAddVelocityが登録されているため
この値を0に戻しておきます。
f:id:PaperSloth:20180325192351p:plain
f:id:PaperSloth:20180325192417p:plain

これで完全にX軸のみの移動となりました。
f:id:PaperSloth:20180325192455p:plain


Niagara Module Scriptを作成する(渦を作る)

先程作成したノードを幾つか削除して下図のようにします。
ここから動きを作っていきましょう。
f:id:PaperSloth:20180325192913p:plain

まず最初に新しいパラメーターとしてSpriteRotationを追加します。
名前はAgeとして登録します。
f:id:PaperSloth:20180325193647p:plain
f:id:PaperSloth:20180325193750p:plain

また、先程の手順でやったようにModuleの追加で
AplitudeとFrequincyを追加します。
f:id:PaperSloth:20180325194300p:plain

ModuleはDefine Module Localから追加でしたね。
f:id:PaperSloth:20180325194432p:plain

続いて下記のようにノードを組みます。
f:id:PaperSloth:20180325195201p:plain

AplitudeとFrequincyに値を入れます。
f:id:PaperSloth:20180325195335p:plain

しかし、これだけではX軸にSinカーブを描くだけになってしまうため
先程0にしたAdd Velocityを再びZを100にします。
f:id:PaperSloth:20180325195619p:plain

そうすると下図のようにX軸上に渦を描くことが出来ました。
f:id:PaperSloth:20180325195807p:plain


Niagaraで作成したエフェクトをLevelに配置する

さて、これでエフェクトは一旦完成したのですがこのままではLevelに配置できません。
配置するにはNiagaraActorをNiagaraSystemが必要になります。


先ずはNiagaraSystemを作成します。
f:id:PaperSloth:20180325200951p:plain

Systemを開いても特に何も表示されません。
f:id:PaperSloth:20180325201123p:plain

SystemのTimelineに先程作成したNiagaraEmitterをドラッグ&ドロップするとEmitterのPreviewが表示されます。
f:id:PaperSloth:20180325201328p:plain

これでSystemの準備整ったので、続いてNiagaraActorの作成です。
※NiagaraSystemだけでもレベル上に配置することが可能です。
f:id:PaperSloth:20180325201426p:plain

NiagaraActorを開くとNiagaraComponentがあります。
f:id:PaperSloth:20180325201524p:plain

そのComponent内にNiagara System Assetがあるので、そこに先程作成したSystemを設定します。
f:id:PaperSloth:20180325201724p:plain

これであとはActorをLevelに配置すればゲーム上でも使えますね!
f:id:PaperSloth:20180325201915p:plain


まとめ

最後に少し弄ってこうなりました!


動画の手順をなぞるだけで解説が不十分なのですが
手順を追ってみると何をするとどういう動きになるのか
どうやって作るのかという感覚は得られると思います。

Shadowriverさんに感謝!

SVN ゲームエンジンのプロジェクトを管理しよう

環境

Windows10
TortoiseSVN 1.9.6
Unity 2017.1.0f3
Unreal Engine 4.19.0


概要

VCSを仕事でも趣味でも使っていなくて、興味はあるけどどうしたらいいか分からない。
そんな人向けのローカルでのバージョン管理の続きです。

前回
papersloth.hatenablog.com


Unityプロジェクトを管理してみる

今回はリポジトリの作成、チェックアウトまで済んだところから始めます。
チェックアウトしたフォルダ内にUnityプロジェクトを作成しましょう。

前回同様に先ずは追加から。
f:id:PaperSloth:20180321151701p:plain

最初にプロジェクトを作成した段階だと
Asset以下とProjectSettingsを追加しましょう。
f:id:PaperSloth:20180321151912p:plain

さて、Unityで管理するデータですが全てを管理する必要はありません。
そこで、前回紹介できなかった無視リストの活用をしてみましょう。

無視リストの活用

先ずはLibraryフォルダ以下のデータをバージョン管理から除外します。
無視リストに追加されたデータは今後追加、コミットをする際の選択肢から除外されて管理がしやすくなります。

無視リストへの追加は下図のように2種類あります。
Library
Library (recursively) または Library (再帰的)

この2つの違いですが、再帰的を選択すると
このフォルダだけでなく、すべてのサブフォルダも同様に無視されます。
この機能はSVN1.8以上が必要になります。

今回は通常のLibraryを無視リストへの追加を選択します。
f:id:PaperSloth:20180321152109p:plain

するとLibraryフォルダから ? マークが消えました。
これでバージョン管理外のデータとして扱う事ができます。
f:id:PaperSloth:20180321152702p:plain

同様にTempフォルダも管理する必要が無いため、無視リストに追加してしまいましょう。

あとは必要なデータだけをコミットして完了です。
f:id:PaperSloth:20180321152849p:plain

Diffツールの活用

この時無視リストに追加したデータは(ProjectName)フォルダに記入されています。
コミット画面にある(ProjectName)ここではNewUnityProjectフォルダをダブルクリックしてみましょう。
するとTortoiseMergeというツールが立ち上がります。
左画面が変更前のデータ、右画面が変更後(今回変更した)データになります。
Library, Tempが追加されていることが確認できますね。
f:id:PaperSloth:20180321153437p:plain

今回は無視リストと差分の見方について紹介しました。
もう少し作業を続けてみましょう。
続いてPlayerControllerのスクリプトを追加しました。
f:id:PaperSloth:20180321154125p:plain

これでコミットしようとすると色々とデータが増えていますね。
今回は.vsとobjが必要ないのでまた無視リストに追加します。
無視リストへの追加はコミットのメニューからも行えます。
しかし、メニューが4種類になっていますね。
"*"を選択してしまうと
プロジェクト以下のバージョン管理したいデータも無視リストに加えてしまいます。
ここでは.vsを選択します。
f:id:PaperSloth:20180321154416p:plain

同様にobjも無視リストに加えましょう。
これで随分とスッキリしましたね。
さて、状態のところがバージョン管理外となっているので、これらも追加をしてやる必要がありますね。
f:id:PaperSloth:20180321154745p:plain

ですが、追加を行わなくても下図のように選択してコミットを行うことで追加とコミットをまとめて行ってくれます。
f:id:PaperSloth:20180321154932p:plain
f:id:PaperSloth:20180321155106p:plain

続いて先程さらっと流してしまった差分の見方についてもう一度おさらいをしましょう。
PlayerContollerを先程作成してコミットしましたね。
あのクラスに少しを変更を加えて差分を確認してみましょう。
f:id:PaperSloth:20180321155410p:plain

先程コミットしたので、状態がバージョン管理外から変更になっていますね。
PlayerContollerをダブルクリックしてTortoiseMergeを開いてみましょう。
左側が変更前の元データで右側が変更後の現在のデータでしたね。
削除した行はグレーで表示され、変更された箇所は黄色で表示されていることが確認できます。
f:id:PaperSloth:20180321155547p:plain

変更の取り消し

続いて変更の取り消しについて紹介します。
バージョン管理を使う理由の1つですね。
先程差分の確認方法について確認しましたね。

ですが、作業前のデータに戻したいという場合もあるでしょう。
そういった場合は下記のように変更の取り消しから変更を元のデータに戻すことができます。
f:id:PaperSloth:20180321160308p:plain
f:id:PaperSloth:20180321160351p:plain

Logの確認方法

時には過去の変更履歴を確認したいこともあるでしょう。
そういった場合はログを表示から確認することができます。
f:id:PaperSloth:20180321160728p:plain
f:id:PaperSloth:20180321160920p:plain

特定リビジョンに戻る

ログから戻りたいリビジョンを選択することでそのリビジョン状態へと戻すことが出来ます。
f:id:PaperSloth:20180321161214p:plain


UE4プロジェクトを管理してみる

基本操作等はUnityと同様ですので、一旦上のUnityの内容に目を通している前提で進めます。

Unity同様にリポジトリの作成、チェックアウトまで済んだところから始めます。
チェックアウトしたフォルダ内にUE4プロジェクトを作成しましょう。
今回はC++の空のプロジェクトで行います。
f:id:PaperSloth:20180321161947p:plain

Unityの時と同様に先ずはプロジェクトを追加します。
f:id:PaperSloth:20180321162359p:plain

無視リストの活用

UE4では.vs, Binaries, Intermadiate, Saved, sln, VC.db, .opendbはバージョン管理の必要がありません。
これはPluginでも同様です。
逆を言ってしまえばConfig, Content, Source, uproject, upluginだけ管理すればいいわけですね。
f:id:PaperSloth:20180321162725p:plain

(ProjectName)フォルダの差分を確認して、無視リストが更新されていることを確認しましょう。
sln, db等はそのファイルだけでなく拡張子ごとまるまる無視リストに追加しています。
f:id:PaperSloth:20180321163048p:plain

また、ContentフォルダのCollections. Developersフォルダも不要であれば無視リストに追加しましょう。
f:id:PaperSloth:20180321163729p:plain

UE4で管理するデータはこれだけになりました。
最初に比べて随分とシンプルになりましたね!
f:id:PaperSloth:20180321163859p:plain

UE4との連携

UE4ではPluginとしてSVNとの連携がサポートされているため、こちらも活用しましょう。
今度はUE4側での操作となります。
Connect to Source Controlを選択しましょう。
f:id:PaperSloth:20180321164204p:plain

すると下図のようなWindowが開くので
ProviderをSubversion
Repository, UserNameを指定しましょう。
f:id:PaperSloth:20180321164415p:plain

接続がうまく行けばSource Controlアイコンが緑になり、下図のような情報が出てきます。
f:id:PaperSloth:20180321164624p:plain


まとめ

UnityとUE4の実際のバージョン管理を交えつつ紹介させていただきました。
まだまだ紹介しきれていない機能があるんですが
とりあえず個人で簡単にバージョン管理を行う場合や練習としてはこれで必要十分だと思います。

バージョン管理したことがないという人は是非ともこの機会に挑戦してみてください。

また、+や?などのアイコンがファイル上に出ないということがあると思います。
これはDropboxやOneDriveなどのIconOverlayの優先度の問題で結構対処が面倒な問題です。
レジストリを書き換えたりしないといけないこともあります。
subversion icon overlays not showing
でググってみてください。

SVN 初めてのバージョン管理

環境

Windows10
TortoiseSVN 1.9.6


概要

VCSを仕事でも趣味でも使っていなくて、興味はあるけどどうしたらいいか分からない。
そんな人向けのローカルでのバージョン管理をご紹介します。
ローカルでのバージョン管理なので、個人プロジェクトや簡単なテスト環境に使える程度に考えてください。
まずはSVNに慣れるところから始めてみましょう。


TortoiseSVNのセットアップ

TortoiseSVNとは

TortoiseSVNApacheSubversion®のWindows版クライアントのことです。
ようはインストールするとExplorerからSVNの機能を呼び出すことができるようになります。
f:id:PaperSloth:20180321105644p:plain

似たものでTortoiseGitというのもあります。
Gitクライアントは自分のように特殊な理由がない限りはSourceTreeを使用する人が多いです。

TortoiseSVNのダウンロード、インストール

下記URLからTortoiseSVNをダウンロードできます。
合わせて日本語の言語パックもダウンロードしておきましょう。
Downloads · TortoiseSVN

インストール作業自体は双方共にNextをぽちぽちするだけです。
注意点として先にTortoiseSVNからインストールし、あとからLanguagePackをインストールするようにしましょう。

インストールが完了したら下図のようにSettingsを開きます。
f:id:PaperSloth:20180321132754p:plain

General -> Languageから日本語に変更します。
f:id:PaperSloth:20180321135636p:plain
これでメニューが日本語になったら準備完了です。


SVNリポジトリ作成

そもそもリポジトリとは?って人に向けて簡単に説明すると
データの保管庫のようなイメージです。
ここに作業データを格納して、更新したり復元したりを行います。

手順① 空のフォルダを作成する
私はDドライブ直下にExampleRepositoryというのを作成しました。
(ProjectName)Repoとか(ProjectName)Repositoryにすると良いでしょう。

手順② 作成したフォルダをリポジトリにする
図のようにフォルダを選択してここにリポジトリを作成を選択するだけです。
f:id:PaperSloth:20180321170540p:plain

すると下図のようにリポジトリを作成したことを示すダイアログが出ます。
f:id:PaperSloth:20180321133756p:plain
OKを押して閉じます。

これで作業データを保管する保管庫が出来ました。


作成したリポジトリをチェックアウトする

先程の手順では保管庫が出来ただけで今は中身は空です。
使用するにはチェックアウトを行う必要があります。

チェックアウトを行うとリポジトリから管理データをダウンロードすることが出来ます。


実際に作業データを格納するディレクトリに移動し、そこで下図のようにチェックアウトを行います。
f:id:PaperSloth:20180321134425p:plain

すると下図のようなダイアログが出ます。
リポジトリのURLの欄に先程作成したリポジトリのパスを入れます。
この時先頭に file:/// と入力するのを忘れないようにしてください。
チェックアウト先のディレクトリというのは実際に作業データを作成するディレクトリを指します。
f:id:PaperSloth:20180321134647p:plain

チェックアウトを行うと下図のようなダイアログが出ます。
f:id:PaperSloth:20180321134958p:plain

チェックアウトしたフォルダ内には.svnというフォルダがありますが
これは削除しないで残しておいてください。
f:id:PaperSloth:20180321140128p:plain


SVNを使ってみる

TortoiseSVNのメニューだけでも色々あって最初は面食らってしまうと思いますが
とりあえず基本の3つだけを知っていれば最低限は動かせます。
追加、SVNコミット、SVN更新です。

追加
ファイルやディレクトリをリポジトリに追加するコマンドです。
コミットするまではリポジトリには追加されません。

コミット
ローカルの作業コピーの変更をリポジトリに渡し、
リポジトリの内容を更新するコマンドです。

更新
リポジトリからデータを取得するコマンドです。
他の人が行った変更も合わせて取得されます。

試しにExampleCommitText.txtというデータで練習をしてみます。
新規にファイルを作成すると ? アイコンが追加されます。
ファイルを選択し、下図ように追加を選択します。
f:id:PaperSloth:20180321142106p:plain

追加を行うと下図のようにアイコンが ? から + に変わったことが確認できます。
f:id:PaperSloth:20180321142321p:plain

続いてコミットを行うことで初めてリポジトリにデータが追加されます。
下図のようにコミットを行いましょう。
f:id:PaperSloth:20180321142621p:plain

コミットをする際にはメッセージを残す必要があります。
このメッセージは後からログを参照した場合などに
どういう変更を行ったかを明確にするためにきちんと書くことをオススメします。
f:id:PaperSloth:20180321142750p:plain

コミットに成功すると下図のようなダイアログが出ます。
f:id:PaperSloth:20180321142932p:plain

リビジョンが0から1になりましたね。

リビジョンとは
変更をするたびに、リビジョンがリポジトリに作成されます。
変更履歴から作業を戻したい場合などにリビジョンNのような形でリポジトリを調べる時に役立ちます。


自分のPC複数でデータを共有する

ここで紹介する方法は荒業ですが、個人でしか作業していないという場合は役に立つかもしれません。

やり方は簡単でDropboxやOneDriveなどにリポジトリを作成するだけです。
今回はDropboxで作成する方法を紹介します。

Dropboxクライアントをインストールすると
C:\Users\(UserName)\Dropboxにファイルが同期されますね。

そこに新規でリポジトリを作成します。
すうと、同一ユーザーの他のPCでもリポジトリが同期されるため
このリポジトリからチェックアウトすることでファイルを共有することが出来ます。


用語のおさらい

リポジトリ
 データを格納する場所。保管庫のようなもの。
 本来はネットワーク上で管理をする。
・チェックアウト
 空のディレクトリに、リポジトリからデータをダウンロードし
 ローカルの作業コピーを作成するコマンド。
・リビジョン
 コミットをするたびに、リポジトリに作成される。
 変更履歴から作業を戻したい場合などにリビジョンNのような形で指定する。
 作業データの管理履歴IDのようなもの。
・追加
 データをリポジトリに追加するコマンド。
 コミットするまでリポジトリに追加されない。
SVN 更新
 リポジトリから最新データを取得するコマンド。
 他の人が行った変更や別PCで行った作業の最新データを取得する。
SVN コミット
 ローカルの作業コピーの変更データをリポジトリに渡すコマンド。
 リポジトリのリビジョンを新しく作成するのに使用する。


最後に

1回では紹介しきれない機能が沢山あるため、続きを書きます。
次回は無視リスト、ログ表示、差分の見方を紹介する予定です。

冒頭でも軽くふれましたが、VCSを仕事でも趣味でも使っていない人向けの入門記事です。
そのため、仕事ではありえないような運用ですが
やらないよりはマシ、まずはやってみようという内容になっています。

続きを書いたらこちらにもリンクを載せます。
続きを書きました。
SVN ゲームエンジンのプロジェクトを管理しよう - PaperSloth’s diary

UE4 Moduleについて

環境

Unreal Engine 4.17.2
Visual Studio Community 2015


概要

今回はPluginではなく、Moduleの分割でビルドを早くしたりコードの結合度を下げたいとかそういう人向けです。
Plugin開発についての知見も得られると思います。


Moduleの追加

先ずは新規にModuleを追加する方法を説明します。
基本的にはTemplateからのPluginの作成とかで勝手に作られるのですが
BlankでC++プロジェクトを作成した状態から開始します。

今回はModuleSandboxというプロジェクトに
BattleSystemという新規Moduleを追加します。

手順①
追加したいModule名のフォルダをProject/Source以下に作成する。
f:id:PaperSloth:20180305213201p:plain

手順②
[ProjectName].Build.cs
[ProjectName].cpp
[ProjectName].h
を手順①で作成したModuleNameフォルダにコピー
f:id:PaperSloth:20180305213402p:plain

手順③
コピーした
[ProjectName].xxxを
[ModuleName].xxxに変更する
f:id:PaperSloth:20180305213657p:plain

手順④
[ModuleName].Build.csのProjectNameの箇所をModuleNameに変更する

using UnrealBuildTool;

// 変更前:public class ModuleSandbox : ModuleRules
public class BattleSystem : ModuleRules
{
	// 変更前:public ModuleSandbox(ReadOnlyTargetRules Target) : base(Target)
	public BattleSystem(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

		PrivateDependencyModuleNames.AddRange(new string[] {  });
	}
}

手順⑤
[ModuleName].cppのProjectNameの箇所をModuleNameに変更
Moduleのクラスを追加し、Moduleの種類を指定する。

// 変更前:#include "ModuleSandbox.h"
#include "BattleSystem.h"
#include "Modules/ModuleManager.h"

// 追加
class FBattleSystemModule : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return true;
    }
};
// 追加終わり

// 変更前:IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ModuleSandbox, "ModuleSandbox" );

// 今回はGameModuleとして追加する。
// GameModule, Moduleの違いは次の章で解説します。
IMPLEMENT_GAME_MODULE(FBattleSystemModule, "BattleSystem");
// IMPLEMENT_MODULE(FBattleSystemModule, "BattleSystem");

IMPLEMENT_PRIMARY_GAME_MODULEはプロジェクト中に1つしか存在しません。
追加するModuleは全てPRIMARYを外す必要があります。

手順⑥
ExtraModuleNamesに追加Moduleを加える。
[ProjectName].Target.cs
[ProjectName].Editor.Target.cs
上記2つに変更を加えます。変更方法は同じです。

using UnrealBuildTool;
using System.Collections.Generic;

public class ModuleSandboxTarget : TargetRules
{
	public ModuleSandboxTarget(TargetInfo Target) : base(Target)
	{
		Type = TargetType.Game;

		// 変更前:ExtraModuleNames.AddRange( new string[] { "ModuleSandbox" } );
		ExtraModuleNames.AddRange( new string[] { "ModuleSandbox", "BattleSystem" } );
	}
}


手順⑦
uprojectにModuleを登録する。
[ProjectName].uprojectをテキストエディタで開き、追加Moduleの設定を加える。

{
    "FileVersion": 3,
    "EngineAssociation": "4.17",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "ModuleSandbox",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        },
        // 追加分
        {
            "Name": "BattleSystem",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
        // 追加終了
    ]
}

手順⑧
Solutionの更新
.uprojectを右クリックし、Generate Visual Studio project filesでslnの更新を行う。
f:id:PaperSloth:20180305215306p:plain


エラーが出なければこれでModuleの追加が出来ています。
ソリューションを開いてSource以下に追加されていれば成功です。
f:id:PaperSloth:20180305215549p:plain


GameModuleとModuleの違い

この辺りは少し面倒な話でUE4 C++本でも
プラグインに対してのホットリロードには難ありというのが現時点での正直な感想です"と書かれています。

私はあまり不便だと感じていませんが、それはさて置き
先に結論だけ述べます。
GameModuleの場合はHotReloadの際にビルドに含まれます。
Moduleの場合はCompileボタンを押してもHotReloadが行われません。

Moduleの場合はWindow -> Developer Tools -> Modulesウィンドウから行う必要があります。
f:id:PaperSloth:20180305221125p:plain
Recompileボタンを押下することでHotReloadが行われます。

他にも幾つか違いがあるとは思いますが、私が触ってみて分かった範囲について記述させていただきました。


PluginでもProjectの別Moduleの場合でも開発中にModuleとGameModuleを切り替えることが可能です。
ビルド時間をとりあえず短縮したい場合はModuleとして作成すれば良いと思われます。
Pluginの場合はModuleとして作成されます。

GameModuleの場合

#include "[ModuleName].h"
#include "Modules/ModuleManager.h"

class F[ModuleName]Module : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return true;
    }
};
IMPLEMENT_GAME_MODULE(F[ModuleName]Module, "[ModuleName]");


Moduleの場合

#include "[ModuleName].h"
#include "Modules/ModuleManager.h"

class F[ModuleName]Module : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return false;
    }
};
IMPLEMENT_MODULE(F[ModuleName]Module, "[ModuleName]");

UE4 マウスカーソルを自作アイコンにする

環境

Unreal Engine 4.18.3


マウス座標の取得

Widgetでマウス座標を取得するには下記の2つのノードがあります。
・Get Mouse Position
・Get Mouse Position Scaled by DPI
f:id:PaperSloth:20180206195202p:plain

基本的にはGet Mouse Position Scaled by DPIで良いです。

画面解像度が固定でDPIスケールが1.0の場合や
DPIスケールの値が変わらないようにしている場合など
特殊な場合はGet Mouse Positionを使う場合もあると思います。

DPIスケールは画面解像度の変更に合わせてUIのサイズを変更してくれる機能です。
例えば、DPI Scaleが1.0の場合はGet Mouse Positionで問題ありませんが
初期設定では1280x720でWidgetを編集しており、DPI Scaleは約0.66です。
この値はProjectSettings -> Engine -> User Interfaceからカーブエディタで自由に編集できますが
基本的には初期設定のまま開発を行うでしょう。
初期値は1920x1080では1.0で3840x2160の場合は2.0が設定されています。
f:id:PaperSloth:20180206195021p:plain

この辺りの解像度の変化の違いをユーザー側が気にせず取得できるのが
Get Mouse Position Scaled by DPIとなっています。


マウスカーソルの変更

今回はマウスカーソルのみのWidget(WBP_MouseCursor)と
それを使用するWidget(WBP_MouseMove)の2つのWidgetを用意します。
f:id:PaperSloth:20180206195421p:plain

マウスカーソルのみのWidgetは下記のようにImageを1つ置いただけのWidget
Blueprint Graphには特に変更を加えません。
今回は特に素材を用意していないので、ポイントライトのテクスチャを使用しています。
f:id:PaperSloth:20180206195342p:plain

因みにマウスカーソルのみのWidget(WBP_MouseCursor)を作成してパーツとして分けていますが
今回の実装内容的にはWBP_MouseMoveにImageを配置する実装でも問題ありません。

もう一つのWidget(WBP_MouseMove)には先ほど作成したWidgetを配置します。
Palette内にUser Created -> WBP_MouseCursor(自作Widget名)があることを確認します。
f:id:PaperSloth:20180206195740p:plain

今回はマウスカーソルのアイコンがマウスの中心にきてほしいため
Allignmentには0.5. 0.5を代入してマウスカーソルの中心にアイコンがくるように補正しています。
f:id:PaperSloth:20180206195918p:plain
座標は画面外でもどこでもお好きなように。

続いてBlueprintの実装に移ります。
一旦は作成したWidgetを描画します。
ここでは確認のためにLevel Blueprintで行っています。
f:id:PaperSloth:20180206200024p:plain

続いて先ほど作成したWidgetのBlueprint Graphに移ります。
先ずは動作確認用に一旦マウスカーソルを表示することにします。
f:id:PaperSloth:20180206200141p:plain

ここまでで実行してアイコン画像とマウスカーソルが表示されていることを確認します。
f:id:PaperSloth:20180206200508p:plain


マウスカーソルの追従

マウスカーソルの座標を更新するには
アイコンの座標にMouse座標を代入するだけですね。

下記のようなノードになります。
f:id:PaperSloth:20180206200828p:plain

実行すると下記のようにアイコンがマウスの位置に合わせて動くことが確認できます。
※キャプチャーツールの都合上、マウスカーソルは表示されていません。


続いてマウスカーソルの位置に徐々に近づくような実装も紹介しておきます。
Interp Toノードで徐々に値を更新することで実現可能です。
f:id:PaperSloth:20180206201540p:plain

動画が分かりにくいのですが、実行してみるとこんな感じです。

今回は適当に5という値を入れましたが
変数に昇格させて場面によって速度を変えたりオプション画面から変更できると便利そうですね。

最後に動作確認ができたので、不要であればShow Mouse Cursorを削除しておきます。
以上で完成です。

参考資料

DPIスケールについて
https://docs.unrealengine.com/latest/JPN/Engine/UMG/UserGuide/DPIScaling/index.html

Mouse座標の取得について
[UE4] Input入門|株式会社ヒストリア


マウスカーソルの表示方法についてはPlayer Controllerを継承したBlueprintから変更する方法とBlueprintからアクセスする方法の2つがあります。

Blueprintから変更する方法について
http://kagring.blog.fc2.com/blog-entry-122.html

PlayerControllerを継承して変更する方法について
https://docs.unrealengine.com/ja/Resources/ContentExamples/MouseInterface/MouseControlSetup/index.html

おまけ

おかずさんがもっと便利な作成な方法について教えてくださいました。
https://twitter.com/pafuhana1213/status/960869841598013440?s=20

UE4 リフレクションに指定できる型について

環境

Unreal Engine 4.17.2

概要

Blueprintに公開可能な型と公開できない型を書いていきます。
他にも追記した方がいいことがあれば、コメントやTwitterで指摘いただけると助かります。
UPROPERTYで説明していますが、UFUNCTIONでも基本的に同様です。

enumUENUMを付ければ公開可能です。
structはUSTRUCTを付けてBlueprintTypeが指定されていれば公開可能です。

コードは下記クラスに追加していきます。
因みにUPROPERTYでCategoryを指定しない場合はクラス名がカテゴリ名になります。

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ReflectionActor.generated.h"
 
UCLASS()
class PROJECTNAME_API AReflectionActor : public AActor
{
    GENERATED_BODY()
public:
    AReflectionActor();
private:
    // ここに変数を追加
};



Blueprintに公開可能な型

公開可能なプリミティブ型

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
bool _bool;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint8 _uint;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int32 _int;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
float _float;

f:id:PaperSloth:20180131234057p:plain


公開可能なUE4の型

ここではよく使う型だけを紹介します。
他にも挙げきれないくらい種類があります。
一部の構造体とUObjectを継承しているクラスポインタが公開可能です。

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FName name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FString string;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FText text;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FVector vector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FRotator rotator;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FTransform transform;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
UObject* _object;

f:id:PaperSloth:20180131234302p:plain

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FMatrix matrix;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FQuat quat;

Matrix, QuaternionはBlueprintへの公開は可能です。
f:id:PaperSloth:20180201005332p:plain
ですが、これらを操作するための関数はBlueprintにはほとんど公開されていません。

公開可能な構造体、列挙型

USTRUCT(BlueprintType)
struct FFoo
{
    GENERATED_USTRUCT_BODY()
 
    float _float;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 _int;
};
 
// 省略
 
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FFoo foo;


この場合はUPROPERTYを公開設定で付与した_intのみがBlueprint上に公開されます。
f:id:PaperSloth:20180201005640p:plain

UENUM()
enum class EBar : uint8
{
    State1,
    State2,
    State3,
    Num        UMETA(Hidden)
};
 
UENUM()
namespace EBaz
{
    enum Type
    {
        State1,
        State2,
        State3,
        Num        UMETA(Hidden)
    };
}
 
// 省略
 
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
EBar bar;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TEnumAsByte<EBaz::Type> baz;


enumの書き方を2種類紹介しましたが、Blueprintからの見え方は同じです。
f:id:PaperSloth:20180201005828p:plain
EBazのような古いenumの書き方を示したのには理由があります。
UE4のエンジンコードにはまだ古い書き方が残っているため
それらを利用する際にTEnumAsByteを使用します。
自身で列挙型を定義する際は上のEBarのような書き方をオススメします。
詳しくはこちら
papersloth.hatenablog.com

公開可能なcontainer

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TArray<int32> _array;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TMap<int32, int32> map;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TSet<int32> set;

ここではintを指定していますが
公開可能なプリミティブ型、構造体、クラスポインタは基本的に使用可能です。
f:id:PaperSloth:20180201010039p:plain

また、Map, SetのBlueprintでの扱いに関してですが
4.13以下では使用不可
4.14では実験的な機能
4.15から正式なサポートとなります。


Blueprintに公開不可能な型

これらはC++側では当然使用可能ですが、Blueprintへの公開はできません。
UPROPERTY, UFUNCTIONを付与するとエラーとなります。
Blueprintで使用したい場合はcastするなどの対応が必要です。
数が多いため、一部のみを抜粋しています。

プリミティブ型

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint16 _uint16;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint32 _uint32;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint64 _uint64;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int8 _int8;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int16 _int16;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int64 _int64;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
double _double;


UE4で定義された型、container

#include "Containers/StaticArray.h"
 
// 省略
 
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FMatrix2x2 mat22;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TStaticArray<int32, 10> staticArray;



コード全容

// Copyright(c) 2018 PaperSloth

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Containers/StaticArray.h"
#include "ReflectionActor.generated.h"
 
USTRUCT(BlueprintType)
struct FFoo
{
    GENERATED_USTRUCT_BODY()
 
    float _float;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 _int;
};
 
UENUM()
enum class EBar : uint8
{
    State1,
    State2,
    State3,
    Num        UMETA(Hidden)
};
 
UENUM()
namespace EBaz
{
    enum Type
    {
        State1,
        State2,
        State3,
        Num        UMETA(Hidden)
    };
}
 
UCLASS()
class PROJECTNAME_API AReflectionActor : public AActor
{
    GENERATED_BODY()
public:
    AReflectionActor();
private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FFoo foo;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    EBar bar;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TEnumAsByte<EBaz::Type> baz;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    bool _bool;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint8 _uint;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int32 _int;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    float _float;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FName name;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FString string;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FText text;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FVector vector;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FRotator rotator;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FTransform transform;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    UObject* _object;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FMatrix matrix;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FQuat quat;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TArray<int32> _array;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TMap<int32, int32> map;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TSet<int32> set;
 
#if 0
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint16 _uint16;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint32 _uint32;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint64 _uint64;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int8 _int8;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int16 _int16;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int64 _int64;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    double _double;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FMatrix2x2 mat22;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TStaticArray<int32, 10> staticArray;
#endif
};

f:id:PaperSloth:20180201010405p:plain

UE4 ヒットストップの実装について

※1点修正があったため追記しました。(2018/01/31 1:22)
詳しくはページ下部をご覧ください。

環境

Unreal Engine 4.17.2

Time Dilationについて

ヒットストップの実装に関してはGlobal Time Dilationの値を変更するだけで簡単に実装ができます。
Time Dilationの説明について特に知る必要がないという人は読み飛ばしてください。

ここではTime Dilationの種類等について解説します。
Time Dilationは初期値が1.0です。
デフォルトの設定では
最小値は0.0001
最大値は20.0
となっています。
1を超える値を設定した場合、例えば2.0の場合は2倍速になることを表しています。
逆に1未満の場合、例えば0.5の場合は通常の1/2の速度になることを表しています。

基本的には下図のようなCustom Time DilationとGlobal Time Dilationを使用します。
f:id:PaperSloth:20180128231743p:plain

Custom Time Dilationとは

Custom Time DilationはActorに設定されたActorごとの速度を表します。
定義は下記のActor内に記述があります。
Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
Engine/Source/Runtime/Engine/Private/Actor.cpp

Actorの更新呼び出しの中で
下図のように使用されています。

Target->TickActor(DeltaTime * Target->CustomTimeDilation, TickType, *this);	

Global Time Dilationとは

これは名前にGlobalとあるようにゲーム中の更新に関わります。
定義箇所は下記です。
Engine/Source/Runtime/Engine/Classes/GameFramework/WorldSettings.h
Engine/Source/Runtime/Engine/Private/WorldSetting.cpp

因みにC++側で使用する際はほとんどの場合では
代わりにGetEffectiveTimeDilationを使用してください。という警告があります。

先程最小値は0.0001で最大値は20.0と書きましたが、この設定は変更することが出来ます。
f:id:PaperSloth:20180128232843p:plain

World Setteingsタブの最下部に隠れたパラメーターとして
Min Global Time Dilation, Max Global Time Dilationがあります。
BlueprintからSet Global Time Dilationを呼び出した際にはこの値を参照してClampされています。
そのため、厳密には0を指定しても0.0001が設定されるため、完全に更新が停止するというわけではありません。

ヒットストップの実装

前置きが長くなりましたが、ヒットストップの実装は至って簡単です。
今回はMacroLibraryとして作成したものを例に紹介します。
親クラスにはActorを指定しています。

実装にはたった5つのノードを呼び出すだけの非常にシンプルな実装となっています。
f:id:PaperSloth:20180128233845p:plain

図に説明があるように
Global Time Dilationにどれくらいゆっくりにしたいかの値を設定します。
0以上1未満の値でゲーム全体をゆっくりにすることが出来ます。

次にDelayで元の早さに戻るまでの時間を設定します。

呼び出し例としては下記のような感じです。
ここではわかりやすくするためにDelayに渡す値をStop Timeという名前にしています。
通常の1/10の速度に変更し、0.15秒後に元の早さに戻るように設定しています。
※0.15秒ではなく、1.5秒後になります。
詳しくは追記したページ下部の説明をご参照ください。
f:id:PaperSloth:20180128234145p:plain

実際にヒットストップを使用した感じは下記のような感じになります。
ここでは敵の撃破時に使用しています。
容量の都合上、10FPSのGifで分かりにくいですが、こんな動きになります。
f:id:PaperSloth:20180130223753g:plain

※1点誤りがあったため、追記です。
ブログ公開後に指摘をいただきましたように
Delayノードも時間の進みが遅くなります。

これは最初、気付かずに実装すると少しハマってしまうので注意が必要です。
Global Time Dilationの影響を受けるノード
・Delay
Global Time Dilationの影響を受けないノード
・Set Timer by Event


以上のように5つのノードだけで簡単にヒットストップが実装できます。
先程の例のようにMacroLibraryにしておくと便利です。
その時はActor以上を継承したものを選択しましょう。
また、FunctionLibraryでは実装できません。
理由としてはDelayなどのLatentノードの呼び出しができないからです。
ゲームのコンボや撃破時の演出など活用できる場面は幾つかあると思います。
是非試してみてください。