PaperSloth’s diary

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

UE4 Actor間の距離の取得について

環境

UE4.20.3

距離の計算方法

数学的な計算方法は調べれば沢山出てくるので割愛してUE4での使い方の話をします。

C++側であれば下記のように求め

const float Distance = (GetActorLocation() - PlayerPawn->GetActorLocation()).Size();

Blueprintでは下記のように求めているという例をよく目にします。
f:id:PaperSloth:20181213154410p:plain
例えば敵とPlayerの距離とかアイテムとプレイヤーの距離とかですね。

Get ** Distance To

今回は分かりやすい例として何かしらのActorとプレイヤーの距離を求めるというケースです。

先程の計算をわざわざ書かなくともUE4.0の頃からActor内に便利な関数が複数用意されています。

GetDistanceTo

Actor間の距離を取得する便利関数です。
最初の例で書いたものと同様の結果を得ることができます。
f:id:PaperSloth:20181213155527p:plain

GetHorizontalDistanceTo

高さ(Z軸方向)を無視した距離を取得することができます。
平面での距離の取得なので、俯瞰視点のゲームやレーダーに使えそうですね。
f:id:PaperSloth:20181213155827p:plain

GetVerticalDistanceTo

先程と逆で高さ(Z軸方向)の距離のみを取得することができます。
float - floatなので一番計算コストが低いですね。
f:id:PaperSloth:20181213160005p:plain

GetSquaredDistanceTo

この関数だけはUE4.11から追加されたものです。
この関数ではベクトルの2乗の長さを取得します。
f:id:PaperSloth:20181213161353p:plain


意外と知られていないノードがたくさんあると思うので、気が付いたらいろんな人がシェアしてくれると嬉しいです。

UE4 昔ながらのSkydomeを使う

環境

UE4.20.3

SkydomeまたはSkyboxについて

ステージ全体を覆う球体を置いてそこにテクスチャを貼る昔ながらの天球表現方法です。
Unityみたいに画面を単色でクリアする方法はないのかと疑問に思っている人がいたので、その代替手段としての利用もできそうだなと思います。
f:id:PaperSloth:20181213141656p:plain

UE4にはBP_Sky_SphereというBlueprintがあり、天球の表現として大変優れていますが
今回のSkydomeのように局所的に空を表現したい場合や
極力テクスチャそのものの色味を表現したい場合等に今回の表現が使えると思います。
また、モバイルやVRなど負荷を抑えたい場合にも活用できるかもしれません。
f:id:PaperSloth:20181213153031p:plain

Skydomeを使う方法

Meshについて

 MeshはUE4内のアセットをそのまま流用してスケールだけ調整すれば良いでしょう。
 Engine/MapTemplates/Sky内にSM_SkySphereという球体のメッシュがあるのでこちらを使います。

Materialについて

 マテリアルもそのまま流用できるものがありますが、少し手を加えたいので今回は自作します。
 Unlitにして下記のようにCubeMapテクスチャに繋げば完成です。
 f:id:PaperSloth:20181213142304p:plain

ここまでで一旦LevelにMeshを配置してMaterialを割り当てると天球が表現できました。
f:id:PaperSloth:20181213142619p:plain

Materialについて(単色表現)

 冒頭に説明したようにUnityのように単色でクリアしたいというものの代替手段として天球で囲ってしまいましょう。
 因みにUnityのDefaultのSolidColorはR:49, G:77, B:121です。
 UE4のマテリアルで使用する値は0-255ではなく0-1ですので変換が必要です。
 LinearColorとColorの変換用のBlueprintなんかを作っておくとなにかの役に立つかもしれませんね。
 f:id:PaperSloth:20181213143631p:plain

 Colorを変換するとR:0.030713, G:0.074214, B:0.191202であることが分かりました。
 あとはこれをEmissiveColorに繋ぐだけです。
 f:id:PaperSloth:20181213144214p:plain

 しかし、あんまりUnityっぽくならないですね。
 f:id:PaperSloth:20181213144447p:plain

 これはUE4がデフォルトでポストエフェクトを色々かけているからですね。
 "ShowFlag.PostProcessing 0"でポストエフェクトを全て無効にします。
 AAも無効になるため、ジャギーも発生しますがデフォルトのUnityっぽい良い感じの絵になりました。
 f:id:PaperSloth:20181213145550p:plain

 脱線したので本題に戻ります。

Skydomeを回転させたい場合

 さて、このままでは天球を回転させても反映されません。
 そこでマテリアルにひと手間加えます。
 f:id:PaperSloth:20181213145112p:plain

 TransformVectorを追加し、WorldSpaceからLocalSpaceに変換しました。
 これでLevelに配置したメッシュを回転させた場合に見た目上も回転が反映されます。
 f:id:PaperSloth:20181213145351p:plain

CubeMapの変換

UE4では面白いことにCubeMapを2DTextureにしたり
2DTextureをCubeMapに変換してくれるようなMaterial Functionが提供されています。

注意点としてどちらもTextureの設定でNoMipMapsにしていないと意図しない線が表示されてしまいます。

CubeMap→2DTexture

UVToLongLatというMaterialFunctionを利用することでできます。
f:id:PaperSloth:20181213175724p:plain

f:id:PaperSloth:20181213175734p:plain

2DTexture→CubeMap

こちらはLongLatToUVというMaterialFunctionを利用することでできます。
f:id:PaperSloth:20181213175855p:plain

f:id:PaperSloth:20181213180208p:plain

ノイズ系のテクスチャを背景にしてみると面白そうですね。

その他

天球の設定をした後はSkyLightも設定することをオススメします。
天球によってはライティングの質がグッと良くなります。

ExponentialHeightFogも忘れてはいけません。
こちらもInscattering TextureからCubemapの設定ができるので設定しておくとより空気感のある絵になります。
f:id:PaperSloth:20181213152204p:plain

また、HDRテクスチャの圧縮方式ですがデフォルトではHDR(RGB, no sRGB)になっていますが
HDR Compressed(RGB, BC6H, DX11)に変更することで圧縮される場合もあるのでそちらも確認してみてください。

同じくテクスチャの設定ですがHDRテクスチャをインポートするとデフォルト解像度が
512 x 512 x 6になってしまいます。
なんだか思ったよりキレイにならないと感じたらチェックしてみてください。
Maximum Texture Sizeから変更でき、最大で2048 x 2048 x 6に変更できます。
ただし、元のTexture解像度が512や1024の場合は当然効果はありません。


天球を活用してよりリッチな絵作りを目指していきましょう!

Houdini ApprenticeとIndieについて

Houdini ApprenticeとIndieの違い

詳しくはSideFX公式の比較表がとても見やすいです。
www.sidefx.com

公式の比較表を見ると分かるように結構違いがあります。
その中でも個人的にHoudini Indieの契約を検討するラインが下記です。
Apprenticeではできないこと
・fbx形式でのExport(Importのみ可能, objでのExportは可能)
・Houdini Engine Pluginの利用ができない

fbxでのExportができないため、Houdini上でモデルを作成した場合などに
他の環境に移行して作業するのができなくて不便です。

そして、一番大きいのがHoudini Engineの存在ですね。
Maya, 3ds Max, Cinema4DなどのDCCツールや
Unity, UE4といったゲームエンジンにHDA(Houdini Digital Asset)を持っていくことができません。
(できないというよりは正しく動作しない)

Houdini Apprenticeは名前の通り、あくまでもHoudiniの学習用のライセンスです。

Houdini Engineについて

Houdiniの使い方を覚えたらUnity, UE4との連携をしたいという人も多いでしょう。
公式からUnity向け、UE4向けにそれぞれチュートリアル動画が出ています。
バージョンがHoudini14と15で古いですが、それでも取っ掛かりにはちょうど良さそうです。
どちらの動画も10分以内で比較的簡単にプロシージャルなモデルが作成できます。

Unity向けPluginについて
Unity Plug-in | SideFX
Unity向けHDAチュートリアル
Create Asset for Unity | SideFX


UE4向けPluginについて
Unreal Plug-in | SideFX
UE4向けHDAチュートリアル
Create Your Own Asset | UE4 | SideFX


HoudiniといえばBISHAMONのような
VFX用のミドルウェアだと思われてる方もいますがそうではありません。

特に興味深い例がGhost Recon Wildlandsで
昨年のGDC2017でプロシージャルな地形の生成について発表されました。
地面や川、道路だけでなくゲーム内の村もプロシージャルに生成して効率化しています。
崖や地面だけでもこだわって作られているのがよく分かります。
GDC Vault - 'Ghost Recon Wildlands': Terrain Tools and Technology
Procedural Technology in Ghost Recon: Wildlands

Houdini Indieについて

SideFX公式の購入ページから購入する場合
年間契約だと$269で2年契約だと$399でお得。
Houdini Store | SideFX

個人的にはいきなり契約するよりもApprenticeで基本操作を覚えてからの契約がオススメです。
Houdini Engine自体は無料でDLすることができます。
Houdini Engine自体は無料ですが、Apprenticeライセンスでは当然ゲームエンジンとの連携はできません。

また、最近はSteamからも販売されるようになりました。
Houdini Indie on Steam
12ヶ月のサブスクリプション(月々¥2495.75)と
24ヶ月のサブスクリプション(月々¥1896.66)が選べます。

公式のストアから2年契約が1番お得だったんですが
Steamでのセールを待ってみるのも1つの手段かもしれませんね。

Houdiniの学習について

幸せなことにHoudiniに関する書籍は日本語の書籍が世界中で最も多いのではないでしょうか。
書籍の他にも公式のチュートリアル動画やたくさんのHoudinistのブログを見るのも良いでしょう。

書籍は現在自分の知る限りでは4冊出ています。

・Houdiniではじめる3Dビジュアルエフェクト(改訂版)
http://amzn.asia/d/7D2xY0u
本の厚み的にも内容的にも最初に人にオススメできる本だと思います。
Houdiniに興味を持ったらここから始めてみるのもいいかもしれません。


・理論と実践で学ぶHoudini -SOP&VEX編-
http://amzn.asia/d/iXAfRyY
Houdiniの基本操作から応用までを扱った書籍。
元々は教科書として執筆されたもののようで、分かりやすいように丁寧な解説がされています。
また、Houdiniに限らず数学に関しての説明もあるのでプログラミングの知識がない人も安心です。


・Houdini ビジュアルエフェクトの教科書
こちらもHoudiniの基本操作から応用までを扱った書籍。
エフェクトとシミュレーションに特化しているため、そういった機能を特に学びたい人にオススメ。
Houdini Digital Assetについても書かれているので、ゲームエンジンと連携したい人にもオススメです。
http://amzn.asia/d/9QXUmFk


・Houdini実践ハンドブックWrangle×Python
http://amzn.asia/d/ep6e9Cz
現在はKindle Unlimitedに登録されているため、契約されている方は試し読みしてみてもいいでしょう。
(無料での公開ですが、読まれるとちゃんとお金が入るらしいので安心)

タイトルからも分かるようにWrangleとPythonについての基本から応用までが書かれています。
Houdini自体の操作の解説本ではないため、基礎を覚えてから読むと頭に入りやすいでしょう。

まとめ

Houdiniは書籍も充実して、CEDEC等の大きなセミナーでも登壇される方が増えてきました。
Houdiniを使って効率化されてきたと感じるまでの成長曲線が辛い時もありますが
最終的には覚えて良かったと言えるときが来るでしょう(多分)。

UE4の新エフェクトシステムのNiagaraとの連携も発表され、Houdiniを覚えるメリットが増えてきています。
たくさんの人がHoudiniを覚えて色々な知見が公開されるとみんなハッピーになれていいなと思います。
(仕事で使う人はちゃんと規約を読んで必要に応じてHoudini FXとHoudini COREを買ってね)

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++側でも簡単に時間を遅らせて処理をすることができますね。
最後にコードを全て載せておきます。