UE4 做为游戏引擎,已经提供了很是强大的游戏开发的API。做为游戏制做者来说,咱们须要一些专用的功能辅助咱们更好的开发游戏,而不是仅仅从构建游戏逻辑出发。所以也就有了扩展编辑器功能的这个想法,还好 UE4 提供了许多编辑器的接口,便于咱们给编辑器添加咱们的功能。编辑器
下面是一个扩展编辑器工具栏的例子,咱们增长了一个本身的按钮。这个在新建插件,选择 Editor Standalone Window 就能够实现此效果。我这里新建一个空的插件,来实现这个效果,以便于理解。ide
UE4 是以模块的功能构建项目,插件也是一种模块,便于移植更新,因此咱们将代码以插件结构的形式呈现。函数
首先,新建一个空白插件 ExtendedToolBarPlugin ,如红色方框内容工具
***.uplugin 是插件描述文件 ui
{ "FileVersion": 3, "Version": 1, "VersionName": "1.0", "FriendlyName": "ExtendedToolBarPlugin", "Description": "", "Category": "Other", "CreatedBy": "Jqm", "CreatedByURL": "", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "CanContainContent": true, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "ExtendedToolBarPlugin",//插件的名字 "Type": "Editor",//插件的类型,修改成Editor . "LoadingPhase": "Default" } ] }
***.Build.cs 是插件中模块 ExtendedToolBarPlugin 的描述,定义了该模块的引用头文件信息,引用库信息,以及扩展依赖等。稍后,咱们主要会在里面添加模块依赖,以便于咱们引用编辑器的 API。this
***.h 和 ***.cpp 定义了该模块和引擎的接口。spa
//***.h
class FExtendedToolBarPluginModule : public IModuleInterface { public: /** IModuleInterface implementation */ //当模块被加载时调用 virtual void StartupModule() override; //当模块被卸载时调用 virtual void ShutdownModule() override;
// 新增函数
void ButtonClicked();
// 向工具栏中添加按钮
void AddToolbarExtension(FToolBarBuilder& Builder);插件
TSharedPtr<class FUICommandList> ButtonClickCommands;
};
void FExtendedToolBarPluginModule::AddToolbarExtension(FToolBarBuilder& Builder)code
{
Builder.AddToolBarButton(FWindowTestCommands::Get().OpenPluginWindow);blog
}
以上就是整个插件的结构,编译成功插件就行正常运行。可是仅仅是插件正常运行,并未加入任何功能。
咱们开始对 Editor Standalone Window 的分析,发如今模块初始化函数 StartupModule() 中,获取了编辑器模块 FLevelEditorModule& LevelEditorModule, 而后新增长了一个扩展 MakeShareable(new FExtender()) ,而后将新增长的扩展加入到编辑器的工具栏管理模块中。 重点是这个新增长的扩展,设置了一些属性,标记扩展的是编辑器的哪一部分,传递了一个命令,这个命令用来响应触发这个按钮的消息,至关于一个回调,等信息。
首先,咱们来构建这个扩展所须要的内容。
咱们第一步建立这个命令:在插件中新建一个类 FWindowTestCommands,该类继承至一个模板类TCommands,该模板类以本身的定义做为模板,而且提供了注册命令和卸载的一系列方法。因此咱们继承它就能够实现编辑器所须要的命令。
// ***.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Framework/Commands/Commands.h" #include "WindowTestStyle.h" class FWindowTestCommands : public TCommands<FWindowTestCommands> { public: FWindowTestCommands() : TCommands<FWindowTestCommands>(TEXT("WindowTest"), NSLOCTEXT("Contexts", "WindowTest", "WindowTest Plugin"), NAME_None, FWindowTestStyle::GetStyleSetName()) { } // TCommands<> interface virtual void RegisterCommands() override; public: TSharedPtr< FUICommandInfo > OpenPluginWindow; };
// ***.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #include "WindowTestCommands.h" #define LOCTEXT_NAMESPACE "FWindowTestModule" void FWindowTestCommands::RegisterCommands() { UI_COMMAND(OpenPluginWindow, "WindowTest", "Bring up WindowTest window", EUserInterfaceActionType::Button, FInputGesture()); } #undef LOCTEXT_NAMESPACE
TCommands<FWindowTestCommands>(TEXT("WindowTest"), NSLOCTEXT("Contexts", "WindowTest", "WindowTest Plugin"), NAME_None, FWindowTestStyle::GetStyleSetName())传递了4个参数,分别是命令的名字,命令的描述,命令的的上下级,命令的风格。
RegisterCommands()将命令进行注册。
命令的风格。再 RegisterCommands()注册编辑器的时候,有个枚举类型 EUserInterfaceActionType::Button ,说明是个按钮,那么这个风格说的就是响应按钮的这个风格,所以,咱们在构造命令是也传入的一个风格。如今咱们新建一个风格,传入给命令。
在插件中新建一个类 FWindowTestStyle ,这个类全是静态函数。它提供了风格的建立,销毁等方法。
// ***.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Styling/SlateStyle.h" /** */ class FWindowTestStyle { public: static void Initialize(); static void Shutdown(); /** reloads textures used by slate renderer */ static void ReloadTextures(); /** @return The Slate style set for the Shooter game */ static const ISlateStyle& Get(); static FName GetStyleSetName(); private: static TSharedRef< class FSlateStyleSet > Create(); private: static TSharedPtr< class FSlateStyleSet > StyleInstance; };
在建立风格时,他寻找了插件下面的资源图片做为本身的Icon。
// ***.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #include "WindowTestStyle.h" #include "Styling/SlateStyleRegistry.h" #include "Framework/Application/SlateApplication.h" #include "SlateGameResources.h" #include "IPluginManager.h" TSharedPtr< FSlateStyleSet > FWindowTestStyle::StyleInstance = NULL; void FWindowTestStyle::Initialize() { if (!StyleInstance.IsValid()) { StyleInstance = Create(); FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); } } void FWindowTestStyle::Shutdown() { FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); ensure(StyleInstance.IsUnique()); StyleInstance.Reset(); } FName FWindowTestStyle::GetStyleSetName() { static FName StyleSetName(TEXT("ExtendedToolBarStyle")); return StyleSetName; } #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) #define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) const FVector2D Icon16x16(16.0f, 16.0f); const FVector2D Icon20x20(20.0f, 20.0f); const FVector2D Icon40x40(40.0f, 40.0f); TSharedRef< FSlateStyleSet > FWindowTestStyle::Create() { TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("ExtendedToolBarStyle")); Style->SetContentRoot(IPluginManager::Get().FindPlugin("ExtendedToolBarPlugin")->GetBaseDir() / TEXT("Resources")); Style->Set("ExtendedToolBarPlugin.ButtonClick", new IMAGE_BRUSH(TEXT("ButtonIcon_40x"), Icon40x40)); return Style; } #undef IMAGE_BRUSH #undef BOX_BRUSH #undef BORDER_BRUSH #undef TTF_FONT #undef OTF_FONT void FWindowTestStyle::ReloadTextures() { if (FSlateApplication::IsInitialized()) { FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); } } const ISlateStyle& FWindowTestStyle::Get() { return *StyleInstance; }
如今,基本上全部资源都准备完毕,就下来就开始写逻辑,将整个流程实现。
在 ExtendedToolBarPlugin 模块中,初始化模块阶段,也就是 StartupModule() 函数中,咱们开始建立风格和命令:
// 注册编辑器插件风格 FWindowTestStyle::Initialize(); FWindowTestStyle::ReloadTextures(); // 注册编辑器插件命令 FWindowTestCommands::Register();
建立插件命令 ExtendedToolBarCommands,这个在模块类定义,
// 建立插件编辑器命令 ExtendedToolBarCommands = MakeShareable(new FUICommandList); ExtendedToolBarCommands->MapAction(FWindowTestCommands::Get().OpenPluginWindow, FExecuteAction::CreateRaw(this, &FExtendedToolBarPluginModule::ButtonClicked), FCanExecuteAction());
而后,加入到编辑器中,
AddToolBarExtension 第一个参数:是指添加到 工具栏的 Settings 部分;
第二个参数是在这个部分的位置,前面,后面或第一个;
第三个参数,是前面建立的命令,传递进去;
第四个参数,是添加到编辑器的按钮;
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); { TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender); ToolbarExtender->AddToolBarExtension("Settings", EExtensionHook::After, JqmToolsEditorCommands, FToolBarExtensionDelegate::CreateRaw(this, &FExtendedToolBarModule::AddToolbarExtension)); LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); }
到此,整个流程走完。
总结一下,按照顺序,应该是初始化模块入口函数里:
1.建立风格
2.建立命令
3.绑定命令
4.建立工具栏扩展
5.添加的到工具栏