0


【虚幻引擎】UE4/UE5插件

一、插件分类

插件分为七大类

  • Blank:空白插件,可以从头开始自己定义想要的插件风格和内容,用此模板创建的插件不会有注册或者菜单输入。
  • BlueprintLibrary:创建一个含有蓝图函数库的插件,此模板函数都是静态全局函数,可以在蓝图中直接调用。
  • ContentOnly:创建一个只包含内容的空白文件
  • Editor Toolbar Button:创建关卡编辑器的工具栏中的按钮插件,首先在创建的点击事件中实现一些内容
  • Editor Standalone Window:创建关卡编辑器的工具栏中的按钮插件,点击按钮可调出一个空白选项独立卡窗口
  • Editor Mode:创建一个含有编辑器模式的插件
  • Third Party Library:创建一个已包含第三方库的插件

二、插件模块

插件可含有任意数量的模块源目录。多数插件仅有一个模块(但可创建多个模块,例如插件包含纯编辑器功能时),及游戏期间要运行的其他代码。

插件源文件的大部分布局与引擎中其他C++模块相同。

**在模块的

Source

目录(或其子目录)内,插件可在标头文件中声明新反映的类型(

UCLASS

USTRUCT

等)。引擎的构建系统将检测此类文件,并按需要生成代码支持新类型。需遵守C++模块中使用

Uobjects

时的一般规则,例如在模块的源文件中包含生成的标头文件和模块

generated.inl

文件。**

UE4支持共生模块和插件。通过在自身.uproject文件中启用插件,项目模块可依赖插件。类似地,通过在自身.uplugin文件中启用其他插件,插件可表明依赖性。但其中有一项重要限制:插件和模块将拆分为若干层级,仅能依赖同一级或更高级的其他插件或模块。例如,项目模块可依赖引擎模块,但引擎模块无法依赖项目模块。这是因为引擎(及其所有插件和模块)的级别高于项目,须能在无项目的情况下编译。

三、引擎插件和项目插件

虚幻引擎4的

Engine

目录下包含部分内置插件。引擎插件和项目插件类似,但可用于所有项目。此类插件通常由引擎和工具程序员创建,目的在于提供可在多个项目中使用并能在单一位置维护的基线功能。利用此功能,用户可直接添加或覆盖引擎功能,而无需修改引擎代码。

项目插件位于项目目录的

Plugins

子文件夹下,将在引擎或编辑器启动时被探测和加载。如插件包含具有

Source

文件夹(和

.Build.cs

文件)的模块,插件代码将被自动添加到生成的C++项目文件,以便在开发项目时开发插件。编译项目时,有可用源的插件都被作为游戏依赖项进行编译。项目生成器将忽略无

Source

文件夹的插件,其不会出现在C++项目文件中,但若存在二进制文件,启动时仍将加载此类插件。

插件的描述,描述的文件是Json格式,以官方案例为准

{
    "FileVersion" :3,
    "Version" :1,
    "VersionName" :"1.0",
    "FriendlyName" :"UObject Example Plugin",
    "Description" :"An example of a plugin which declares its own UObject type.This can be used as a starting point when creating your own plugin.",
    "Category" :"Examples",
    "CreatedBy" :"Epic Games, Inc.",
    "CreatedByURL" :"http://epicgames.com",
    "DocsURL" :"",
    "MarketplaceURL" :"",
    "SupportURL" :"",
    "EnabledByDefault" : true,
    "CanContainContent" : false,
    "IsBetaVersion" : false,
    "Installed" : false,
    "Modules" :
    [
        {
            "Name" :"UObjectPlugin",
            "Type" :"Developer",
            "LoadingPhase" :"Default"
        }
    ]
}

四、案例分析Editor Standalone Window

创建一个Editor Standalone Window插件,命名为:MyEditorStandaloneWindow

创建成功之后VS会出现Plugins

出现三个cpp文件

MyEditorStandaloneWindow:主函数所有的主要逻辑都是写在这里,包含了注册命令,布局分布,按钮点击事件等。

MyEditorStandaloneWindowCommand:声明一些命令变量,注册函数等。

MyEditorStandaloneWindowStyle:插件的样式风格。

创建好之后我们会在编辑器工具栏中看到我们的插件

点击这个按钮触发的事件通过MapAction创建代理函数绑定,执行

创建独立窗口

void FMyEditorStandaloneWindowModule::PluginButtonClicked()
{
    FGlobalTabmanager::Get()->TryInvokeTab(MyEditorStandaloneWindowTabName);
}

内容的注册通过FGlobalTabmanager::Get()->RegisterNomadTabSpawner

FGlobalTabmanager::Get()->RegisterNomadTabSpawner(MyEditorStandaloneWindowTabName, FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::OnSpawnPluginTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyEditorStandaloneWindow"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);

他本质上就是一个SDockTab,返回的是一个TSharedRef<SDockTab>的引用,

TSharedRef<SDockTab> FMyEditorStandaloneWindowModule::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("FMyEditorStandaloneWindowModule::OnSpawnPluginTab")),
        FText::FromString(TEXT("MyEditorStandaloneWindow.cpp"))
        );

    return SNew(SDockTab)
        .TabRole(ETabRole::NomadTab)
        [
            // Put your tab content here!
            SNew(SBox)
            .HAlign(HAlign_Center)
            .VAlign(VAlign_Center)
            [
                SNew(STextBlock)
                .Text(WidgetText)
            ]
        ];
}

五、自定义插件内容

首先打开UI扩展点

这些绿色的扩展点主要是用于菜单按钮和工具按钮的排序的,添加一个绿色的扩展点统管N个菜单按钮,如果你可以自定义自己的UI扩展点,并将自己的扩展点下的菜单按钮安插在某个扩展点之后。也就是UI扩展点是用于布局菜单栏和工具栏的顺序的。

第一步:首先加载模块

//首先加载模块FExtend最后都在FLevelEditorModule中管理
    FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");

第二步:创建对象

{
        TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
        MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FMyEditorStandaloneWindowModule::AddMenuExtension));

        LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
    }
    {
        TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
        ToolbarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FMyEditorStandaloneWindowModule::AddToolbarExtension));

        LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);

    }

第三步:注册绑定对象事件

FGlobalTabmanager::Get()->RegisterNomadTabSpawner(MyEditorStandaloneWindowTabName, FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::OnSpawnPluginTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyTestWindow"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);
    FGlobalTabmanager::Get()->RegisterNomadTabSpawner("MyWindow1", FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::SpawnCustomTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyWindow1"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);
    FGlobalTabmanager::Get()->RegisterNomadTabSpawner("MyWindow2", FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::SpawnCustomTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyWindow2"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);

第四步:FTabManager重新布局

const TSharedRef<SDockTab> NomadTab = SNew(SDockTab)
        .TabRole(ETabRole::NomadTab);
    if (!MyWindowTabManager.IsValid())
    {
        MyWindowTabManager = FGlobalTabmanager::Get()->NewTabManager(NomadTab);
    }
    if (!MyWindowLayout.IsValid())
    {
        MyWindowLayout = FTabManager::NewLayout("TestLayoutWindow")
            ->AddArea
            (
                FTabManager::NewPrimaryArea()
                ->SetOrientation(Orient_Vertical)
                ->Split
                (
                    FTabManager::NewStack()
                    ->SetSizeCoefficient(.5f)
                    ->AddTab("MyWindow1", ETabState::OpenedTab)
                )
                ->Split
                (
                    FTabManager::NewStack()
                    ->SetSizeCoefficient(.5f)
                    ->AddTab("MyWindow2", ETabState::OpenedTab)
                )
            );
        
    }
    
    TSharedRef<SWidget> TabContents = MyWindowTabManager->RestoreFrom(MyWindowLayout.ToSharedRef(), TSharedPtr<SWindow>()).ToSharedRef();
    NomadTab->SetContent(TabContents);

效果如下:

完整代码附上:

MyEditorStandaloneWindow.h文件

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class FToolBarBuilder;
class FMenuBuilder;

class FMyEditorStandaloneWindowModule : 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();

    TSharedRef<class SDockTab> OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs);

private:
    TSharedPtr<class FUICommandList> PluginCommands;

    void AddMenuExtension(FMenuBuilder& Builder);
    void AddToolbarExtension(FToolBarBuilder& Builder);
    TSharedRef<SDockTab> SpawnCustomTab(const FSpawnTabArgs& Arg);
    TSharedPtr<FTabManager> MyWindowTabManager;
    TSharedPtr<FTabManager::FLayout> MyWindowLayout;

};

MyEditorStandaloneWindow.cpp文件

// Copyright Epic Games, Inc. All Rights Reserved.

#include "MyEditorStandaloneWindow.h"
#include "MyEditorStandaloneWindowStyle.h"
#include "MyEditorStandaloneWindowCommands.h"
#include "LevelEditor.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Text/STextBlock.h"
#include "ToolMenus.h"

static const FName MyEditorStandaloneWindowTabName("MyEditorStandaloneWindow");

#define LOCTEXT_NAMESPACE "FMyEditorStandaloneWindowModule"

void FMyEditorStandaloneWindowModule::StartupModule()
{
    // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
    
    FMyEditorStandaloneWindowStyle::Initialize();
    FMyEditorStandaloneWindowStyle::ReloadTextures();

    FMyEditorStandaloneWindowCommands::Register();
    
    PluginCommands = MakeShareable(new FUICommandList);

    PluginCommands->MapAction(
        FMyEditorStandaloneWindowCommands::Get().OpenPluginWindow,
        FExecuteAction::CreateRaw(this, &FMyEditorStandaloneWindowModule::PluginButtonClicked),
        FCanExecuteAction());

    //UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FMyEditorStandaloneWindowModule::RegisterMenus));
    
    //首先加载模块FExtend最后都在FLevelEditorModule中管理
    FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
    {
        TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
        MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FMyEditorStandaloneWindowModule::AddMenuExtension));

        LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
    }
    {
        TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
        ToolbarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FMyEditorStandaloneWindowModule::AddToolbarExtension));

        LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);

    }
    FGlobalTabmanager::Get()->RegisterNomadTabSpawner(MyEditorStandaloneWindowTabName, FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::OnSpawnPluginTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyTestWindow"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);
    FGlobalTabmanager::Get()->RegisterNomadTabSpawner("MyWindow1", FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::SpawnCustomTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyWindow1"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);
    FGlobalTabmanager::Get()->RegisterNomadTabSpawner("MyWindow2", FOnSpawnTab::CreateRaw(this, &FMyEditorStandaloneWindowModule::SpawnCustomTab))
        .SetDisplayName(LOCTEXT("FMyEditorStandaloneWindowTabTitle", "MyWindow2"))
        .SetMenuType(ETabSpawnerMenuType::Hidden);
    
}

void FMyEditorStandaloneWindowModule::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);

    FMyEditorStandaloneWindowStyle::Shutdown();

    FMyEditorStandaloneWindowCommands::Unregister();

    FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(MyEditorStandaloneWindowTabName);
}

TSharedRef<SDockTab> FMyEditorStandaloneWindowModule::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("FMyEditorStandaloneWindowModule::OnSpawnPluginTab")),
        FText::FromString(TEXT("MyEditorStandaloneWindow.cpp"))
        );*/

    //return SNew(SDockTab)
    //    .TabRole(ETabRole::NomadTab)
    //    [
    //        // Put your tab content here!
    //        SNew(SBox)
    //        .HAlign(HAlign_Center)
    //        .VAlign(VAlign_Center)
    //        [
    //            SNew(STextBlock)
    //            .Text(WidgetText)
    //        ]
    //    ];
    const TSharedRef<SDockTab> NomadTab = SNew(SDockTab)
        .TabRole(ETabRole::NomadTab);
    if (!MyWindowTabManager.IsValid())
    {
        MyWindowTabManager = FGlobalTabmanager::Get()->NewTabManager(NomadTab);
    }
    if (!MyWindowLayout.IsValid())
    {
        MyWindowLayout = FTabManager::NewLayout("TestLayoutWindow")
            ->AddArea
            (
                FTabManager::NewPrimaryArea()
                ->SetOrientation(Orient_Vertical)
                ->Split
                (
                    FTabManager::NewStack()
                    ->SetSizeCoefficient(.5f)
                    ->AddTab("MyWindow1", ETabState::OpenedTab)
                )
                ->Split
                (
                    FTabManager::NewStack()
                    ->SetSizeCoefficient(.5f)
                    ->AddTab("MyWindow2", ETabState::OpenedTab)
                )
            );
        
    }
    
    TSharedRef<SWidget> TabContents = MyWindowTabManager->RestoreFrom(MyWindowLayout.ToSharedRef(), TSharedPtr<SWindow>()).ToSharedRef();
    NomadTab->SetContent(TabContents);
    return NomadTab;
}

void FMyEditorStandaloneWindowModule::AddMenuExtension(FMenuBuilder& Builder)
{
    Builder.AddMenuEntry(FMyEditorStandaloneWindowCommands::Get().OpenPluginWindow);
}

void FMyEditorStandaloneWindowModule::AddToolbarExtension(FToolBarBuilder& Builder)
{
    Builder.AddToolBarButton(FMyEditorStandaloneWindowCommands::Get().OpenPluginWindow);
}

TSharedRef<SDockTab> FMyEditorStandaloneWindowModule::SpawnCustomTab(const FSpawnTabArgs& Arg)
{

    return SNew(SDockTab);
}

void FMyEditorStandaloneWindowModule::PluginButtonClicked()
{
    FGlobalTabmanager::Get()->TryInvokeTab(MyEditorStandaloneWindowTabName);
}

void FMyEditorStandaloneWindowModule::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");
            Section.AddMenuEntryWithCommandList(FMyEditorStandaloneWindowCommands::Get().OpenPluginWindow, PluginCommands);
        }
    }

    {
        UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar");
        {
            FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings");
            {
                FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FMyEditorStandaloneWindowCommands::Get().OpenPluginWindow));
                Entry.SetCommandList(PluginCommands);
            }
        }
    }
}

#undef LOCTEXT_NAMESPACE
    
IMPLEMENT_MODULE(FMyEditorStandaloneWindowModule, MyEditorStandaloneWindow)
标签: 虚幻 ue4 ue5

本文转载自: https://blog.csdn.net/qq_43021038/article/details/129547672
版权归原作者 飞起的猪 所有, 如有侵权,请联系我们删除。

“【虚幻引擎】UE4/UE5插件”的评论:

还没有评论