DungDaj 发表于 2022-7-9 17:21

UE编辑器扩展

编辑器添加扩展

方法

这里只介绍新的方法

[*]思路很简单,先定位到想要修改的编辑器位置-菜单/工具栏 位置,然后再进行扩展。
ExtendMenu:在Menus这个TMap中找对应的Menu,找到了就返回,没找到就新建一个,然后加入到Menus容器中。
FindOrAddSection:找到菜单条下对应的菜单分节
AddMenuEntryWithCommandList:为分节添加条目,内部是调用AddEntry实现的。就是传入一个Entry来添加到Blocks数组中。UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
{
        FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");
        Section.AddMenuEntryWithCommandList(FAddButtonsCommands::Get().PluginAction, PluginCommands);
}

//这个方法用在ToolBar上,因为有InitToolBarButton
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("AssetEditor.BlueprintEditor.ToolBar");
{
        FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("PluginTools");
        {
                FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FAddButtonsCommands::Get().PluginAction));
                Entry.SetCommandList(PluginCommands);
        }
}

[*]理论上将对应的路径代入上面的方法即可实现编辑器的扩展
[*]常见路径


路径查找方法结合蓝图编辑器工具一起介绍蓝图编辑器工具


[*]编辑器工具控件:创建UMG执行写好的的操作
[*]编辑器工具蓝图:在指定的父类的右键时可找到该工具
介绍一种用蓝图控件查看编辑器工具路径的方法:

[*]创建一个蓝图编辑器控件:并添加一个细节视图,勾选强制隐藏属性可视性


2. 连接如下节点


3. 运行蓝图编辑器控件,可查看到81条路径


Slate控件

为什么有了蓝图控件还要Slate控件?
蓝图控件只能在关卡编辑器里使用,局限性强;Slate可以脱离关卡编辑器单独使用,独立性强。               UMG框架通过蓝图工作,蓝图更接近视觉设计。然而,Slate可以保持干净的代码。Slate常用语法

SNew:创建SWidget
SAssignNew:创建一个SWidget,并且将SWidget对象额外传递给外部某个变量
SLATE_BEGIN_ARGS和SLATE_END_ARGS:是用来声明当前SWidget类的内部FArguments类。每一个自定义的SWidget类,都得加上这样的声明,否则无法通过编译。
SLATE_ATTRIBUTE: 在自定义的FArguments添加新的属性
+:向对应Array中增加一个新的Slots*成员
[]:用于表示“嵌套”子SWidget。[]符号的预算优先级,要高于+符号
.:用来给New出来的Slate控件添加属性
Slate扩展


[*]SlateModule的结构
class FMySlateModule : public IModuleInterface
{
public:

        //注册编辑器的扩展,绑定点击事件,给slate绑定OnSpawnPluginTab
        virtual void StartupModule() override;
        virtual void ShutdownModule() override;
       
        //点击Btn时,唤醒Tab
        void PluginButtonClicked();
       
private:
        //创建编译器扩展
        void RegisterMenus();

        //绑定到指定的TabName的Raw
        TSharedRef<class SDockTab> OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs);

private:
        //插件的命令
        TSharedPtr<class FUICommandList> PluginCommands;
};

[*]简单的扩展:这里介绍 Lambda 和 委托 两种形式
static const FName MyTab0Name("MyTab0");
static const FName MyTab1Name("MyTab1");

void FMySlateModule::StartupModule()
{
...
//绑定Lambda
        FGlobalTabmanager::Get()->RegisterNomadTabSpawner(MyTab0Name,
                FOnSpawnTab::CreateLambda([](const FSpawnTabArgs& SpawnTabArgs) {
                        FText WidgetText = LOCTEXT("WindowMyTab0Text", "MyButton");
                        return SNew(SDockTab)
                                .TabRole(ETabRole::NomadTab)
                                [
                                        // Put your tab content here!
                                        SNew(SButton)
                                        .HAlign(HAlign_Fill)
                                        .VAlign(VAlign_Fill)
                                        .OnClicked_Lambda(
                                                []() {
                                                        UE_LOG(LogTemp, Warning, TEXT("ok"));
                                                        return FReply::Handled();
                                                }
                                        )
                                        [
                                                SNew(STextBlock)
                                                .Text(WidgetText)
                                        ]
                                ];
                        }))
                .SetDisplayName(LOCTEXT("MyTab0TabTitle", "MyTab0"))
                .SetMenuType(ETabSpawnerMenuType::Enabled);

//绑定Raw
        FGlobalTabmanager::Get()->RegisterNomadTabSpawner(MyTab1Name, FOnSpawnTab::CreateRaw(this, &FMySlateModule::OnSpawnPluginTab1))
                .SetDisplayName(LOCTEXT("FMyTab1TabTitle", "MyTab1"))
                .SetMenuType(ETabSpawnerMenuType::Hidden);
}

void FMySlateModule::PluginButtonClicked()
{
      FGlobalTabmanager::Get()->TryInvokeTab(MyTab0Name);
      FGlobalTabmanager::Get()->TryInvokeTab(MyTab1Name);
}

TSharedRef<class SDockTab> FMySlateModule::OnSpawnPluginTab1(const FSpawnTabArgs& SpawnTabArgs)
{
        TSharedRef<SCheckBox> MyCheckBox = SNew(SCheckBox).IsChecked(ECheckBoxState::Unchecked);
        TSharedRef<SEditableText> MyEditableText = SNew(SEditableText).IsEnabled_Lambda(
                () {
                        return MyCheckBox->GetCheckedState() == ECheckBoxState::Checked;
                }
        );

        return SNew(SDockTab)
                .TabRole(ETabRole::NomadTab)
                [
                        SNew(SVerticalBox)
                        + SVerticalBox::Slot().AutoHeight()
                        [
                                MyCheckBox
                        ]
                        + SVerticalBox::Slot().AutoHeight()
                        [
                                MyEditableText
                        ]
                        + SVerticalBox::Slot()
                        .HAlign(HAlign_Center)
                        .VAlign(VAlign_Center)
                        [
                                SNew(STextBlock)
                                .Text_Lambda(
                                        () {
                                                auto res = MyEditableText->GetText();
                                                return res;
                                        }
                                )
                        ]               
                ];
}



MyTab1:勾选框被选上时可以开始输入,输入的内容将在中间同步显示

自定义资产

需要做的三个内容:

[*]自定义的资产类:(继承UObject),作为自定义资产的数据模型类.
[*]相应的Factory类:(继承UFactory),这个工厂类用于被UE4引擎识别,作为可以在ContentBrowser中创建资产(Asset)
[*]相应的AssetTypeActions:指定了诸如Asset的名称,应属于的类别,颜色等内容,在模块中实现自定义资产的类型


注意点:

[*]在Editor模块的配置cs文件中要添加对Runtime模块和UnrealEd的依赖
[*]在插件的uplugin中添加Editor模块



Edotor模块一定是Editor类型,Asset模块根据需求确定类型


[*]AssetTypeAction类:自定义资产的行为



图源:reference

细致的实现流程请参阅 reference,原作代码有两处小错误需注意细节面板扩展



尝试做了两个模块,一个是细节面板,一个是数据面板(可省略)

[*]细节面板

[*]CustomizeObject:继承自UObject,是我们自定义细节的物体本体,在其上添加所需信息
[*]TestDetails:继承自IModuleInterface ,主要用于实现模块的构建和关闭,以及自定义属性
[*]CustomizeWidget:继承自SCompoundWidget,设计细节面板的层级样式
[*]MyTestDetailCustomization:继承自DetailCustomization,用于映射到细节面板和实例化
[*]StructVariableDetail: 继承自IPropertyTypeCustomization,用于detail面板父类下创建子项

[*]把细节信息反射到data面板<UCustomizeObject>

[*]在OnSpawnPluginTab创建的Tab里添加SCustomizeWidget即可

编辑模式扩展

新建 编辑器模式 插件会帮我们直接生成一个案例,我们看看他的实现





[*]MyModeModule:负责Module的注册和注销
[*]MyModeEditorModeToolkit :获取Toolkit的Interface
[*]MyModeEditorMode:设置Mode工具的名字,初始化Toolkit和Commands ,将Commands和Tools里的ToolBuilder绑定
[*]MyModeEditorModeCommands:创建命令
[*]Tools:功能的实现


实现

Reference

★数仅代表对我这篇文章的贡献度,每篇都是非常好的参考资料★★ UE4给编辑器添加菜单栏(Menu),菜单条(MenuBar),工具条(ToolBar)——套路与源码分析
★ Unreal Engine 4(虚幻引擎4)编辑器开发基础课程| ABOUTCG视频教程
★★★ Shijiening/ToolBox: UE4 编辑器工具 (github.com)
★★★★★ 【合集】UE4插件与Slate_哔哩哔哩_bilibili
★★★ Unreal Engine 5 - Writing Plugins in C++ - YouTube
★★ 扬·沃索克 |UE4 #1中的编辑器插件 – 简介 (wlosok.cz)
★★★★★ 【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★】 | キンアジのブログ
★★ (69条消息) UE4 Slate 特殊语法_写文档试验号的博客-CSDN博客
★★★★ (69条消息) UE中创建自定义Asset类_天才宝藏的博客-CSDN博客_ue4 自定义asset
★★ UE4拓展Details面板 - 知乎 (zhihu.com)
页: [1]
查看完整版本: UE编辑器扩展