PaperSloth’s diary

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

UE4 不要なComponentを継承先で作成させない方法

環境

・Unreal Engine4.20.3

不要なComponentを継承先で作成させない方法

例えばCharacterクラスを継承したいけど、Skeletal Meshを使用しないためMeshを継承させたくない場合

愚直に実装をするとctor内でこう書くと思います。

AExampleCharacter::AExampleCharacter()
{
    if (GetMesh())
    {
        GetMesh()->DestroyComponent();
    }
}

もちろんこうすることで継承先のCharacterからComponentを破棄することはできます。

ですが、もう少しスマートに書く方法があります。
普段あまり使用しないかもしれませんがObjectInitializerを使用します。

// AExampleCharacter.h
UCLASS()
class EXAMPLE_API AExampleCharacter: public ACharacter
{
    GENERATED_BODY()

public:
    AExampleCharacter(const FObjectInitializer& ObjectInitializer);
};

// AExampleActor.cpp
AExampleCharacter::AExampleCharacter(const FObjectInitializer& ObjectInitializer)
  : Super(ObjectInitializer)
{
}

ObjectInitializerからはDoNotCreateDefaultSubobjectというメソッドが提供されており
不要なComponentの作成を防ぐことができます。

今回はMeshを作成させたくないため、MeshComponentの名前を指定して作成を防ぎます。

AExampleCharacter::AExampleCharacter(const FObjectInitializer& ObjectInitializer)
  : Super(ObjectInitializer.DoNotCreateDefaultSubobject(MeshComponentName)),
{
}

同様にCharacterMovementComponentの作成を防ぐ場合は
CharacterMovementComponentNameを指定し

Collisionの作成を防ぐ場合は
CapsuleComponentNameを指定します。


こうすることでSkeletal Meshを使用しないCharacterを作成できます。
他にもCapsule Collision以外を持ったCharacterや
自前のMovementComponentを持たせたCharacterを作成することができます。
f:id:PaperSloth:20181106165119p:plain


また、前回の記事でTickについて書きましたが
Skeletal Mesh ComponentもTickComponentが走っているため、使用しない場合は作成を防止したほうが良いでしょう。
papersloth.hatenablog.com

UE4 無駄なTickを省く方法

環境

・Unreal Engine4.20.3

Tickが動作しているActor一覧の確認

Console Commandの「dumpticks」でTickが動作しているActorやComponentの一覧が取得できます。
するとOutput Logに一覧が表示されます。
しかし、これだと通常のログと混ざって少し見にくいため、私はSession Frontendを使用しています。
f:id:PaperSloth:20181031225051p:plain

Session FrontEndはWindow -> Developer Tools -> Session Frontendから開けます。

試しにThirdPersonTemplate(Blueprint)で動作を試してみます。
画像が見にくいですが、概ね見るとすれば赤枠で囲った範囲かと思います。
f:id:PaperSloth:20181031225629p:plain

この中でも特にHUDを使用している人は少ないと思いますので、無駄にTickを走らせていることが分かります。

Tickを止める方法

①Blueprintの場合

Actor(例えばHUDやCharacterの場合)は
Actor Tickというカテゴリの中にStart with Tick Enabledというフラグがあるため
これをオフにすることで無効にできます。
f:id:PaperSloth:20181031225919p:plain

同様にComponentの場合も
Component Tickというカテゴリの中にStart with Tick Enabledというフラグがあるため
これをオフにすることで無効にできます。
f:id:PaperSloth:20181031230429p:plain

C++の場合

今回はActorを継承して新規作成したクラスを例に見てみます。


さて、このActorのctor内で

PrimaryActorTick.bCanEverTick = true;

とあるので、これをfalseにするだけでこのActorのTickを無効にすることができます。

AExampleTickActor::AExampleTickActor()
{
    PrimaryActorTick.bCanEverTick = false;
}

しかし、これには少し罠があります。
このActorを継承したBlueprintを作成しTickノードに何か処理を繋ぐとC++側もBlueprint側もTickが動いてしまいます。
ただし、Tickがゴーストノードの場合はTickが動くことはありません。

とはいえこんな不安な状態のまま開発を行いたくはありません。
その場合は、先程Blueprintで変更したのと同じStartWithTickEnabledをC++側からfalseにしてやりましょう。

AExampleTickActor::AExampleTickActor()
{
    PrimaryActorTick.bCanEverTick = false;
    // BlueprintでActorTickカテゴリにあるフラグ
    PrimaryActorTick.bStartWithTickEnabled = false;
}

これでこのActorのTickをしっかりと止めることができました。

追記

PrimaryActorTick.bCanEverTickのフラグですがAActorクラス内ではfalseになっています。
そのため、EmptyActorをLevelに配置した場合や
Actorを継承したBlueprintでもTickがゴーストノードの場合や削除されている場合はTickが呼び出されません。
明示的に呼び出しを無効化したい場合はStart With Tick Enabledをfalseにすることをオススメします。

UE4 Characterの多段ジャンプについて

環境

・Unreal Engine4.20.3

多段ジャンプの実装方法

Characterクラス内に「Jump Max Hold Time」と「Jump Max Count」というパラメーターがある。
f:id:PaperSloth:20181101172105p:plain
GitHubのログを見た限りではUE4.2の頃からあるようだ。
CharacterMovementComponentではなくCharacterに定義されているため、気付きにくく調整の際には注意が必要。
落下速度や空中での入力の効き具合などの細かい調整はCharacterMovementComponent側で行う。


Jump Max Hold Timeはジャンプボタンを押し続けると指定した時間の間上昇させることができる。
ジャンプの入力時間に応じて上昇量が変わるため
いわゆるマリオジャンプを簡単に実装できる。
ジャンプ力やゲームデザインにもよるが0.3以下くらいがちょうど良さそう。


次にJump Max Countはその名のとおり多段ジャンプの最大数である。
例えば2にしてやれば接地判定が動くまでの間に2段ジャンプを行う事ができる。
試しに100にしてみたが正しく動作しており、int32に収まる範囲なら何度でも跳べそう。
Windowsの場合は2,147,483,647段ジャンプができる。
データ型の範囲



また、両方のパラメーターを組み合わせることもできるため
多段ジャンプの度にプレイヤーがジャンプ量を調整するゲームというのも可能ではある。

UE4 C++でTimerを使用した処理の書き方

環境

Visual Studio Community 2017
・Unreal Engine4.20.3

SetTimer by Eventについて

指定時間の経過後に処理を呼び出せる便利なノードです。
f:id:PaperSloth:20180924153029p:plain

こういったLatentノードではDelayなんかをよく使うと思います。
SetTimer系の便利なとこはループさせたり
任意のタイミングでループを終了させたりできることですね。

似た関数でSet Timer by Function Nameというのがあります。
今回はこちらの解説は端折ります。
個人的に関数名を文字列で渡したりするのはあんまり好きではないですね。
f:id:PaperSloth:20180924153036p:plain

Unreal C++での実装について

FTimerManagerについて

こういったタイマー系の処理は素直にC++で組むと結構面倒です。
ですが、Unreal C++は色々と便利な関数を用意してくれています。

先ずはSetTimer関数を呼び出すためにFTimerManagerを取得します。
TimerManagerはWorldが保持しており、このように取得できます。

FTimerManager& timerManager = GetWorld()->GetTimerManager();

また、Actorを継承したクラスであれば下記のように取得できます。

FTimerManager& timerManager = GetWorldTimerManager();

SetTimerについて

TimerManagerの中にSetTimer関数があるのでこちらを使用します。
今回は例としてActorからの呼び出しを行っています。

FTimerHandle handle;

// Function:デリゲートとして渡す関数
// Duration:遅延させる秒数
// InLoop  :処理をループさせるか
GetWorldTimerManager().SetTimer(handle, this, /*Function*/, /*Duration*/, /*InLoop*/);

簡単な例として0.2秒後にLogを出す処理を書いてみます。

void AExampleActor::BeginPlay()
{
    Super::BeginPlay();
    FTimerHandle handle;
    GetWorldTimerManager().SetTimer(handle, this, &AExampleActor::OutputLog, 0.2f, false);
}

void AExampleActor::OutputLog()
{
    UE_LOG(LogTemp, Log, TEXT("Call SetTimer"));
}

また、SetTimerはいくつか種類があり、ラムダ式を使用することもできるため
簡単な処理であれば直接書いてしまってもいいかもしれません。
この場合は引数としてthisポインタを渡しておらず、引数が違う点に注意です。

void AExampleActor::BeginPlay()
{
    Super::BeginPlay();
    FTimerHandle handle;
    GetWorldTimerManager().SetTimer(handle, []()
        {
            UE_LOG(LogTemp, Error, TEXT("Log message"));
        } , 0.2f, false
    );
}

タイマーの停止、再開について

処理をループさせている場合のタイマーの停止、再開は下記のように行います。

FTimerHandle handle;
FTimerManager& timerManager = GetWorldTimerManager();

// タイマー停止
timerManager.PauseTimer(handle);
// タイマー再開
timerManager.UnPauseTimer(handle);

これで任意のタイミングでタイマーを操作できます。

タイマーの終了処理について

タイマーを使用した場合は終了処理も合わせて書くようにしましょう。

void AExampleActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);

    FTimerManager& timerManager = GetWorldTimerManager();

    // Handleに登録されたTimerの解放
    timerManager.ClearTimer(handle);
 
    // このActorが所有するタイマーの解放
    timerManager.ClearAllTimersForObject(this);
}

引数付きの関数を渡す場合について

関数にパラメーターを渡す場合はFTimerDelegateのBindUFunctionを使用します。
このBindUFunctionを使用する場合はUFUNCTIONマクロがないとエラーになるので注意です。
UFUNCTIONの説明の際に軽く触れています。
UE4 UFUNCTIONの種類について - PaperSloth’s diary

// Header
// BindUFunctionに渡す関数はUFUNCTIONの定義がないとエラーになる
UFUNCTION()
void InitializeTransform(const int32 Id, const FVector& Location, const FQuat& Quat);

// cpp
void AExampleActor::InitializeTransform(const int32 Id, const FVector& Location, const FQuat& Quat)
{
    UE_LOG(LogTemp, Error, TEXT("Call InitializeTransform"));
}
FTimerHandle handle;
FTimerManager& TimerManager = GetWorldTimerManager();

FTimerDelegate TimerDelegate;
// BindUFunctionで使用する関数名、引数に渡す値を入れる
TimerDelegate.BindUFunction(this, FName("InitializeTransform"), 100, FVector::ZeroVector, FQuat::Identity);
TimerManager.SetTimer(handle, TimerDelegate, 1.0f, false);

BindUFunctionは文字列で関数名を渡すため、関数名や引数を変更する際は漏れが発生しやすく注意が必要です。

参考資料

Delay in C++ - UE4 AnswerHub
Using SetTimer() on a Function with Parameters - UE4 AnswerHub


とりあえずはこれでC++側でも簡単に時間を遅らせて処理をすることができますね。
最後にコードを全て載せておきます。

VisualStudio UE4のCode Snippets登録について

環境

Visual Studio Community 2017
・Unreal Engine4 git release branch(4.20.2)


Code Snippetsについて

簡単に言ってしまうとよく使うコードのテンプレートを簡単に呼び出せるものです。
Visual C++ のコード スニペット - Visual Studio | Microsoft Docs

例えばUE4の場合はマクロが多く、都度入力するのが結構な手間だったりします。
特にstruct, enum, property, functionあたりが定義が面倒で使用頻度が高いですね。


UE4のCode Snippetsについて

以前に何度か使うといいですよとは書いたものの…
UE4 Unreal C++を書くための環境構築 - PaperSloth’s diary
UE4 UFUNCTIONの種類について - PaperSloth’s diary
使い方が書いていなかったため、今回改めてまとめてみました。

GitやPerforceからエンジンコードを取得した際に
Engine\Extras\VisualStudioSnippets の中にCodeSnippetsが保存されています。
4.20.2現在で64種類あるようです。

GitHub経由でのダウンロード方法についてはこちら
Unreal Engine | アンリアル エンジン ソースコードのダウンロード


Code Snippetsのインポート方法

Visual Studioのツールの中にコードスニペットマネージャーがあるので、それを開きます。
(CtrlK, CtrlBでも開けます)

コードスニペットマネージャーを開いたら言語をVisual C++に変更します。
f:id:PaperSloth:20180919221736p:plain

続いてインポートを押下します。
その後、Engine\Extras\VisualStudioSnippets 以下のコードスニペットを全て選択します。
すると下図のようにインポート画面が開くため、My Code Snippetsにチェックを入れて完了します。
f:id:PaperSloth:20180919221949p:plain

インポートが正常に完了していれば
Visual Studio (VersionName)\Code Snippets\Visual C++\My Code Snippets 以下に
先程インポートしたCode Snippetsがコピーされています。
直接ここにSnippetsをコピーしてVisual Studioを再起動でも動作すると思います。


Code Snippetsの使い方

SnippetのShortcutに登録された名前を入力してTabで使用できます。
よく使うのはuproperty, ufunction, ue4log, ue4enum, ue4structあたりでしょうか。

私の場合はue4enum, ue4logとかは少し編集しています。
是非とも自分の使いやすいように気軽にsnipettを編集してみてください。
f:id:PaperSloth:20180919223732g:plain

UE4 UFUNCTIONの種類について

環境

Visual Studio Community 2017
・Unreal Engine4.20.2


概要

UFUNCTIONの呼び出しについて日本語でまとまった情報が少なく
毎回BlueprintImplementableEventとBlueprintNativeEventあたりがごっちゃになるのでまとめる。

この記事で説明しないこと
メタデータ指定子(BlueprintAutocast, BlueprintInternalUseOnlyとかとか)
 https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Reference/Metadata/DeprecatedFunction/index.html
・RPC(Client, Server, NetMulticast, Reliable, Validation)について
 RPCs | Unreal Engine Documentation



C++関数とBlueprintの連携について

BlueprintCallable

Blueprintからの呼び出しが可能になる。
UPROPERTYの場合はCategoryがなくてもエラーにならないが
UFUNCTIONはCategoryの指定がない場合エラーとなる。

UFUNCTION(BlueprintCallable, Category="Transformation")
void UpdateActorTransform(const FTransform& Transform);

f:id:PaperSloth:20180919193059p:plain

BlueprintPure

Blueprintからの呼び出しが可能。
実行ピンを持たないノードになる。
Pure関数の作成方法は下記の2種類がある。

①通常のPure関数

UFUNCTION(BlueprintPure, Category="Transformation")
FTransform GetTransformPure();

f:id:PaperSloth:20180919193139p:plain

②const指定した場合はBlueprintCallableであっても
Pure関数となり、実行ピンを持たない。

UFUNCTION(BlueprintCallable, Category="Transformation")
FTransform GetActorTransform() const;

f:id:PaperSloth:20180919193214p:plain

BlueprintImplementableEvent

Blueprintからの呼び出しが可能でC++側での実装は持たない。
BlueprintCallableを指定しない場合はBlueprint側で呼び出しはできない
C++側からは呼び出しが可能なため
意図的にBlueprintのみにロジックを書くこともできる

UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category="Vehicle")
void SpawnVehicle();

f:id:PaperSloth:20180919193324p:plain

BlueprintCallableを指定していればBlueprintからも呼び出しが可能
f:id:PaperSloth:20180919193740p:plain

戻り値がvoidの場合はEventNodeが生成されるが
戻り値を持つ関数の場合はEventではなく関数として生成される

Blueprint側でオーバーライドしたい場合は関数名を右クリックして
「Implement Function」で関数が生成される

UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category="Vehicle")
bool CanSpawnVehicle();

処理を記述する場合はOverrideして使用する
f:id:PaperSloth:20180919193434p:plain

f:id:PaperSloth:20180919193456p:plain

BlueprintNativeEvent

Blueprintからの呼び出しが可能でC++側での実装を持つ
BlueprintCallableを指定しない場合はBlueprint側で呼び出しはできない
C++で基本的な実装を行い、Blueprintでロジックを拡張する場合等に使う

UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Job")
void UpdateCharacterJob();

f:id:PaperSloth:20180919193927p:plain

C++側での実装は「"関数名"_Implementation」という書き方でなければならない

void AExampleActor::UpdateCharacterJob_Implementation()
{
}

C++側で関数を呼び出す場合は注意が必要
呼び出し時に_ImplementationをつけるとC++側の実装のみが呼び出される

呼び出し時に_ImplementationをつけなければBlueprint側の実装のみが呼び出される
この時にNode側でParentを呼び出していないとC++側の処理は実行されない

void AExampleActor::BeginPlay()
{
    Super::BeginPlay();
    // C++側の実装のみ呼び出し
    //UpdateCharacterJob_Implementation();

    // Blueprintの実装呼び出し
    UpdateCharacterJob();
}

その他

Categoryの階層構造について

UPROPERTY同様に"|"をつけることで階層構造になる。

UFUNCTION(BlueprintCallable, Category = "Utilities|Log")
void PrintLog(const FString& Log);

f:id:PaperSloth:20180919194145p:plain

参照について

引数を参照にした場合に入力ピンとなる場合と出力ピンとなる場合がある

①const参照の場合
入力ピンとして扱われる

UFUNCTION(BlueprintCallable, Category="Collision")
bool SimpleRayTrace(const FVector& StartLocation, const FVector& EndLocation);

f:id:PaperSloth:20180919194232p:plain

②constなしの参照の場合
出力ピンとして扱われる

UFUNCTION(BlueprintCallable, Category="Collision")
bool SimpleRayTraceHit(const FVector& StartLocation, const FVector& EndLocation, FHitResult& OutHit);

f:id:PaperSloth:20180919194304p:plain

③constなしの参照を入力として扱いたい場合
UPARAM(ref)を付けることで入力として扱われる

UFUNCTION(BlueprintCallable, Category="Collision")
bool RayTraceHit(const FVector& StartLocation, const FVector& EndLocation, UPARAM(ref)FHitResult& HitResult);

f:id:PaperSloth:20180919194341p:plain

空のUFUNCION()について

エンジンコードを眺めているといくつかUFUNCTION()を見かけます。
その中でも OnRep_ で始まる関数は変数のReplicationに関わるものです。
それ以外にもBindUFunctionを使用したデリゲートで必要な場合もあります。

コードスニペットについて

本題とは離れますが、登録しておくと快適にコードが書けるので使ってください
以前にも少し紹介しています。
Git or Perforceで落としてきたエンジンのEngine/Extras/VisualStudioSnippets以下にあります
UE4 Unreal C++を書くための環境構築 - PaperSloth’s diary

f:id:PaperSloth:20180919194545g:plain

UE4 Fade処理について

環境

UE4.19.2


FadeIn, Outについて

FadeIn:画面が徐々に明るくなる演出
FadeOut:画面が徐々に暗くなる演出

Level切替時やカットシーン等色々な場面で使用されますね。



UE4での表現方法について

Widgetのアニメーションを使用して全画面表示の画像で表現する
お手軽に表現できますが、都度Widgetに埋め込むのが面倒。
別パーツとして分けてUser Widgetにした場合だとCallback関数の登録が面倒。
SoundFadeを別途組む必要がある。

参考資料
Widgetでのアニメーションの作成方法が書かれています。
UE4 Unreal Motion Graphicsを使って黒画面フェードを実装する - Let's Enjoy Unreal Engine


②CameraFade関数を使用
・Set Manual CameraFade
・Start Camera Fade関数がある
お手軽に使えるがWidgetのアニメーションと違い終了時のコールバック関数の登録がない。
また、自前のPostProcessと相性が悪い場合がある。

参考資料
StartCameraFadeの引数の詳細な情報が書かれています。
UE4 シンプルな画面のフェードイン・フェードアウト(Start Camera Fade) 凛(kagring)のUE4とUnityとQt勉強中ブログ


FunctionLibraryに登録してFade終了後のCallback関数も登録可能にする

さて、今回の本題です。

①シンプルなFade
先ずはシンプルに組んでみましょう。
簡単なFadeであればこれで十分でしょう。
f:id:PaperSloth:20180613223526p:plain

②FadeIn, Outの切り替え
続いて、FadeIn, Outを切り替えれるようにします。
IsReverseなどのFlagを用意しても良いですが
UMGで用意されている列挙型(EUMG SequencePlayMode)が名前も分かりやすいので今回はこちらを使用します。
f:id:PaperSloth:20180613224637p:plain
f:id:PaperSloth:20180613223906p:plain

③Callback関数の登録
さて、これまでは通常通りのCameraFadeでした。
次にFade終了時のEventを登録できるようにします。
下図のようにSet Timer by Eventを使用します。
f:id:PaperSloth:20180613224856p:plain
f:id:PaperSloth:20180613224232p:plain

しかし、FunctionLibraryからはSet Timer by Eventを呼び出せないため
他のBlueprintでノードを作成して、コピペします。
Event引数も通常はdelegate変数の登録ができないため、関数の入力ノードにEventピンをドラッグします。
この方法は少々ハック的なやり方でいつ動作しなくなるかは保証ができません。

④補足:Delayを使用しない理由について
MucroLibraryにしてDelayを使わない理由についてです。

先ずはMacroLibraryでDelayを使用して作った例が下図です。
Delayノードを使用することでシンプルにFade終了時のタイミングを取得できます。
f:id:PaperSloth:20180613224333p:plain

さて、使用しない理由についてですが
例えばゲーム内でスローモーションや早送りがあった場合にDelayノードだった場合
処理速度の変化の影響を受けてしまい、意図した挙動にならないことがあると思います。
そういった問題を回避するために今回はSet Timer by Eventを使用するようにしています。

参考資料
時間変化の影響を受けるノード等について書かれています。
UE4小ネタ : 制限時間の実装方法を色々と - ぼっちプログラマのメモ


まとめ

Fadeについては以上です!
Fadeはおおよそどんなゲームでも使うのではないでしょうか?

今回紹介した方法に限らずプロジェクトに合った便利機能を作っていきましょう!