PaperSloth’s diary

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

UE4 C++のバージョンを変更する方法

環境

UE4.23.1
Visual Studio Comuunity 2019

UE4Unreal C++環境について

リリース時のUE4Unreal C++C++11をベースに一部C++14で開発されていました
jp docだとC++11, us docだとC++14と記載されています。
Coding Standard | Unreal Engine Documentation
コーディング標準 | Unreal Engine ドキュメント

しかし、現在はC++17以降も使用できるようになっています。
ちょうどUE4.22でVisual Studio 2015のサポートを(基本的には)終了した頃からでしょうか。
※基本的にはと書いたのはtarget.csにCompilerの指定を書けばVS2015でも動作するためです。

おさらいとして、UE4で使用するIDEの変更方法についての記事も載せておきます。
papersloth.hatenablog.com

UE4C++のバージョン指定方法

UE4で使用されるC++のバージョン指定はProjectのtarget.cs内に記述をすることで変更が可能です。

以下はプロジェクト作成時の target.csです

using UnrealBuildTool;
using System.Collections.Generic;

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

        ExtraModuleNames.AddRange(new string[] { "ProjectName" });
    }
}

C++のバージョン指定は下記を追記するだけで行なえます。
CppStandard = CppStandardVersion.{任意のバージョン};

using UnrealBuildTool;
using System.Collections.Generic;

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

        ExtraModuleNames.AddRange(new string[] { "ProjectName" });
        CppStandard = CppStandardVersion.Cpp17; //C++17を指定
        // C++14の場合 -> CppStandardVersion.Cpp14
        // 最新のC++の場合 -> CppStandardVersion.Latest
    }
}


以上です!
お好きなIDEC++バージョンで快適な開発ライフを!

UE4 Lighting Channelsについて

環境

UE4.23.1

Lighting Channelsについて

通常のLightingではChannel 0番のみが使用されており、特に意識する必要はありません。
Lighting Channelsを使うと独特で面白い絵作りや
広いワールドに対してエリアごとに全く異なるライティングの絵作りなど
特徴的な表現ができるようになります。
ただし、パフォーマンスにも影響を与えるためプロファイリングをとって注意して扱ってください。
f:id:PaperSloth:20191112212109p:plain

設定方法

Lighting Channelsの設定が可能なのは
・Directional Light
・Point Light
・Spot Light
・Rect Light
上記4つになります。

・Sky Light についてはLighting Channelsが設定できないため、注意が必要です

設定箇所は各Lightの 「Light -> Lighting Channels」にあります。
f:id:PaperSloth:20191112211706p:plain
デフォルトでは0のみが使用されています。

Channelは単一でも組み合わせでも設定が可能です。
f:id:PaperSloth:20191112213104p:plain

さて、Channelを設定しても画面上で特に変化は起きないと思います。
Lighting ChannelsはLightだけでなく、各メッシュごとの設定が必要で
「Static Mesh, Skeletal Meshごとに設定が必要です」
設定箇所はLight同様に「Light -> Lighting Channels」です。
f:id:PaperSloth:20191112212436p:plain


さらにもう1点注意が必要なのが「影」です。
Shadow Castするのは同一のChannelのメッシュ同士のみです。

Channel 0に白、1に赤、2に青のChannelを設定した
Directional Lightを3つ配置しました。
言葉にすると分かりにくいので、下図をご覧ください。
f:id:PaperSloth:20191112212711p:plain

まずステージ全体のStatic Mesh(床)はLight Channel 0です。
そのため、0と0 & 1と書かれたキャラクターの影のみが落ちます。

続いてChannel 1と書かれたキャラクターと床は1の赤いライトのみが適用され
2と書かれたキャラクターと床には2の青いライトのみが適用されるようになっています。


以上の特性に気をつけて、色々な絵作りをやってみてください!
以上です。

UE4 Projectから直接参照されていないuassetをパッケージに含める方法

環境

UE4.23.1

登録方法と概要

Project内では通常使用されないが、特殊なケースで使用したい場合に使えると思います。
例:開発用に外部のjsoncsvからパス指定でロードするアセットを変更して確認を行いたい場合
例:ロードしたいアセットがパッケージに上手く紐付かずやむを得ず暫定対応でロードさせたい場合
...etc
なんにせよ通常の開発では使わないものと思いますし
ずっと登録したままだと無駄にパッケージサイズが肥大化するため注意が必要です。

注意点として、この方法が使えるのはContentフォルダ以下に配置された.uassetに限ります。

方法としては簡単で
「ProjectSetting -> (Project)Packaging -> (Packaging)Additional Asset Directories to Cook」
以下に参照先のディレクトリを登録するだけです。
f:id:PaperSloth:20191112205054p:plain

UE4 Blueprintでdelegateを引数で使用する方法

環境

UE4.23.0

Delagateとは

Blueprintから使用するDelegateではSetTimer by EventやEvent DispatcherにBindする際に使用するEvent等があります。
今回はSet Timer by Eventで例を作成していますが
Event DIspatcherを使用した場合でも同様にBindすることが可能です。
f:id:PaperSloth:20191023021502p:plain

関数やMacroからは通常delegateを引数に追加することができません。
f:id:PaperSloth:20191023021703p:plain

MacroでDelegateを使用する方法

MacroにはWildcardという型があり、それを引数にすることで使用できます。
f:id:PaperSloth:20191023022703p:plain

C++でいうところのTemplateのようなものだと理解してもらえればと思います。
WildcardはBlueprintのforeachloopノード等で使用されています。

例えば1秒後にEventを実行するようなMacroを組む場合は下記のような形になります。
f:id:PaperSloth:20191023022747p:plain

あとはEventをWildcardに接続すると下図のようにWildcard型がDelegateとして認識されます。
f:id:PaperSloth:20191023022909p:plain

実行結果は通常のSetTimer by Eventで1.0を設定したときと同じ結果を得ることができました。
f:id:PaperSloth:20191023023043p:plain

関数/MacroでDelegateを使用する方法

続いて関数とMacroのどちらでもDelegateを使用できる方法を紹介しますが
ハック的なやり方のため、動作の安全性については保証できません

関数またはMacroのノードグラフからSet Timer by Eventノードを呼び出します。
続いて、Event PinをInputに接続することでDelegateを入力引数として登録することができます。
f:id:PaperSloth:20191023023923g:plain
f:id:PaperSloth:20191023024028p:plain

動画ではMacroで説明を行いましたが、関数の場合でも同様の操作でDelegateの登録が可能です。
動作についても問題なく行えます。
f:id:PaperSloth:20191023024227p:plain


以上、UE4のちょっとしたハック的なテクニックでした。

実は過去記事でもこちらをサラッと紹介していました。
「FunctionLibraryに登録してFade終了後のCallback関数も登録可能にする」のところでFunction Libraryにdelegateを登録しています。
papersloth.hatenablog.com

UE4 EditConditionsについて

環境

UE4.23.0
Visual Studio Community2017

EditConditionsについて

EditConditions metadataはbool値によって任意のPropertyの編集を有効化/無効化する機能です。

例 : 水泳可能フラグが有効なキャラクターのみ水泳速度を設定する場合

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character")
bool IsSwim;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character", meta = (EditCondition = "IsSwim"))
FVector SwimVelocity;

フラグが無効な状態では速度を編集することができない
f:id:PaperSloth:20191012175849p:plain
f:id:PaperSloth:20191012175915p:plain

フラグが有効な状態のため、速度を編集することができる
f:id:PaperSloth:20191012180154p:plain
f:id:PaperSloth:20191012180201p:plain

このmetadata自体はUE4.23以前のバージョンからも使用できました。


しかし、UE4.23からはこのmetadataに簡単なbool式を割り当てることが可能となりました。
下記の「新機能: EditConditions メタデータの改善 (ベータ版)」を参照
www.unrealengine.com

例えばEnumとの比較やint値との比較などが可能です。
UE4.23以前のEditConditionsでは下記はエラーにはなりませんが認識されませんでした。

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character", meta = (EditCondition = "IsSwim == true"))
FVector SwimVelocity;


UE4.23からはbool式の評価ができるため、下記のような使い方ができます。
例 : 弾丸の種類がMissileの時だけホーミング性能を設定できる

UENUM(BlueprintType)
enum class EBulletType : uint8
{
    Beam,
    Missile,
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bullet")
EBulletType BulletType;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Bullet",
         meta = (EditCondition = "BulletType == EBulletType::Missile"))
float HomingAcceleration;

弾丸の種類がビームの場合はHoming性能の変更はできない
f:id:PaperSloth:20191012181301p:plain
f:id:PaperSloth:20191012181320p:plain

弾丸の種類がミサイルの場合はHoming性能の変更ができる
f:id:PaperSloth:20191012181326p:plain
f:id:PaperSloth:20191012181516p:plain



Unreal C++のproperty向けのmetadataはほかにもたくさん便利な機能があります。
その中でもEditConditionを適切に設定することは他職種とのやり取りが多い、ある程度規模の大きなタイトルでは必須ではないかと思います。

以上です!

UE4 MaterialのHierarchyの紹介

環境

UE4.23.0

Hierarchyボタンについて

UE4.23からMaterial Editor, Material Instnce EditorにHierarchyボタンが追加されました。
Hierarychyボタンが追加されたのはMaterialで、Material Instanceには以前のバージョンからありました。
具体的なバージョンについては記憶していないです……
後日分かり次第追記いたします。

www.unrealengine.com

まずはMaterial Editorから
Third Person TemplateのGraymanのMaterialを見てみましょう。
f:id:PaperSloth:20191009234138p:plain

このMaterialはMaterial Instanceとして胸のロゴのMaterial Instnaceを持っていますね。
UE4.23から追加された[Hierarchy]ボタンを押すと
依存関係にある子のMaterial Instanceの一覧が表示されます。

複数子のMaterial Instanceがある場合はそれらが全て表示されます。
そこからMaterial Instanceを開くこととContent Browserから探すことができます。

あのマテリアルどっかで使ってたけど、どこだったかなって時に便利ですね。


続いてMaterial Instanceの方を見ていきましょう。
先程同様にHierarchyボタンが追加されています。
Material Instanceには以前のバージョンからありました!
ここからさらに子のMaterial Instanceや親のMaterial, Material Instanceを探すことができます。
f:id:PaperSloth:20191009234626p:plain


簡単な機能紹介でしたが、ちょっと便利になりましたね!

UE4 1フレーム処理をスキップさせる方法

環境

UE4.23.0

処理を1フレームスキップさせる方法

結論からいきましょう。
DelayノードをDuration 0で配置するだけで実装ができます。

実験その1 Sequence

Sequenceノードは上から順に実行されていくものでしたね。
ここではThen 0で1フレーム後に"Delay"と出力するのと
Then 1でDelayなしで"Execute"と出力される2つのノードがあります。

結果としてはThen 0の実行ピンが先に実行されますが
Delayを挟んでいるため
出力結果としては"Execute"が表示されてから"Delay"が表示されます。

実験その2 複数フレームの遅延は可能か


続いての実験はDelayノードを2つ挟んだ場合にどうなるかです。

この場合は"Execute" -> "Delay" -> "DelayDelay"の順で表示されます。

先程同様にSequenceノードは上から処理していきますが
Delayを挟んでいるため、最初の処理はこのフレームでは実行されないためです。
複数使用した場合はその個数分のフレームを遅らせることができるようです。
※2つまでしかテストしていないため最大幾つかは不明です

ただし、複数フレーム遅延させてノードを組むのは極力避けてほしいです。
処理が大変追いにくくなり、開発終盤になってからバグの原因だったということにもなりかねません。
もしも、このフレームスキップを使用するのであれば1フレーム以下として
使用する際も分かりやすいような命名やコメント等に色を付けるなど工夫してもらえればと思います。

Delayの仕組み

Delayノードの中身は下記に処理が書かれています。
Engine\Source\Runtime\Engine\Private\KismetSystemLibrary.cpp
以下にDelayのコードを載せておきます。

void UKismetSystemLibrary::Delay(UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo )
{
    if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
    {
        FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
        if (LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
        {
            LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
        }
    }
}

Delayノードが呼び出されると
1. FLatentActionManagerを取得
2. FLatentActionManager内に重複したFObjectActionsが登録されていないかチェック
3. FDelayActionを作成
3. FLatentActionManagerにFDelayActionを登録
というような流れになっています。

Delayノードが呼び出されると処理が直ぐに実行されるわけではなく
FLatentActionManagerに登録だけを行っているということが分かりましたね。


ここがちゃんと追えていなくて申し訳ないのですが
FLatentActionManager内に登録された処理がこのフレームで呼び出されていないため
1フレーム遅延して実行されるのではないかと考えています。

そのため、厳密には1フレームの実行ではなく
「1フレーム遅延して実行されているように見える」というのが正しい可能性もあります。
この辺りは後日確認をしてから追記しようと思います。

また、余談ですが
「RetriggerableDelay」の実装は「Delay」とほぼ同一の実装になっています。
Delayではアクションが登録されていれば何も処理を行わず終了しますが
RetriggerableDelayでは登録されている場合はDurationを更新するだけです。

FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
FDelayAction* Action = LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID);
if (Action == nullptr)
{
    LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
}
else
{
    // アクションが登録されていた場合はタイマーを上書きする
    Action->TimeRemaining = Duration;
}

以上です!

追記
UE5ではこっちを使った方がいいかも?という記事を書きました。
papersloth.hatenablog.com