编辑器 - 使用数据资产新增、配置按钮 - 心意不定的UE4随笔录
使用数据资产新增、配置按钮
成果
- 创建用于描述按钮行为的数据资产。

- 选择行为类型,并设置参数。



- 添加至Config,并填写其按钮的名称等描述性参数

- 重启Editor 可以点击上方按钮使用配置的功能



实现原理
- 首先添加一个插件, 这里推荐
Editor Stanalone Window插件,可以复用部分代码

- 创建
EditorAction类,此类用于定义按钮的行为。
之前的示例里使用到了我提供了两个类:
UEditorAction_OpenWidget: 用于打开指定的EditorUtilityWidgetBlueprintUEditorAction_RunPython: 执行指定的Python脚本
创建 ButtonActionDefine 类,此类继承自DataAsset,其持有一个 EditorAction 类的字段,以使用资产来定义按钮的行为。

-
创建
UButtonToolsGlobalConfig用作配置文件,其持有一个FEditorButtonDefine类实例数组,该类用于定义按钮的名称&行为等。
- 将设置注册进项目设置面板,参考如下代码:
void FButtonToolsModule::RegisterSettings() { ISettingsModule* SettingModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"); if(SettingModule) { SettingModule->RegisterSettings("Project", "Plugins", "ButtonTools", LOCTEXT("ButtonToolsSettings", "ButtonToolsSettings"), LOCTEXT("ButtonToolsSettings", "Configure editor buttons."), GetMutableDefault<UButtonToolsGlobalConfig>()); } }- 生成按钮对应的Commands,此处因为将配置进行了开放,不能直接使用UI_COMMAND宏来进行声明,请参考以下代码:
void FButtonToolsCommands::RegisterConfigCommands() { const UButtonToolsGlobalConfig* Config = GetDefault<UButtonToolsGlobalConfig>(); for (const auto& Define : Config->ButtonDefines) { TSharedPtr<FUICommandInfo>& Command = ConfigCommands.Add_GetRef(TSharedPtr<FUICommandInfo>()); MakeUICommand_InternalUseOnly(this, Command, TEXT(LOCTEXT_NAMESPACE), *Define.CommandName, *(Define.CommandName + "_ToolTip"), TCHAR_TO_ANSI(*("." + Define.CommandName)), *Define.FriendlyName, *Define.Description, EUserInterfaceActionType::Button, FInputGesture()); } }- 定义点击按钮的事件
void FButtonToolsModule::DoConfigButtonAction(int ID) { const UButtonToolsGlobalConfig* Config = GetDefault<UButtonToolsGlobalConfig>(); if(Config->ButtonDefines.IsValidIndex(ID) && Config->ButtonDefines[ID].Action.TryLoad()) { UButtonActionDefine* Define = Cast<UButtonActionDefine>(Config->ButtonDefines[ID].Action.TryLoad()); if(Define && Define->Action) { Define->Action->DoAction(); } } }- 生成Combo按钮
TSharedRef<SWidget> FButtonToolsModule::FillComboButton(TSharedPtr<class FUICommandList> Commands) { FMenuBuilder MenuBuilder(true, Commands); const UButtonToolsGlobalConfig* EditorConfig = GetDefault<UButtonToolsGlobalConfig>(); for (int i = 0; i < FButtonToolsCommands::Get().ConfigCommands.Num(); ++i) { MenuBuilder.AddMenuEntry(FButtonToolsCommands::Get().ConfigCommands[i], NAME_None, TAttribute<FText>(), TAttribute<FText>()); } return MenuBuilder.MakeWidget(); }模块依赖与版本问题
如果需要使用提供的两个Action类型,需要添加对以下模块的依赖:
"Blutility", "PythonScriptPlugin", "UMGEditor", "AssetRegistry",另外,本样例代码基于4.26进行的开发,若您使用更早的版本,则可以存在链接不到
UEditorUtilityWidgetBlueprint该类的情况,这是因为UE在早期版本中并未将此类开发出来,可以给该类加上导出宏以解决该问题。// add BLUTILITY_API class BLUTILITY_API UEditorUtilityWidgetBlueprint : public UWidgetBlueprint源码参考
ButtonActionDefine.h & cpp
// .h #pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "ButtonActionDefine.generated.h" UCLASS(BlueprintType) class BUTTONTOOLS_API UButtonActionDefine : public UDataAsset { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Instanced) UEditorAction* Action; }; UCLASS(BlueprintType, EditInlineNew) class BUTTONTOOLS_API UEditorAction : public UObject { GENERATED_BODY() public: virtual void DoAction() const PURE_VIRTUAL(UEditorAction::DoAction) }; UCLASS(BlueprintType, EditInlineNew) class BUTTONTOOLS_API UEditorAction_OpenWidget : public UEditorAction { GENERATED_BODY() protected: UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, meta = (AllowedClasses = "EditorUtilityWidgetBlueprint")) FSoftObjectPath WidgetBlueprintPath; public: virtual void DoAction() const override; }; UCLASS(BlueprintType, EditInlineNew) class BUTTONTOOLS_API UEditorAction_RunPython : public UEditorAction { GENERATED_BODY() protected: UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) FString Script; public: virtual void DoAction() const override; }; // .cpp #include "ButtonActionDefine.h" #include "EditorUtilitySubsystem.h" #include "EditorUtilityWidgetBlueprint.h" #include "IPythonScriptPlugin.h" #include "AssetRegistry/AssetRegistryModule.h" void UEditorAction_OpenWidget::DoAction() const { UEditorUtilitySubsystem* EditorUtilitySubsystem = GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>(); if (EditorUtilitySubsystem) { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry"); UEditorUtilityWidgetBlueprint* Widget = Cast<UEditorUtilityWidgetBlueprint>(WidgetBlueprintPath.TryLoad()); if (Widget) { EditorUtilitySubsystem->SpawnAndRegisterTab(Widget); } } } void UEditorAction_RunPython::DoAction() const { IPythonScriptPlugin::Get()->ExecPythonCommand(*Script); }ButtonToolsGlobalConfig.h
//ButtonToolsGlobalConfig.h #pragma once #include "CoreMinimal.h" #include "ButtonToolsGlobalConfig.generated.h" USTRUCT(BlueprintType) struct FEditorButtonDefine { GENERATED_BODY() public: // 用于注册UICommand时的CommandID UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) FString CommandName; // 用于注册UICommand时的展示名 UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) FString FriendlyName; // 用于注册UICommand时的描述 UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) FString Description; // 该按钮点击时的事件DA路径 UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, meta=(AllowedClasses="ButtonActionDefine")) FSoftObjectPath Action; }; UCLASS(config=Game, defaultconfig) class BUTTONTOOLS_API UButtonToolsGlobalConfig : public UObject { GENERATED_BODY() public: UPROPERTY(Config, EditDefaultsOnly, Category = ButtonTools) TArray<FEditorButtonDefine> ButtonDefines; };ButtonToolsCommands.h & cpp
// .h #pragma once #include "CoreMinimal.h" #include "Framework/Commands/Commands.h" #include "ButtonToolsStyle.h" class FButtonToolsCommands : public TCommands<FButtonToolsCommands> { public: FButtonToolsCommands() : TCommands<FButtonToolsCommands>(TEXT("ButtonTools"), NSLOCTEXT("Contexts", "ButtonTools", "ButtonTools Plugin"), NAME_None, FButtonToolsStyle::GetStyleSetName()) { } // TCommands<> interface virtual void RegisterCommands() override; void RegisterConfigCommands(); public: TSharedPtr< FUICommandInfo > OpenPluginWindow; TArray<TSharedPtr< FUICommandInfo >> ConfigCommands; }; // .cpp #include "ButtonToolsCommands.h" #include "ButtonToolsGlobalConfig.h" #define LOCTEXT_NAMESPACE "FButtonToolsModule" void FButtonToolsCommands::RegisterCommands() { UI_COMMAND(OpenPluginWindow, "ButtonTools", "Bring up ButtonTools window", EUserInterfaceActionType::Button, FInputGesture()); RegisterConfigCommands(); } void FButtonToolsCommands::RegisterConfigCommands() { const UButtonToolsGlobalConfig* Config = GetDefault<UButtonToolsGlobalConfig>(); for (const auto& Define : Config->ButtonDefines) { TSharedPtr<FUICommandInfo>& Command = ConfigCommands.Add_GetRef(TSharedPtr<FUICommandInfo>()); MakeUICommand_InternalUseOnly(this, Command, TEXT(LOCTEXT_NAMESPACE), *Define.CommandName, *(Define.CommandName + "_ToolTip"), TCHAR_TO_ANSI(*("." + Define.CommandName)), *Define.FriendlyName, *Define.Description, EUserInterfaceActionType::Button, FInputGesture()); } } #undef LOCTEXT_NAMESPACEButtonTools.h & cpp
// .h #pragma once #include "CoreMinimal.h" #include "Modules/ModuleManager.h" class FToolBarBuilder; class FMenuBuilder; class FButtonToolsModule : public IModuleInterface { public: /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; /** This function will be bound to Command (by default it will bring up plugin window) */ void PluginButtonClicked(); private: void RegisterMenus(); void RegisterSettings(); void UnregisterSettings(); TSharedRef<class SDockTab> OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs); void DoConfigButtonAction(int ID); TSharedRef<SWidget> FillComboButton(TSharedPtr<class FUICommandList> Commands); private: TSharedPtr<class FUICommandList> PluginCommands; }; // .cpp // Copyright Epic Games, Inc. All Rights Reserved. #include "ButtonTools.h" #include "ButtonToolsStyle.h" #include "ButtonToolsCommands.h" #include "LevelEditor.h" #include "Widgets/Docking/SDockTab.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Text/STextBlock.h" #include "ToolMenus.h" #include "ButtonToolsGlobalConfig.h" #include "ButtonActionDefine.h" #include "ISettingsModule.h" static const FName ButtonToolsTabName("ButtonTools"); #define LOCTEXT_NAMESPACE "FButtonToolsModule" void FButtonToolsModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module FButtonToolsStyle::Initialize(); FButtonToolsStyle::ReloadTextures(); FButtonToolsCommands::Register(); RegisterSettings(); PluginCommands = MakeShareable(new FUICommandList); for(int i=0;i<FButtonToolsCommands::Get().ConfigCommands.Num();++i) { PluginCommands->MapAction( FButtonToolsCommands::Get().ConfigCommands[i], FExecuteAction::CreateRaw(this, &FButtonToolsModule::DoConfigButtonAction, i), FCanExecuteAction()); } UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FButtonToolsModule::RegisterMenus)); /*FGlobalTabmanager::Get()->RegisterNomadTabSpawner(ButtonToolsTabName, FOnSpawnTab::CreateRaw(this, &FButtonToolsModule::OnSpawnPluginTab)) .SetDisplayName(LOCTEXT("FButtonToolsTabTitle", "ButtonTools")) .SetMenuType(ETabSpawnerMenuType::Hidden);*/ } void FButtonToolsModule::ShutdownModule() { // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, // we call this function before unloading the module. UToolMenus::UnRegisterStartupCallback(this); UToolMenus::UnregisterOwner(this); UnregisterSettings(); FButtonToolsStyle::Shutdown(); FButtonToolsCommands::Unregister(); FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(ButtonToolsTabName); } TSharedRef<SDockTab> FButtonToolsModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) { FText WidgetText = FText::Format( LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"), FText::FromString(TEXT("FButtonToolsModule::OnSpawnPluginTab")), FText::FromString(TEXT("ButtonTools.cpp")) ); return SNew(SDockTab) .TabRole(ETabRole::NomadTab) [ // Put your tab content here! SNew(SBox) .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(WidgetText) ] ]; } void FButtonToolsModule::DoConfigButtonAction(int ID) { const UButtonToolsGlobalConfig* Config = GetDefault<UButtonToolsGlobalConfig>(); if(Config->ButtonDefines.IsValidIndex(ID) && Config->ButtonDefines[ID].Action.TryLoad()) { UButtonActionDefine* Define = Cast<UButtonActionDefine>(Config->ButtonDefines[ID].Action.TryLoad()); if(Define && Define->Action) { Define->Action->DoAction(); } } } TSharedRef<SWidget> FButtonToolsModule::FillComboButton(TSharedPtr<class FUICommandList> Commands) { FMenuBuilder MenuBuilder(true, Commands); const UButtonToolsGlobalConfig* EditorConfig = GetDefault<UButtonToolsGlobalConfig>(); for (int i = 0; i < FButtonToolsCommands::Get().ConfigCommands.Num(); ++i) { MenuBuilder.AddMenuEntry(FButtonToolsCommands::Get().ConfigCommands[i], NAME_None, TAttribute<FText>(), TAttribute<FText>()); } return MenuBuilder.MakeWidget(); } void FButtonToolsModule::PluginButtonClicked() { FGlobalTabmanager::Get()->TryInvokeTab(ButtonToolsTabName); } void FButtonToolsModule::RegisterMenus() { // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner FToolMenuOwnerScoped OwnerScoped(this); { UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); { FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); for (int i = 0; i < FButtonToolsCommands::Get().ConfigCommands.Num(); ++i) { Section.AddMenuEntryWithCommandList(FButtonToolsCommands::Get().ConfigCommands[i], PluginCommands); } } } { UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); { FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings"); { Section.AddEntry(FToolMenuEntry::InitComboButton( "ButtonTools", FUIAction( FExecuteAction(), FCanExecuteAction(), FGetActionCheckState() ), FOnGetContent::CreateRaw(this, &FButtonToolsModule::FillComboButton, PluginCommands), LOCTEXT("ButtonTools", "ButtonTools"), LOCTEXT("ButtonTools", "ButtonTools"), FSlateIcon() )); } } } } void FButtonToolsModule::RegisterSettings() { ISettingsModule* SettingModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"); if(SettingModule) { SettingModule->RegisterSettings("Project", "Plugins", "ButtonTools", LOCTEXT("ButtonToolsSettings", "ButtonToolsSettings"), LOCTEXT("ButtonToolsSettings", "Configure editor buttons."), GetMutableDefault<UButtonToolsGlobalConfig>()); } } void FButtonToolsModule::UnregisterSettings() { ISettingsModule* SettingModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"); if (SettingModule) { SettingModule->UnregisterSettings("Project", "Plugins", "ButtonTools"); } } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FButtonToolsModule, ButtonTools)

浙公网安备 33010602011771号