ぼっちプログラマのメモ

Unreal Engineについて書いたりしてます

【UE5】猫でも分かるChooser(動的アセットの選択)ことはじめ Part 1 - Chooserとは?編 -

本記事はUnreal Engine (UE) Advent Calendar 2024 の13日目の記事です。

UE5.3(5.2だったかも)辺りで追加され、UE5.5ではProduction ReadyになったChooser(動的アセットの選択)機能に関して書いていきます!「なんか凄そうな機能だけど、具体的に何ができるのかよくわからんし、どう使ったらいいかもわからん!」という(半年前の僕のような)人にとって足がかりになればなぁというノリで書いてます。

なお、本記事はUE5.5.1 時点での内容をベースに書いています(ただ大半はUE5.4でもいけます)。また、UEの基本的な機能は軽く知っていること前提としています。もしChooser関連以外で知らない用語ができた際はその都度Google先生に聞くのが良いと思います。

UE5.4リリースノート:
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/unreal-engine-5.4-release-notes#チューザーとプロキシテーブル(ベータ版))

UE5.5 リリースノート:
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/unreal-engine-5.5-release-notes#チューザー

公式ドキュメント:
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/dynamic-asset-selection-in-unreal-engine

公式サンプル:
- 機能別サンプル:Game/Maps/Animation/Animation_Choosers レベル (おすすめ!)
- GameAnimationサンプル:CHT_XXX でアセット検索

続きを読む

【UE5】FGuid型のプロパティに対して「is not initialized properly even though its struct probably has a custom default constructor」というエラーが出たときの対処方法

本記事は Unreal Engine (UE) - Qiita Advent Calendar 2024 - Qiita シリーズ4の 1日目の記事です。
定期的に遭遇するエラーの対処方法を毎回忘れるので、備忘録的な小ネタ記事です。
( 1日目の記事なのにこんなに地味 & シンプルでいいのかなと思いつつも、サクッと書いて公開しようという気になったのでしょうがない)

遭遇した問題

とある構造体(USTRUCT)にて FGuid型のプロパティ Guid を使っていたところ…パッケージ作成時に以下のエラーが出るようになりました(UE5.4環境)。

LogClass: Error: StructProperty FCollisionLimitDataBase::Guid is not initialized properly even though its struct probably has a custom default constructor. Module:KawaiiPhysics File:Public/KawaiiPhysicsLimitsDataAsset.h

実際のコードはこちら。うーん、初期化してるのになぁ…なんでや…

USTRUCT()
struct FCollisionLimitDataBase
{
    GENERATED_BODY()

...

    UPROPERTY()
    FGuid Guid = FGuid::NewGuid();

対処方法

早速ググったり調べたところ、どうやら IgnoreForMemberInitializationTest をUPROPERTYのmetaに追加すればいいみたいです。

UPROPERTY( meta=(IgnoreForMemberInitializationTest))
FGuid Guid = FGuid::NewGuid();

ちゃんと原因を調べるべきだとは思うのですが…エンジン内の各所でも同様の対応をしているのを見つけたので、「まあ、それならいいか!動けば良し!」となったので深掘りはせず

Engine\Plugins\FX\Niagara\Source\Niagara\Public\NiagaraTypes.h

UPROPERTY(VisibleAnywhere, Category = "Version Control", meta=(IgnoreForMemberInitializationTest))
FGuid VersionGuid = FGuid::NewGuid();

なおIgnoreForMemberInitializationTestは、名前の通り、Structが持つ各プロパティが初期化されているか否かのチェックをスルーするためのものみたいです。

static void FindUninitializedScriptStructMembers(UScriptStruct* ScriptStruct, EScriptStructTestCtorSyntax ConstructorSyntax, TSet<const FProperty*>& OutUninitializedProperties)
{
...
    for (const FProperty* Property : TFieldRange<FProperty>(ScriptStruct, EFieldIteratorFlags::ExcludeSuper))
    {
#if    WITH_EDITORONLY_DATA
        static const FName NAME_IgnoreForMemberInitializationTest(TEXT("IgnoreForMemberInitializationTest"));
        if (Property->HasMetaData(NAME_IgnoreForMemberInitializationTest))
        {
            continue;
        }

簡単な内容ではありますが、少し混乱する挙動 & 解決方法なので記事にしてみました。少しでも参考になれば幸いですー!

参考:

https://forums.unrealengine.com/t/fs-test-id-is-not-initialized-properly/560690/7

【UE5】FEditorDelegates 自分用メモ

はじめに

エディタの特定のタイミングに処理を追加するのにFEditorDelegatesをよく使いますが、ちょうど忘れたタイミングで必要になり毎回エンジンコードを探しに行くのが面倒だったので、メモ代わりに関数名・コメント(日本語版)を並べました。何か追記することがでてきたらその都度更新で…

なお、UE5.4時点でのものです。

続きを読む

【UE5】Live Coding時に自動的にSlateを更新する「Live Update for Slate」プラグインが便利という話

はじめに ✕ おわりに ✕ おまけ

SlateをC++ガリガリ書く際、変更結果を確認するためにエディタやタブを開き直すのが手間に感じていました…が、

偶然見つけた「Live Update for Slate」プラグイン有効にすると、LiveCodingによるビルド成功時に自動的にエディタ・タブに反映されて超便利なのでオススメ!

www.youtube.com

エンジンの変更履歴を見る限りだと、UE5.2時点でプルリク経由で組み込まれたようです。感謝
https://github.com/EpicGames/UnrealEngine/tree/ue5-main/Engine/Plugins/Editor/LiveUpdateForSlate
GitHub - Sythenz/LiveUpdateForSlate: Refreshes the editor layout and tabs when Live Coding is complete. Compiled in UE4.27.2.

おしまい

【UE5】エンジン内にコッソリあるSlateサンプル集(SlateViewer)を見る方法

はじめに

下記公式ドキュメントにて「Slate Viewer Widget Gallery で実演されないレイアウトとウィジェットの複雑性」とあるのですが… Unreal Engine でのスレート UI ウィジェットのサンプル | Unreal Engine 5.5 ドキュメンテーション | Epic Developer Community

Slate Viewer Widget Gallery ってどこにあるの!?!?!?

SlateViewer アプリをビルドして実行するか、エディター内から「ウィンドウ」→「開発者ツール」→「デバッグ ツール」→「テスト スイート」に移動することができます。どちらも同じものが表示され、ウィジェット ギャラリーを含むタブが含まれます。 How do I run the Widget Gallery? - UI - Epic Developer Community Forums

ないよ!?

となって調べたら見つけました。めちゃええやん!!! ( Icon一覧で見れるの凄く助かる…カーソル合わせたらパス出るし…)

というわけで、このSlateサンプル集の出し方と実装されてるコードの場所についてメモ代わりに書いていきます。

続きを読む

【UE5】Instanced Object におけるプロパティ表示を少し強引にフィルタリングしてみる

はじめに

Instanced Object という機能を使うことで実装を分割・併用することができ、柔軟かつ変更に強い設計を構築することができます。

UE5/UE4 C++で変数にクラス(Class)のインスタンス(Instance)をプロパティ指定子(Property Specifiers)を使って指定する(UPROPERTY(Instanced)、UCLASS(EditInlineNew)) 凛(kagring)のUE5/UE4とゲーム制作と雑記ブログ
エディタのUXを向上させるUnreal C++との付き合い方 | ドクセル

特に個人的に好きなのは、C++で基盤となる仕組みを作っておけば、BPでも拡張・量産できることです。下記のように書いておけば、

UCLASS(Blueprintable, EditInlineNew, CollapseCategories)
class MYPROJECT_API UMyCommand : public UObject
{
    GENERATED_BODY()

    UMyCommand();

public:
    UFUNCTION(BlueprintNativeEvent)
    void Run();
    virtual void Run_Implementation() PURE_VIRTUAL();

};

BP側で派生クラスを作成することで、

専用のActorクラス や DataAsset などで Instanced Object による処理のつけ外しを行うことができます。便利!

ただ少し困ったことに、BP側で追加したプロパティが全て Instanced Object の設定部分で表示されてしまいます。プロパティのInstance Editable設定は残念ながら無視されます…

使用するプロパティを全てC++側で実装したり、C++側の CollapseCategories を削除 & BP側で Hide Categories を設定すれば解決するのですが…色々面倒でInstanced Object の良さが減ってしまいます。

なにか良い方法がないか調べていたのですが見つからず…最終的にはInstanced Objectの表示カスタマイズにより強引に対応してみた、というのがこの記事の内容です。C++多めです。

続きを読む

【UE5】特定のディレクトリ以下にあるファイルの変更を検出する方法について

はじめに

UE5にはAuto Reimportという「アセットの元データ(画像、モデル、テキストデータなど)が変更されると自動的に再インポートを行う」機能があります。ただ直接的な元データではなかったり、プロジェクト独自にチェックや変換処理をしたい時はAuto Reimportでは難しい場合があります。

dev.epicgames.com

そんなときに「元データが変更されたことを検出できる仕組み」があると便利だったりします。ということで、UE5における実装方法について調べてみました。

ファイルの変更を検出する方法

早速ですが、指定ディレクトリ以下にある各ファイルの変更を検出する処理は以下のようにして組めます。

// 検出機能に登録するデリゲートを管理
FDelegateHandle DirectoryWatcherHandle;
// 検出対象のディレクトリ。相対パス、フルパスどちらもOK
FString Directory = FPaths::ProjectSavedDir() / TEXT("hogehoge");

FDirectoryWatcherModule& DirectoryWatcherModule =
    FModuleManager::LoadModuleChecked<FDirectoryWatcherModule>("DirectoryWatcher");
if (IDirectoryWatcher* DirectoryWatcher = DirectoryWatcherModule.Get())
{
    DirectoryWatcher->RegisterDirectoryChangedCallback_Handle(
        Directory,
        IDirectoryWatcher::FDirectoryChanged::CreateLambda([this](const TArray<FFileChangeData>& FileChanges)
        {
            for (const FFileChangeData& FileChange : FileChanges)
            {
                // ファイルの追加・削除ではなく変更のみを対象に
                if (FileChange.Action == FFileChangeData::FCA_Modified)
                {
                    // FileChange.Filename でファイルパスを取得
                }
            }
        }),
        DirectoryWatcherHandle
    );
}

監視を切る場合はUnregisterDirectoryChangedCallback_Handleを使用すればOKです。

DirectoryWatcher->UnregisterDirectoryChangedCallback_Handle(Directory, DirectoryWatcherHandle);

おまけ

UE標準の Auto Reimport では AutoReimportManagerFContentDirectoryMonitorFFileCache周辺のクラスを使っているので真似しようと思ったのですが、FFileCache::Tick()を回す必要があったりと手間が多そうなので今回は簡単な方法を選んでみました。

FFileCacheの方だとTimestampだけでなくHashでも検出ができるので、より高度な判定をしたい場合はこちらを使ったほうがいいかもしれません。

おしまい