UE4 插件扩展引擎工具栏
UE4 作为游戏引擎,已经提供了非常强大的游戏开发的API。作为游戏制作者来讲,我们需要一些专用的功能辅助我们更好的开发游戏,而不是仅仅从构建游戏逻辑出发。因此也就有了扩展编辑器功能的这个想法,还好 UE4 提供了许多编辑器的接口,便于我们给编辑器添加我们的功能。
下面是一个扩展编辑器工具栏的例子,我们增加了一个自己的按钮。这个在新建插件,选择 Editor Standalone Window 就可以实现此效果。我这里新建一个空的插件,来实现这个效果,以便于理解。
UE4 是以模块的功能构建项目,插件也是一种模块,便于移植更新,所以我们将代码以插件结构的形式呈现。
首先,新建一个空白插件 ExtendedToolBarPlugin ,如红色方框内容
***.uplugin 是插件描述文件
{
"FileVersion": ,
"Version": ,
"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。
***.h 和 ***.cpp 定义了该模块和引擎的接口。
//***.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)
{
Builder.AddToolBarButton(FWindowTestCommands::Get().OpenPluginWindow);
}
以上就是整个插件的结构,编译成功插件就行正常运行。但是仅仅是插件正常运行,并未加入任何功能。
我们开始对 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.添加的到工具栏
UE4 插件扩展引擎工具栏的更多相关文章
- QML插件扩展2(基于C++的插件扩展)
上一节介绍了纯QML的插件扩展方式,这种扩展方式基本满足大部分的扩展需求,下面开始介绍比较小众的基于C++的扩展 (一)更新插件工程 1.更新MyPlugin工程下的qmldir文件,加入plugin ...
- QML插件扩展(一)
准备分两节来介绍QML扩展插件,分别为 (一)基于QML文件的扩展方式 (二)基于C++的插件扩展 这篇先介绍基于QML的插件扩展. 先介绍几个基本概念: qmldir: 用于组织自定义的QML插件, ...
- [Unity]Unity3D编辑器插件扩展和组件扩展
1. 插件扩展 1.1. 命名空间 using UnityEditor; using UnityEngine; //非必需,常用到 1.2. 使用语法 [MenuItem("Assets/M ...
- 百度umeditor富文本编辑器插件扩展
富文本编辑器在WEB开发中经常用到,个人比较喜欢用百度出的ueditor这款,ueditor这款本身支持插件扩展的,但是ueditor的mini版本 umeditor 就没有那么方便了,不过找了很多资 ...
- Chrome插件(扩展)开发全攻略
[干货]Chrome插件(扩展)开发全攻略:https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html
- FireFox 浏览器插件/扩展开发学习
2014-11-08 内容存档在evernote,笔记名"FireFox 浏览器插件/扩展开发学习"
- jQuery的noConflict以及插件扩展
一.noConflict函数 JavaScript有很多插件,如果jQuery对象的$与其他插件冲突,我们可以使用noConflict()方法去掉$或者使用其他的符号代替 注:noConflict() ...
- jQuery笔记之工具方法extend插件扩展
jQuery工具方法 $.extend()插件扩展(工具方法) $.fn.extend()插件扩展(实例方法) 浅度克隆.深度克隆 两个方法基本是一样的,唯一不同的就是调用方式不一样 -------- ...
- Chrome插件(扩展)
[干货]Chrome插件(扩展)开发全攻略 写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均 ...
随机推荐
- 1.1.2最小生成树(Kruskal和Prim算法)
部分内容摘自 勿在浮沙筑高台 http://blog.csdn.net/luoshixian099/article/details/51908175 关于图的几个概念定义: 连通图:在无向图中,若任意 ...
- TSP+Floyd BestCoder Round #52 (div.2) 1002 Victor and Machine
题目传送门 题意:有中文版的 分析:(出题人的解题报告)我们首先需要预处理出任意两个国家之间的最短距离,因为数据范围很小,所以直接用Floyd就行了.之后,我们用f[S][i]表示访问国家的情况为S, ...
- 灰度世界算法(Gray World Algorithm) 分类: 图像处理 Matlab 2014-12-07 18:40 874人阅读 评论(0) 收藏
人的视觉系统具有颜色恒常性,能从变化的光照环境和成像条件下获取物体表面颜色的不变特性,但成像设备不具有这样的调节功能, 不同的光照环境会导致采集的图像颜色与真实颜色存在一定程度的偏差,需要选择合适的颜 ...
- Linux oraenv Tips
Linux for the Oracle DBA -Customizing the Oracle User's Environment There are many ways to customize ...
- Android开发学习——ButterKnife使用
为了码代码的效率,我们有了ButterKnife;其基本使用如下步骤: 1.在Android Studio的Setting中,下载plugin 2.在整个Project的build.gradle中添加 ...
- fastDFS shiro权限校验 redis FreeMark页面静态化
FastDFS是一个轻量级分布式文件系统, 使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传.下载等服务 FastDFS服务端有两个角色:跟踪器(tracker)和存储节点( ...
- Android基础夯实--重温动画(三)之初识Property Animation
每个人都有一定的理想,这种理想决定着他的努力和判断的方向.就在这个意义上,我从来不把安逸和快乐看作生活目的的本身--这种伦理基础,我叫它猪栏的理想.--爱因斯坦 一.摘要 Property Anima ...
- Prim算法以及Kruskal算法
Prim算法主要用于计算最小生成树.算法在选取最小路径的时候需要优化,算法思路:从某个顶点开始,假设v0,此时v0属于最小生成树结点中的一个元素,该集合假设V,剩下的点待选择的点为U,然后找寻V中的点 ...
- 最新最强短视频SDK——来自RDSDK.COM
北京锐动天地信息技术有限公司成立于2007年9月.多年来一直专注于音视频领域核心技术的研发, 拥有Windows.iOS.Android全平台自主知识产权的领先技术产品. 2011年获得新浪战略投资, ...
- Vue.js——打包之后资源路径产生问题
https://blog.csdn.net/qq_30632003/article/details/79353035 https://www.cnblogs.com/diantao/p/7776523 ...