mcpField
U
ue-editor-tools

ue-editor-tools

github.com/quodsoler開発4191.3k 回インストール4 週間前に更新
リポジトリ

ue-editor-tools について

Unreal Engine 5.8 のエディター拡張向けスキルガイドです。カスタムツールやワークフローを作る開発者向けに、詳細パネル、エディターユーティリティウィジェット、メニュー、サブシステム、アセット操作を扱います。

主な機能

  1. 01エディターモジュールの設定
  2. 02詳細パネルのカスタマイズ
  3. 03エディターユーティリティウィジェットとスクリプト操作
  4. 04メニューとツールバーの拡張
  5. 05エディターサブシステムとアセットツール

活用シーン

  1. 01カスタムプロパティ編集画面の追加
  2. 02エディター内ユーティリティパネルの作成
  3. 03Content Browser のコンテキスト操作の拡張
  4. 04メニューコマンドやエディターサブシステムの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/quodsoler/unreal-engine-skills --skill ue-editor-tools

インストール後、Claude Code で/ue-editor-toolsを入力して読み込まれたか確認します。

同梱ファイルreferences/detail-customization-patterns.mdreferences/editor-module-setup.md

詳細

UE エディターツール

対象エンジン:UE 5.8。以下の API は 5.8 のヘッダーに照らして確認済みです。古い形式は「非推奨 — 使用禁止」に記載しています。

エディター拡張機能は、"Type": "Editor" を指定し、UnrealEd、Slate、SlateCore、PropertyEditor(詳細パネル)、ToolMenus(メニューとツールバー)、EditorSubsystem(UEditorSubsystem)、Blutility(エディターユーティリティウィジェットとスクリプト化されたアクション)、AssetTools、AssetDefinition(コンテンツブラウザーのアセットタイプ)に依存するモジュールに配置します。これらは Runtime モジュールから無条件に参照できません(エンジンのランタイムモジュールでは、AIModule.Build.cs:31-34 のように、if (Target.bBuildEditor) と #if WITH_EDITOR の両方の条件下でのみ追加します)。

コンテキスト

.agents/ue-project-context.md が存在する場合は読み込みます(モジュール名、規約、有効なプラグイン、GAS/ネットワーク設定)。ファイルがなくても作業を中断しないでください。

リクエストとコードベースから対象領域を特定します。異なるコードになる可能性のある解釈が 2 つある場合にのみ質問してください。

リクエストの内容…参照先
エディターモジュールの作成、Build.cs、登録のライフサイクルエディターモジュールの設定
クラスまたは構造体のカスタムプロパティパネル詳細パネルのカスタマイズ
エディター内で実行する UMG パネル、アセット/アクターの右クリックアクションエディターユーティリティウィジェットとスクリプト化されたアクション
メインメニュー、ツールバー、コンテキストメニューの項目、コマンドメニューとツールバー
エディターのライフタイムにわたるシングルトン、GEditor->GetEditorSubsystem<T>()エディターサブシステム
取り消し/やり直し、オブジェクトのダーティ状態への設定、エディターイベントへの応答トランザクションとエディターデリゲート
カスタムインタラクションまたはツールを備えたビューポートモードエディターモード
コンテンツブラウザーのタイプカラー、カテゴリ、ファクトリー、サムネイルアセットタイプ、ファクトリー、サムネイル
タブ付きアセットエディターウィンドウアセットエディターツールキット
保存時または CI でのアセット検証データ検証

エディターモジュールの設定

.uproject(または .uplugin)のモジュールエントリーでは、詳細パネルのカスタマイズと UToolMenus::RegisterStartupCallback メニューには Default で十分です(CommonUIEditor は詳細パネルのカスタマイズを Default で登録します)。StartupModule 自体で GEditor またはその他のエンジン初期化済み状態が必要な場合は、PostEngineInit を使用します。

{ "Name": "MyGameEditor", "Type": "Editor", "LoadingPhase": "PostEngineInit" }
// MyGameEditor.Build.cs
PrivateDependencyModuleNames.AddRange(new string[] {
    "Core", "CoreUObject", "Engine", "UnrealEd",
    "Slate", "SlateCore", "InputCore",
    "PropertyEditor",   // IDetailCustomization, IPropertyTypeCustomization
    "EditorSubsystem",  // UEditorSubsystem
    "Blutility",        // UEditorUtilityWidget, UAssetActionUtility
    "UMG", "UMGEditor",  // EditorUtilityWidget(Blueprint).h include UMG headers; Blutility links them privately
    "ToolMenus",        // UToolMenus
    "AssetTools",       // IAssetTools
    "AssetDefinition",  // UAssetDefinition
    "MyGame"            // your runtime module
});

モジュールクラスを宣言し、ライフサイクル関数を両方定義してから、最後に IMPLEMENT_MODULE を配置します。

// MyGameEditor.h
#pragma once
#include "Modules/ModuleManager.h"

class FMyGameEditorModule : public IModuleInterface
{
public:
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;

private:
    void RegisterMenus();
    static void OpenMyPanel();
};

// MyGameEditorModule.cpp — includes MyDataAsset.h, MyRange.h, the two customization
// headers, PropertyEditorModule.h and ToolMenus.h
void FMyGameEditorModule::StartupModule()
{
    FPropertyEditorModule& PropertyModule =
        FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");

    PropertyModule.RegisterCustomClassLayout(
        UMyDataAsset::StaticClass()->GetFName(),
        FOnGetDetailCustomizationInstance::CreateStatic(&FMyDataAssetCustomization::MakeInstance),
        FRegisterCustomClassLayoutParams());

    PropertyModule.RegisterCustomPropertyTypeLayout(
        FMyRange::StaticStruct()->GetFName(),
        FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyRangeCustomization::MakeInstance));

    PropertyModule.NotifyCustomizationModuleChanged();

    UToolMenus::RegisterStartupCallback(
        FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FMyGameEditorModule::RegisterMenus));
}

void FMyGameEditorModule::ShutdownModule()
{
    UToolMenus::UnRegisterStartupCallback(this);
    UToolMenus::UnregisterOwner(this);

    if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
    {
        FPropertyEditorModule& PropertyModule =
            FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
        PropertyModule.UnregisterCustomClassLayout(UMyDataAsset::StaticClass()->GetFName());
        PropertyModule.UnregisterCustomPropertyTypeLayout(FMyRange::StaticStruct()->GetFName());
        PropertyModule.NotifyCustomizationModuleChanged();
    }
}

IMPLEMENT_MODULE(FMyGameEditorModule, MyGameEditor)

RegisterMenus と OpenMyPanel はメニューとツールバーで定義します。FRegisterCustomClassLayoutParams は TOptional<int32> OptionalOrder を保持します。同じクラスを 2 つのモジュールでカスタマイズし、順序が重要な場合は設定してください。それ以外の場合は、デフォルト構築した構造体を渡します。

ディレクトリ構成、WITH_EDITOR ガード、ファクトリー、サムネイル、ツールキット、モードの完全な定型コードについては、editor-module-setup.mdを参照してください。

詳細パネルのカスタマイズ

IDetailCustomization は、UClass の詳細パネル全体のレイアウトを置き換えます。IPropertyTypeCustomization は、USTRUCT またはインスタンス化されたオブジェクトプロパティの行を置き換えます。どちらも、上記のように StartupModule から登録します。

// MyDataAssetCustomization.h
#pragma once
#include "IDetailCustomization.h"
#include "Templates/SharedPointer.h"

class FMyDataAssetCustomization : public IDetailCustomization
{
public:
    static TSharedRef<IDetailCustomization> MakeInstance();

    virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
    virtual void CustomizeDetails(const TSharedPtr<IDetailLayoutBuilder>& DetailBuilder) override;

private:
    TWeakPtr<IDetailLayoutBuilder> WeakBuilder;
};

// MyDataAssetCustomization.cpp
void FMyDataAssetCustomization::CustomizeDetails(const TSharedPtr<IDetailLayoutBuilder>& DetailBuilder)
{
    WeakBuilder = DetailBuilder;   // store it before calling ForceRefreshDetails() later
    CustomizeDetails(*DetailBuilder);
}

1 引数のオーバーライドでレイアウトを処理します。EditCategory、GetProperty、AddProperty、AddCustomRow、HideProperty、GetObjectsBeingCustomized などを使用します。実装の全文は、以下のリファレンスを参照してください。

USTRUCT に対応する FMyRangeCustomization : public IPropertyTypeCustomization では、CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) と CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) をオーバーライドします。

主要なインターフェイス(すべて Engine/Source/Editor/PropertyEditor/Public にあります):

型ヘッダー呼び出すもの
IDetailLayoutBuilderDetailLayoutBuilder.hEditCategory、HideCategory、GetProperty、HideProperty、AddPropertyToCategory、GetObjectsBeingCustomized、GetSelectedObjects、ForceRefreshDetails、RegisterInstancedCustomPropertyTypeLayout
IDetailCategoryBuilderDetailCategoryBuilder.hAddProperty、AddCustomRow、AddCustomBuilder、AddGroup、AddExternalObjects、SetSortOrder、InitiallyCollapsed(IDetailChildrenBuilder.h の IDetailChildrenBuilder は、CustomizeChildren 内で同じ 5 つの追加メソッドに加え、AddExternalStructure を提供します)
FDetailWidgetRowDetailWidgetRow.hNameContent()、ValueContent()、WholeRowContent()、ExtensionContent()、FilterString、Visibility、IsEnabled
IPropertyHandlePropertyHandle.hGetValue/SetValue のオーバーロード、GetNumChildren、GetChildHandle、SetOnPropertyValueChanged、SetOnChildPropertyValueChanged、CreatePropertyNameWidget、CreatePropertyValueWidget、EnumerateRawData、NotifyPreChange、NotifyPostChange
IDetailPropertyRowIDetailPropertyRow.hDisplayName、ToolTip、EditCondition、IsEnabled、Visibility、GetDefaultWidgets、CustomWidget
PropertyCustomizationHelpersPropertyCustomizationHelpers.hMakeAddButton、MakeRemoveButton、MakeClearButton、MakeBrowseButton、MakeUseSelectedButton、MakePropertyComboBox、SObjectPropertyEntryBox、SClassPropertyEntryBox

GetValue/SetValue は FPropertyAccess::Result(Success、Fail、MultipleValues)を返します。NotifyPostChange は EPropertyChangeType::Type(例:EPropertyChangeType::ValueSet)を受け取ります。ECategoryPriority::Type は上から順に並べ替えます:Variable、Transform、Important、TypeSpecific、Default、Uncommon。

行のパターン、CustomizeHeader/CustomizeChildren の実装、SObjectPropertyEntryBox、オブジェクトごとの値の列挙、フォントについては、detail-customization-patterns.md を参照してください。

エディター用ユーティリティウィジェットとスクリプトアクション

UEditorUtilityWidget(EditorUtilityWidget.h、モジュール Blutility)は、エディター内で実行される UUserWidget です。C++ でサブクラスを作成してヘルパー関数を公開し、ビジュアルは Blueprint の子クラスで構築します。

// MyEditorUtilityWidget.h
#pragma once
#include "EditorUtilityWidget.h"
#include "MyEditorUtilityWidget.generated.h"

UCLASS()
class MYGAMEEDITOR_API UMyEditorUtilityWidget : public UEditorUtilityWidget
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = "My Tools")
    void PrefixSelectedAssets(const FString& Prefix);
};

// MyEditorUtilityWidget.cpp
void UMyEditorUtilityWidget::PrefixSelectedAssets(const FString& Prefix)
{
    UEditorAssetSubsystem* AssetSubsystem = GEditor->GetEditorSubsystem<UEditorAssetSubsystem>();
    for (UObject* Asset : UEditorUtilityLibrary::GetSelectedAssets())
    {
        const FString Source = Asset->GetOutermost()->GetName();
        AssetSubsystem->RenameAsset(
            Source, FPaths::GetPath(Source) / (Prefix + TEXT("_") + Asset->GetName()));
    }
}

UEditorUtilityLibrary(EditorUtilityLibrary.h、Blutility)には、GetSelectedAssetData()、GetSelectedAssetsOfClass(UClass*)、GetSelectionSet()(レベルアクター)、SyncBrowserToFolders(const TArray<FString>&) も用意されています。

UEditorUtilitySubsystem(EditorUtilitySubsystem.h)を使うと、コードからウィジェット Blueprint を開けます。

UEditorUtilitySubsystem の呼び出し効果
SpawnAndRegisterTab(UEditorUtilityWidgetBlueprint*)タブを登録して開きます。UEditorUtilityWidget* を返します
SpawnAndRegisterTabAndGetID(UEditorUtilityWidgetBlueprint*, FName& OutTabID) / SpawnAndRegisterTabWithId(UEditorUtilityWidgetBlueprint*, FName InTabID)同様に、タブ ID を書き戻すか指定します
RegisterTabAndGetID(UEditorUtilityWidgetBlueprint*, FName& OutTabID)タブを開かずにスポーナーを登録します
TryRun(UObject* Asset)UEditorUtilityObject 派生アセットを実行します(Run を呼び出します)

スクリプトアクション:UAssetActionUtility のサブクラスにある UFUNCTION(CallInEditor) は、コンテンツブラウザーのコンテキストメニューに表示されます。レベルアクターには UActorActionUtility が同じ機能を提供します。どちらも UEditorUtilityObject から派生します。SupportedClasses 配列(TArray<TSoftClassPtr<UObject>>、クラスのデフォルトで編集可能)を使ってフィルタリングします。GetSupportedClasses() で値を読み取れます。

// MyAssetActionUtility.h
#pragma once
#include "AssetActionUtility.h"
#include "MyAssetActionUtility.generated.h"

UCLASS()
class MYGAMEEDITOR_API UMyAssetActionUtility : public UAssetActionUtility
{
    GENERATED_BODY()

public:
    UFUNCTION(CallInEditor, Category = "My Tools")
    void SetTextureCompressionToUI();
};

// MyAssetActionUtility.cpp
void UMyAssetActionUtility::SetTextureCompressionToUI()
{
    for (UObject* Asset : UEditorUtilityLibrary::GetSelectedAssets())
    {
        if (UTexture2D* Texture = Cast<UTexture2D>(Asset))
        {
            Texture->CompressionSettings = TC_EditorIcon;
            Texture->MarkPackageDirty();
            Texture->PostEditChange();
        }
    }
}

エディターは、FAssetActionUtilityPrototype(EditorUtilityAssetPrototype.h)でラップされたアセットレジストリタグから、ユーティリティアセットをロードせずにこれらのメニューを構築します。フィルターは実行時に計算するのではなく、CDO の SupportedClasses に設定してください。

メニューとツールバー

UToolMenus(モジュール ToolMenus)は、エディターのすべてのメニューとツールバーを管理します。拡張する Slate とメニューが存在するよう、スタートアップコールバックから登録してください。

// MyGameEditorModule.cpp (continued) — ToolMenuSection.h, ToolMenuEntry.h,
// Styling/AppStyle.h, EditorUtilitySubsystem.h, EditorUtilityWidgetBlueprint.h
void FMyGameEditorModule::OpenMyPanel()
{
    if (UEditorUtilityWidgetBlueprint* WidgetBP = LoadObject<UEditorUtilityWidgetBlueprint>(
            nullptr, TEXT("/Game/EditorWidgets/BP_MyTool.BP_MyTool")))
    {
        GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>()->SpawnAndRegisterTab(WidgetBP);
    }
}

void FMyGameEditorModule::RegisterMenus()
{
    FToolMenuOwnerScoped OwnerScoped(this);
    UToolMenu* WindowMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
    FToolMenuSection& Section = WindowMenu->FindOrAddSection("MyGame");
    Section.Label = FText::FromString("My Game");
    Section.AddMenuEntry(
        "OpenMyPanel",
        FText::FromString("My Panel"),
        FText::FromString("Open the My Game tool panel"),
        FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Settings"),
        FUIAction(FExecuteAction::CreateStatic(&FMyGameEditorModule::OpenMyPanel)));

    UToolMenu* PlayToolBar =
        UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
    PlayToolBar->FindOrAddSection("MyTools").AddEntry(
        FToolMenuEntry::InitToolBarButton(
            "MyToolButton",
            FUIAction(FExecuteAction::CreateStatic(&FMyGameEditorModule::OpenMyPanel)),
            FText::FromString("My Tool"),
            FText::FromString("Run the My Game tool"),
            FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Toolbar.Settings")));
}
呼び出し用途
UToolMenus::Get() / UToolMenus::TryGet()シングルトンです。TryGet はモジュールのロードを強制しません
RegisterMenu(FName Name, FName Parent = NAME_None, EMultiBoxType Type = EMultiBoxType::Menu, bool bWarnIfAlreadyRegistered = true)所有するメニューを宣言します
ExtendMenu(FName)他者が所有するメニューに追加します。未登録の場合は拡張をキューに入れます
FindMenu(FName)登録済みメニューを検索します。存在しない場合は nullptr です
UToolMenu::FindOrAddSection(FName) / AddSection(FName, Label, FToolMenuInsert)セクションを取得または作成します
FToolMenuSection::AddMenuEntry(FName, Label, ToolTip, FSlateIcon, FToolUIActionChoice, EUserInterfaceActionType, FName TutorialHighlight, TOptional<FText> InputBindingOverride)メニュー項目
FToolMenuSection::AddSubMenu / AddSeparator / AddEntryサブメニュー、区切り線、事前構築済みのエントリー
FToolMenuEntry::InitToolBarButton / InitComboButton / InitWidget / InitMenuEntryAddEntry に渡すエントリーを構築します
UToolMenus::RegisterStartupCallback(const FSimpleMulticastDelegate::FDelegate&)メニューが存在するまで登録を遅延させます
UToolMenus::UnRegisterStartupCallback(this) + UToolMenus::UnregisterOwner(this)ShutdownModule で必ず呼び出します

FToolMenuOwnerScoped はスコープ内で作成されたすべての項目にオーナーを設定するため、UnregisterOwner ですべて削除できます。便利なメニュー名:LevelEditor.MainMenu.Window、LevelEditor.MainMenu.Tools、LevelEditor.LevelEditorToolBar.PlayToolBar、LevelEditor.LevelEditorToolBar.User、LevelEditor.ActorContextMenu、ContentBrowser.AssetContextMenu。アイコンは FSlateIcon(FAppStyle::GetAppStyleSetName(), "StyleName") です。

キーボードショートカットと再利用可能なコマンドには UI_COMMAND とともに TCommands を使用します。editor-module-setup.md を参照してください。

エディターサブシステム

UEditorSubsystem(モジュール EditorSubsystem、ヘッダー EditorSubsystem.h)は、所有モジュールの読み込み時に自動で一度だけインスタンス化されます。登録の呼び出しは不要です。GEditor->GetEditorSubsystem<T>() で取得します。

// MyEditorSubsystem.h
#pragma once
#include "EditorSubsystem.h"
#include "MyEditorSubsystem.generated.h"

UCLASS()
class MYGAMEEDITOR_API UMyEditorSubsystem : public UEditorSubsystem
{
    GENERATED_BODY()

public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    virtual void Deinitialize() override;

private:
    void OnMapOpened(const FString& Filename, bool bAsTemplate);
};

// MyEditorSubsystem.cpp
void UMyEditorSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    Super::Initialize(Collection);
    FEditorDelegates::OnMapOpened.AddUObject(this, &UMyEditorSubsystem::OnMapOpened);
}

void UMyEditorSubsystem::Deinitialize()
{
    FEditorDelegates::OnMapOpened.RemoveAll(this);
    Super::Deinitialize();
}

void UMyEditorSubsystem::OnMapOpened(const FString& Filename, bool bAsTemplate)
{
    UE_LOG(LogMyGame, Display, TEXT("Opened map %s"), *Filename);
}

UEditorSubsystem は UDynamicSubsystem を継承します。Initialize(FSubsystemCollectionBase&)、Deinitialize()、ShouldCreateSubsystem(UObject* Outer) const は USubsystem に由来するため、必ず Super:: を呼び出してください。独自のサブシステムを作成する前に利用する価値のある、エンジンのエディターサブシステムは次のとおりです。

サブシステムモジュール用途
UEditorActorSubsystemUnrealEdGetAllLevelActors、GetSelectedLevelActors、SetSelectedLevelActors、SpawnActorFromClass、DestroyActor
UEditorAssetSubsystemUnrealEdLoadAsset、RenameAsset、DuplicateAsset、DeleteAsset、SaveLoadedAsset、ListAssets
UAssetEditorSubsystemUnrealEdOpenEditorForAsset、FindEditorForAsset、CloseAllEditorsForAsset、GetEditorModeInfoOrderedByPriority
UEditorUtilitySubsystemBlutilitySpawnAndRegisterTab、RegisterTabAndGetID、TryRun
ULevelEditorSubsystemLevelEditorレベルの開く/保存/新規作成操作

ゲームインスタンス、ワールド、ローカルプレイヤー、エンジン、エディターの各サブシステムの標準的な型一覧は、ue-cpp-foundations にあります。

トランザクションとエディターデリゲート

エディターで UObject を変更する場合はすべて、トランザクションでラップし、事前に Modify() を呼び出す必要があります。そうしないと、元に戻す操作で古いデータが復元されます。

#include "ScopedTransaction.h"
#include "Subsystems/EditorActorSubsystem.h"

void UMyEditorSubsystem::OffsetSelection(const FVector& Delta)
{
    const FScopedTransaction Transaction(
        NSLOCTEXT("MyGameEditor", "OffsetSelection", "Offset Selection"));
    UEditorActorSubsystem* ActorSubsystem = GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
    for (AActor* Actor : ActorSubsystem->GetSelectedLevelActors())
    {
        Actor->Modify();
        Actor->SetActorLocation(Actor->GetActorLocation() + Delta);
    }
}

FScopedTransaction(ScopedTransaction.h、UnrealEd)には、Cancel() と IsOutstanding() も用意されています。アセットのプロパティを直接書き換えた後は、MarkPackageDirty() と PostEditChange() を呼び出してください。

デリゲートシグネチャ
FEditorDelegates::PostUndoRedoFSimpleMulticastDelegate
FEditorDelegates::OnMapOpened(const FString& Filename, bool bAsTemplate)
FEditorDelegates::PreBeginPIE / PostPIEStarted / EndPIE(const bool bIsSimulating)
FEditorDelegates::OnAssetPostImport(UFactory*, UObject*)
FEditorDelegates::OnNewActorsPlaced(UObject*, const TArray<AActor*>&)
FEditorDelegates::OnPackageDeleted / OnAssetsPreDelete(UPackage*) / (const TArray<UObject*>&)
FCoreUObjectDelegates::OnObjectPropertyChanged / OnPreObjectPropertyChanged(UObject*, FPropertyChangedEvent&) / (UObject*, const FEditPropertyChain&)
FCoreUObjectDelegates::OnObjectModified(UObject*)

エディターモード

UEdMode(Tools/UEdMode.h、UnrealEd)はモードの基底クラスです。すべての UEdMode サブクラスは、すべてのモジュール読み込みフェーズが完了すると UAssetEditorSubsystem::RegisterEditorModes によって自動的に検出されます。コンストラクターで Info を設定し、レジストリは呼び出さないでください。

// MyEditorMode.h
#pragma once
#include "Tools/UEdMode.h"
#include "MyEditorMode.generated.h"

UCLASS()
class MYGAMEEDITOR_API UMyEditorMode : public UEdMode
{
    GENERATED_BODY()

public:
    static const FEditorModeID EM_MyEditorMode;
    UMyEditorMode();

    virtual void Enter() override;
    virtual void Exit() override;
    virtual void ModeTick(float DeltaTime) override;
    virtual void CreateToolkit() override;
    virtual bool UsesToolkits() const override;
};
  • コンストラクターで Info = FEditorModeInfo(EM_MyEditorMode, DisplayName, FSlateIcon(...), /*InVisibility=*/true) を設定します。Info は保護された UEdMode メンバーです。Enter() と Exit() では Super:: を呼び出す必要があります。
  • インタラクティブツール:Enter() から RegisterTool(TSharedPtr<FUICommandInfo> UICommand, FString ToolIdentifier, UInteractiveToolBuilder* Builder, EToolsContextScope ToolScope = EToolsContextScope::Default) を呼び出します。GetInteractiveToolsContext(EToolsContextScope) は UEditorInteractiveToolsContext を返します。モード独自のコンテキストオブジェクトは UEdModeInteractiveToolsContext です。
  • ビューポート入力(MouseMove、InputKey、HandleClick、InputDelta、Tick)を扱うには、ILegacyEdModeViewportInterface(Tools/LegacyEdModeInterfaces.h)を実装します。Render と DrawHUD は ILegacyEdModeWidgetInterface から提供されます。UBaseLegacyWidgetEdMode は両方を実装し、CreateWidgetHelper() が返す FLegacyEdModeWidgetHelper に処理を転送します。
  • GLevelEditorModeTools().ActivateMode(UMyEditorMode::EM_MyEditorMode) で有効化し、IsModeActive / GetActiveScriptableMode で状態を確認し、DeactivateMode で終了します。
  • FEdMode(EdMode.h)はレガシーの共有ポインターモードです。FLegacyEdModeWidgetHelper を継承し、FEditorModeRegistry::Get().RegisterMode<T>(...) / UnregisterMode(...) で登録します。新しいモードは UEdMode で作成してください。

ツールキットを含むモードの完全な実装については、editor-module-setup.md を参照してください。

アセットタイプのファクトリーとサムネイル

UAssetDefinition(モジュール AssetDefinition、ヘッダー AssetDefinition.h)は、5.8 におけるコンテンツブラウザーとの統合ポイントです。UAssetDefinitionDefault(AssetDefinitionDefault.h、UnrealEd)を継承すると、OpenAssets が汎用の FSimpleAssetEditor(AssetDefinitionDefault.cpp:16-25)をすでに開くようになります。定義は CDO から検出されるため、登録呼び出しは不要です。

FText GetAssetDisplayName() const、FLinearColor GetAssetColor() const、TSoftClassPtr<UObject> GetAssetClass() const、TConstArrayView<FAssetCategoryPath> GetAssetCategories() const をオーバーライドします。デフォルトのツールキットのルーティングが要件に合わない場合に限り、EAssetCommandResult OpenAssets(const FAssetOpenArgs& OpenArgs) const をオーバーライドします。

EAssetCategoryPaths は Basic、Animation、Audio、Blueprint、Cinematics、Data、Foliage、FX、Gameplay、AI などを提供します。FAssetCategoryPath(EAssetCategoryPaths::Gameplay, INVTEXT("My Game")) を使って階層化できます。このタイプのコンテキストメニュー項目は、定義クラスではなく、ContentBrowser.AssetContextMenu の UToolMenus 拡張を通じて追加します。

FAssetTypeActions_Base(AssetTypeActions_Base.h、モジュール AssetTools)と IAssetTools::RegisterAssetTypeActions/UnregisterAssetTypeActions は引き続きコンパイルできますが、IAssetTypeActions のメンバーは定義システムへの移行に伴い非推奨になりつつあります。新しいタイプには UAssetDefinition を使用してください。

  • ファクトリー:エディターモジュール内の UFactory サブクラスで、コンストラクター内に SupportedClass、bCreateNew、bEditAfterNew を設定し、FactoryCreateNew(UClass*, UObject*, FName, EObjectFlags, UObject*, FFeedbackContext*) をオーバーライドします。自動的に検出されるため、登録は不要です。
  • サムネイル:UThumbnailRenderer を継承し、Draw(UObject*, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget*, FCanvas*, bool bAdditionalViewFamily) をオーバーライドします。次に、StartupModule で UThumbnailManager::Get().RegisterCustomRenderer(UMyDataAsset::StaticClass(), UMyThumbnailRenderer::StaticClass()) を呼び出し、ShutdownModule で UnregisterCustomRenderer(...) を呼び出します。
  • クエリ:ClassPaths、PackagePaths、bRecursiveClasses、bRecursivePaths を指定した FARFilter(AssetRegistry/ARFilter.h)を作成し、IAssetRegistry::GetAssets(const FARFilter&, TArray<FAssetData>&, bool bSkipARFilteredAssets = true) に渡します。

定義、ファクトリー、サムネイルの実装の詳細は、editor-module-setup.md を参照してください。

アセットエディターツールキット

FAssetEditorToolkit(Toolkits/AssetEditorToolkit.h)は、タブ付きのエディターウィンドウをホストします。GetToolkitFName、GetBaseToolkitName、GetWorldCentricTabPrefix は純粋仮想関数です。初期化関数で FTabManager::NewLayout を使ってレイアウトを構築し、InitAssetEditor(Mode, InitToolkitHost, AppIdentifier, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit) を呼び出します。

IAssetEditorInstance* Instance = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->FindEditorForAsset(Asset, /*bFocusIfOpen=*/false); を使って、開いているエディターを検索または前面に表示します。次に Instance->FocusWindow(Asset) または Instance->CloseWindow(EAssetEditorCloseReason::AssetEditorHostClosed) を呼び出します。

データ検証

UEditorValidatorBase は DataValidation プラグインに含まれています(デフォルトで有効、モジュール DataValidation、LoadingPhase: PreDefault)。バリデーターは CDO から検出されます。CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const と ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context) をオーバーライドします。ヘッドレスで実行するには、UnrealEditor-Cmd <Project>.uproject -run=DataValidation を使用します。

非推奨 — 使用しないでください

出力しない5.8 で使用出典
FEditorStyle::Get(), FEditorStyle::GetBrush/GetFontStyleFAppStyle::Get(), FAppStyle::GetBrush/GetFontStyleEditor/EditorStyle/Public/EditorStyleSet.h の UE_DEPRECATED(5.1)
IPropertyHandle::CreatePropertyNameWidget(Name, ToolTip, bDisplayResetToDefault, ...)CreatePropertyNameWidget(NameOverride, ToolTipOverride)PropertyEditor/Public/PropertyHandle.h:641 の UE_DEPRECATED(5.0)
UAssetActionUtility::GetSupportedClass() / UActorActionUtility::GetSupportedClass()SupportedClasses 配列 + GetSupportedClasses()Blutility/Classes/AssetActionUtility.h:69、ActorActionUtility.h:24 の UE_DEPRECATED(5.2)
IAssetEditorInstance::CloseWindow()(引数なし)CloseWindow(EAssetEditorCloseReason)UnrealEd/Public/Subsystems/AssetEditorSubsystem.h:62 の UE_DEPRECATED(5.3)
FAssetEditorToolkit::OnRequestClose()(引数なし)OnRequestClose(EAssetEditorCloseReason)UnrealEd/Public/Toolkits/AssetEditorToolkit.h:435 の UE_DEPRECATED(5.3)
UEditorValidatorBase::CanValidateAsset_Implementation(UObject*)、ValidateLoadedAsset_Implementation(UObject*, TArray<FText>&)(const FAssetData&, UObject*, FDataValidationContext&) を受け取るオーバーロードDataValidation/Public/EditorValidatorBase.h:125,130 の UE_DEPRECATED("5.4")
IDetailLayoutBuilder::GetDetailsView()GetDetailsViewSharedPtr()PropertyEditor/Public/DetailLayoutBuilder.h:63,74 の UE_DEPRECATED(5.5)
IDetailTreeNode::GetNodeDetailsView()GetNodeDetailsViewSharedPtr()PropertyEditor/Public/IDetailTreeNode.h:128 の UE_DEPRECATED(5.5)
IAssetTypeActions::GetThumbnailOverlayUAssetDefinition + GetThumbnailActionOverlayDeveloper/AssetTools/Public/IAssetTypeActions.h:122 の UE_DEPRECATED(5.5)
FEditorModeRegistry::GetModeInfo のイテレーションヘルパー、OnRegisteredModesChangedUAssetEditorSubsystem::GetEditorModeInfoOrderedByPriority、FindEditorModeInfo、OnEditorModesChangedUnrealEd/Public/EditorModeRegistry.h:98,104,141 の UE_DEPRECATED(4.26)
UEdModeInteractiveToolsContext::UsesDragTools() / OnRegisterViewportInteractions()UsesViewportInteractions() / OnBuildViewportInteractions()(実験的)UnrealEd/Public/Tools/EdModeInteractiveToolsContext.h:427,441 の UE_DEPRECATED(5.8)
FEditorModeTools::GetOverrideCursorVisibility(bool&, bool&, bool)(値渡しの bSoftwareCursorVisible)GetCursorVisibilityOverride(bool&, bool&, bool&)UnrealEd/Public/EditorModeManager.h:321 の UE_DEPRECATED(5.8)

よくある間違い

Runtime モジュールでのエディター用ヘッダー: UnrealEd、PropertyEditor、Blutility、ToolMenus はエディター専用であり、パッケージ化されたビルドにはリンクされません。そのため、これらに無条件で依存する Runtime モジュールはパッケージ化に失敗します(UBT が BuildException をスローします。UnrealEd.Build.cs:12-14)。コードは MyGameEditor に配置してください。#if WITH_EDITOR は、PostEditChangeProperty のようなランタイムクラスのフックにのみ使用してください。

クラスの定義前に IMPLEMENT_MODULE を置く: マクロは FMyGameEditorModule の宣言と両方の関数定義の後に置く必要があります。.cpp の末尾に配置してください。

登録して解除しない: すべての RegisterCustomClassLayout、RegisterCustomPropertyTypeLayout、RegisterCustomRenderer、RegisterStartupCallback には、ShutdownModule に対となる解除処理が必要です。その処理は FModuleManager::Get().IsModuleLoaded(...) で保護してください。対となる処理がないと、Live Coding の再読み込み時にクラッシュします。

トランザクションなしで UObject を編集する: 書き込みの前に、必ず FScopedTransaction と Object->Modify() を使用してください。

UEdMode に対して FEditorModeRegistry::RegisterMode を呼び出す: UEdMode のサブクラスは CDO から自動登録されます。レジストリを使用するのは FEdMode の場合のみです。

Slate のラムダで生の this をキャプチャする: TWeakPtr/TWeakObjectPtr をキャプチャし、使用前に pin してください。詳細パネルのウィジェットはカスタマイズオブジェクトより長く存続します。

値が変わるたびに ForceRefreshDetails() を呼び出す: レイアウトが破棄され、再構築されます。行の構造が変わる場合にのみ呼び出し、値の更新には TAttribute と _Lambda バインディングを使用してください。

LoadingPhase: Default で StartupModule 内から GEditor に触れる: Default のプロジェクトモジュールは、エディターエンジンの作成前に読み込まれます。UToolMenus::RegisterStartupCallback/FCoreDelegates::GetOnPostEngineInit() を通じて遅延させるか(OnPostEngineInit メンバーは UE_DEPRECATED(5.8) です。CoreDelegates.h:240)、PostEngineInit で読み込んでください。

関連スキル

  • ue-module-build-system — .Build.cs の構文、モジュールの種類、Public と Private の依存関係、プラグイン記述子
  • ue-cpp-foundations — UCLASS/UPROPERTY/UFUNCTION 指定子、標準的なサブシステム型の一覧表、UObject のライフサイクル
  • ue-ui-umg-slate — すべてのカスタム行で使用する Slate ウィジェットの基本(SNew、TAttribute、FReply、複合ウィジェット)
  • ue-data-assets-tables — これらのエディターと定義がラップする UDataAsset、UPrimaryDataAsset、UDataTable の型
  • ue-testing-debugging — UE_LOG のカテゴリと詳細度、エディターコードの自動テスト、Insights によるプロファイリング
  • ue-actor-component-architecture — 詳細パネルをカスタマイズするアクタークラスとコンポーネントクラス
  • ue-project-context — .agents/ue-project-context.md の作成と利用
  • ue-blueprint-cpp-interop — C++ を Blueprint に公開する方法:UFUNCTION/UPROPERTY のメタキー、潜在アクション、非同期ノード

コメント