PaperSloth’s diary

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

UE4 IDEの変更方法

環境

UE4.19.1
Visual Studio 2017 Community


IDEの変更方法について

Unreal C++IDEMacではXCode, WindowsではVisual Studioを使用することが多いと思います。

IDEの変更方法はEditor PreferencesのSource Codeから変更します。
f:id:PaperSloth:20180422174833p:plain

Visual Studio 2017 Communityをインストールしたので、この機会に変更してみました。
f:id:PaperSloth:20180422175049p:plain

因みにこの後にOpen Visual Studio 2017を選択しても
プロジェクトをVisual Studio 2015で作成していたらVisual Studio 2015が開きます。
f:id:PaperSloth:20180422175229p:plain

Refresh Visual Studio 2017 Projectを選択してソリューションを更新します。
f:id:PaperSloth:20180422175351p:plain

これでプロジェクトでVisual Studio 2017を使用出来るようになります。
Visual Stuidio Code等他のIDEを使用したい場合もここから変更出来ます。

UE4 C++でGameModeの初期化を行う方法

環境

UE4.19.1
Visual Studio 2015 Community


GameModeで登録する初期値の初期化について

ここの部分の初期化についてです。
f:id:PaperSloth:20180422161755p:plain

C++で作成したGameModeBaseだと編集できないのですが
Blueprintだと楽に編集ができますね。
ここをC++側で編集する方法について書きます。

GameModeBaseに登録されているのは下記のような処理です。

AGameModeBase::AGameModeBase(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer.DoNotCreateDefaultSubobject(TEXT("Sprite")))
{
    // 省略

    DefaultPawnClass                     = ADefaultPawn::StaticClass();
    PlayerControllerClass                = APlayerController::StaticClass();
    PlayerStateClass                     = APlayerState::StaticClass();
    GameStateClass                       = AGameStateBase::StaticClass();
    HUDClass                             = AHUD::StaticClass();
    GameSessionClass                     = AGameSession::StaticClass();
    SpectatorClass                       = ASpectatorPawn::StaticClass();
    ReplaySpectatorPlayerControllerClass = APlayerController::StaticClass();
    ServerStatReplicatorClass            = AServerStatReplicator::StaticClass();
}

例えばGameModeBaseを継承したGameModeでnullptrを設定するとNoneになります。

AExampleGameModeBase::AExampleGameModeBase()
{
    DefaultPawnClass = nullptr;
}

f:id:PaperSloth:20180422163144p:plain

継承したクラスを登録したい場合は下記のようになります。

#include "ExampleGameModeBase.h"
#include "ExampleCharacter.h"
#include "ExamplePlayerController.h"

AExampleGameModeBase::AExampleGameModeBase()
{
    DefaultPawnClass      = AExampleCharacter::StaticClass();
    PlayerControllerClass = AExamplePlayerController::StaticClass();
}

f:id:PaperSloth:20180422170729p:plain

UE4 C++で複数実行ピンの作成

環境

UE4.19.1
Visual Studio 2015 Community


概要

いくつかやり方があって、今回は一番簡単でお手軽なenumを使ったやり方を紹介します。


enumを使った関数のおさらい

先ずは下記のようなenumを定義します。

UENUM(BlueprintType)
enum class EErrorCode : uint8
{
    Success,
    Failed
};

続いてUFUNCTIONマクロを使用してBlueprintに関数を公開します。

UFUNCTION(BlueprintCallable, Category = "Example")
void ExampleEnumInput(const EErrorCode ErrorCode);

// 関数内でErrorCodeの値が変わる関数を想定
UFUNCTION(BlueprintCallable, Category = "Example")
void ExampleEnumOutput(EErrorCode& ErrorCode);

constなしの参照にすることで引数が出力ピンに変わります。
constをつけた参照の場合は引数が入力ピンになります。

Blueprintから先程の関数を呼び出すと下図のようになります。
f:id:PaperSloth:20180422143212p:plain


enumを使った複数実行ピンの作成方法

さて、ようやく本題です。
enumを使って複数の実行ピンを使用するにはUFUNCTIONのmetaに下記を追加することで実現可能です。

UFUNCTION(/*省略*/,  meta = (ExpandEnumAsExecs = "ArgumentName"))

ArgumentNameの部分は任意の引数名を入力します。
実際に使用する引数名と一致するようにします。

先程の関数に手を加えて下記のように変更しました。

UFUNCTION(BlueprintCallable, Category = "Example", meta = (ExpandEnumAsExecs = "ErrorCode"))
void ExampleEnumMultiInput(const EErrorCode ErrorCode);
UFUNCTION(BlueprintCallable, Category = "Example", meta = (ExpandEnumAsExecs = "ErrorCode"))
void ExampleEnumMultiOutput(EErrorCode& ErrorCode);

Blueprint側から呼び出すと下記のようになります。
f:id:PaperSloth:20180422144216p:plain

enumで登録した値が全て実行ピンになっていますね。
後はC++側でswitchとかで処理を分けると良さそうです。

また、enumで定義を追加するとノードも変更されます。

UENUM(BlueprintType)
enum class EErrorCode : uint8
{
    None,
    Success,
    InternalError,
    ExternalError,
    NetworkError,
    UnknownError,
};

Noneを定義した場合は実行ピンには名前が出ません。
f:id:PaperSloth:20180422152849p:plain


簡単な応用

もう少し気の利いた処理になるように関数の中身も書いてみます。

Successの時、Failedの時でそれぞれ呼び出す関数を登録します。
C++だけで処理を実装するのは直ぐにできますが
今回はC++で関数の定義をしてBlueprintでロジックを実装する形にしてみましょう。

先ずは呼び出す関数の登録を行います。

UFUNCTION(BlueprintImplementableEvent, Category = "Example")
void OnSuccess();
UFUNCTION(BlueprintImplementableEvent, Category = "Example")
void OnFailed();

成功時、失敗時のそれぞれの関数を用意しました。
BlueprintImplementableEventはC++側で定義してBlueprintで関数のロジックを実装するときに使用します。
この定義だとBlueprintで処理の記述はできますが、関数呼び出しはC++からしか呼び出せません。
Blueprintからも呼び出したい場合は、BlueprintCallableと組み合わせることで使用できます。

複数実行ピンを入力できる関数で呼び出しを行うように処理を記述します。

void AExampleActor::ExampleEnumMultiInput(const EErrorCode ErrorCode)
{
    switch (ErrorCode)
    {
        case EErrorCode::Success:
            OnSuccess();
            break;
        case EErrorCode::Failed:
            OnFailed();
            break;
    }
}

これでBlueprint側で処理の登録と呼び出しを行ってみましょう。
下図のように組むとSuccessに実行ピンを繋ぐとOnSuccessが呼ばれ
Failedに実行ピンを繋ぐとOnFailedが呼びだされます。
f:id:PaperSloth:20180422150809p:plain

こういった処理を書く場合はDelegateで登録することが多いですが
今回は複数実行ピンを使用したイメージとして用意してみました。


また、余談ですがMultiOutput関数は
下図のようにOutputをSwitch分岐させるのと同様の処理になります。
ですが、C++側で何か処理を加えたい場合に役立ちますし、ノードもスッキリするため
ExpandEnumAsExecs を積極的に使用した方が良いでしょう。
f:id:PaperSloth:20180422151304p:plain

UE4 SpawnActorDeferredについて

環境

UE4.19.0
Visual Studio Community 2015

SpawnActorについて

SpawnActorを呼び出すにはWorld, 生成位置、回転情報、名前や所有者の登録等のオプションが必要になります。

#include "Engine/World.h"
#include "ExampleActor.h"

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

    auto const World = GetWorld();
    const FTransform SpawnTransform = FTransform::Identity;
    FActorSpawnParameters parameters;
    auto const exampleActor = World->SpawnActor<AExampleActor>(AExampleActor::StaticClass(), SpawnTransform, parameters);
}

基本的にはこれで問題ないのですが
BeginPlayが呼び出される前に処理を行いたい場合や値を渡したいという場合もあります。

そんな場合に役立つのがSpawnActorDeferredです。

SpawnActorDeferredについて

一先ずコードから見ていきましょう。

#include "Engine/World.h"
#include "ExampleActor.h"

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

    auto const World = GetWorld();
    const FTransform SpawnTransform = FTransform::Identity;
    auto const exampleActor = World->SpawnActorDeferred<AExampleActor>(AExampleActor::StaticClass(), SpawnTransform);

    // BeginPlayが呼び出される前に処理したい初期化処理等
    exampleActor->Initialize();

    // ここでBeginPlayが呼び出される
    exampleActor->FinishSpawning(SpawnTransform);
}

SpawnActorDeferredを利用する機会は何かしら作っていると何度も出てきます。
頭の片隅に入れておくと便利です。

UE4 C++でのExposeOnSpawnについて

環境

UE4.19.0
Visual Studio Community 2015

Expose On Spawnとは

Spawn Actor from ClassでActorをSpawnする際に初期化できる変数のことです。
f:id:PaperSloth:20180413230104p:plain

Blueprintで設定する際は下図のように設定を行います。
f:id:PaperSloth:20180413230236p:plain

Expose on Spawnにチェックを入れるのはもちろんですが
Instance Editableにもチェックを入れる必要があります。

チェックを忘れた場合はコンパイル時に警告となります。


さて、これをC++側で定義する方法についてです。

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game", meta = (ExposeOnSpawn = true))
    float ExampleExposeValue;

このようにmetaにExposeOnSpawnと書くだけでSpawn時に値を割り当てることができます。
f:id:PaperSloth:20180413232056p:plain

    meta = (AllowPrivateAccess = "true")

このようにmeta指定子の種類によってはtrueを文字列として囲うものもあるので、ちょっとした注意が必要です。

また、C++側で生成時に値を割り当てる時はSpawnActorDeferredを使用しますが、それはまた別の機会に紹介させていただきます。
SpawnActorDeferredについて書きました。
こちらも合わせて読んでおくと便利です。
papersloth.hatenablog.com

UE4 Unreal C++でデストラクタ

環境

UE4.19.0
Visual Studio 2015 Community


参考文献

下記ドキュメントはUE4を触っている人は必ず目を通しておくべきだと思います。
今回はこのドキュメントを参考にActorの解放処理についてみていきます。
Unreal Engine | アクタのライフサイクル


UE4でのデストラクタ

結論から書くとデストラクタはUObjectを継承したクラスでは基本的に使用しません。

class PROJECTNAME_API AExampleActor : public AActor
{
	GENERATED_BODY()
public:	
	AExampleActor();
	// 下記はNG
	virtual ~AEampleActor();	

UObjectを継承したクラスではBeginDestroyをOverrideすると良さそうです。
これはオブジェクトがGC用にマークされた状態でも、まだ削除されていないときに呼び出されるものです。
ゲームプレイ機能は後述のEndPlayで解放し、BeginDestroyでオブジェクトが使用していた全リソースを解放します。

// header側
class PROJECTNAME_API AExampleActor : public AActor
{
	GENERATED_BODY()
public:	
	AExampleActor();
	virtual void BeginDestroy() override;
}

// cpp側
void AExampleActor::BeginDestroy()
{
	Super::BeginDestroy();
}


AActorを継承したクラスで明示的なDestroy呼び出し等による終了処理を記述したい場合にはEndPlayを使用します。

// header側
class PROJECTNAME_API AExampleActor : public AActor
{
	GENERATED_BODY()
public:	
	AExampleActor();
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
}

// cpp側
void AExampleActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
}


他にも終了処理の過程にはDestroy関数やOnDestroyのようなデリゲートもありますが
ドキュメントを見た限りでは特別な理由がなければ終了処理はEndPlayで良さそうです。

また、ReceiveEndPlayなどのReceive~系はC++側の処理が呼ばれる際に合わせて呼び出されるBlueprint側の処理になります。
Blueprint側のEndPlay等は実際にはReceiveEndPlayを使用しています。


まとめ

終了処理を明示的に記述したい場合はEndPlay
デストラクタの代替機能が欲しい場合はBeginDestroy

UObjectを継承したクラスでは基本的にデストラクタを記述する必要はありません。


また、今回は解説しませんがUnreal C++でもどうしてもC++のnewを使いたいことがあるかもしれません。
そういった特殊なケースの場合はRuntime/Core/Public/HALUnrealMemory.hに目を通しておくと幸せになれるかもしれません。
mallocやfree等々メモリーに関する処理が幾つか用意されています。

UE4 Niagara Rendererについて

環境

UE4.19.0


Rendererの種類について

先ず新規Emitterを作成すると下図のようなエフェクトが生成されます。
f:id:PaperSloth:20180402205657p:plain

これはSprite Rendererでパーティクル作成でよく使用される基本形です。
SubUV Indexを利用することで煙や炎も表現することができます。

NiagaraのRendererの種類は全部で4種類あります。
・Light
使用について未検証のため、詳細が分かり次第追記予定です。
・Mesh
Static Meshをパーティクルに利用できるものです。
SFチックな盾や爆発の破片などに利用され、Spriteの次に利用されると思います。
Ribbon
リボン形状のもので発生源から終点への軌跡を描きます。
綺麗なリボン上のものから複雑な形状まで表現できます。
・Sprite
パーティクルの基本形で最も利用されるものです。


Rendererの追加方法はEmitter, Systemの最下部にRenderカテゴリがあるため
そこの + ボタンから追加することができます。
f:id:PaperSloth:20180402210653p:plain

"+"ボタンを押下すると下図のようにRenderer Properties一覧が出るので
使用したいRendererを選択しましょう。
f:id:PaperSloth:20180402210728p:plain


Mesh Renderer

試しにMesh Rendererを追加してCubeを設定してみました。
f:id:PaperSloth:20180402210911p:plain

f:id:PaperSloth:20180402211004p:plain

また、設定したMeshのOverride Materialsを有効にすることで
Materialを変更することができます。
f:id:PaperSloth:20180402211156p:plain

f:id:PaperSloth:20180402211309p:plain


簡単にですが、Mesh Particleでシールドを作ってみました。
動きの制御はSpriteの時と同じなので、使いやすいですね。


Ribbon Renderer

Default EmitterにRibbon Rendererを割り当ててみました。
f:id:PaperSloth:20180402212541p:plain

f:id:PaperSloth:20180402212646p:plain

NiagaraPlugin内にDefaultRibbonMaterialがあるので、これを利用するとRibbonの基本的な動きは表現できそうです。

また、RibbonではEmitter自体はSpawn位置を少しばらつかせるだけで下図のようになります。
f:id:PaperSloth:20180402213616p:plain

これにBlueprintで簡単な移動処理を加えるだけで面白い移動の軌跡を表現できます。


NiagaraSystem, Actorについてのプログラマー向け小ネタ

Blueprintとの連携については以前に記事を書きました。
papersloth.hatenablog.com

Niagara Systemについて

Niagara SystemはLevel上に配置が可能ですね。
これは配置した時にNiagara Actorが作成されて、その下にNiagara Componentがアタッチされます。
また、Niagara System自体はUObjectを継承したクラスなので厳密にはNiagara Systemは配置していません。
f:id:PaperSloth:20180402214503p:plain

Niagara Actorについて

f:id:PaperSloth:20180402215311p:plain
Niagara Actorの継承関係を見るとActorを継承して作られていますね。

Niagara Actorを継承してBlueprintを作成すると
Niagara Component
・Bliiboard Component
・Arrow Component
の3つがアタッチされていますね。
f:id:PaperSloth:20180402215657p:plain

Niagara Actor側のソースコードを見ると分かるのですが
ほとんどEditor側でのみ有効になっています。
Runtimeで有効なのはNiagara Componentぐらい。

そのため、動作的にはActorを継承したBlueprintに
Niagara Componentをアタッチするのに近いですね。
f:id:PaperSloth:20180402220110p:plain