引擎版本 4.22.0
前言
在实际的开发中我们可能需要创建自定义资源,很遗憾的是官方文档中并没有提供相关教程。本文将以引擎的Editor Widget创建为例配合源码介绍如何创建自定义资源。
注意源码图片看不清楚的看这里:http://note.youdao.com/noteshare?id=54fed93922a7fbeed459519437e30abe
Editor Widget简介
![a0660e4ae1f043878d9ea11f1a52aa8a.png](https://i-blog.csdnimg.cn/blog_migrate/a3c51a1bfefa94f869a702db24e00e3e.jpeg)
先上官方文档关于这方面的介绍:
https://docs.unrealengine.com/zh-CN/Engine/UMG/UserGuide/EditorUtilityWidgets/index.html
简单来讲Editor Widget就是在编辑器环境下使用的Widget,只不过该Widget可以显示在编辑器窗口中罢了。该资源为蓝图资源,类型为UEditorUtilityWidgetBlueprint,该蓝图资源的ParentClass为UEditorUtilityWidget。
![437a0b9d945f4ba36aa0bed11222695c.png](https://i-blog.csdnimg.cn/blog_migrate/cf93b2eb13581420ea3722d6bbdf7f03.jpeg)
我们常用的UserWidget同样也是蓝图资源,类型为UWidgetBlueprint,ParentClass为UUserWidget。
看一下类结构示意图:
![83e1b631a8b3b76105df193b213d23f5.png](https://i-blog.csdnimg.cn/blog_migrate/a534f71b9c38a32dd3283ed9fd927b82.jpeg)
可以看出EditorWidget是从UserWidget扩展而来,因为有自己的蓝图类,所以是独立的资源。
自定义资源创建相关类
- AssetTypeActions
AssetTypeActions是用来创建资源的入口,常见的是我们在内容浏览器中空白出右键出现的一系列将要创建的资源列表:
![0a258a1a48c1da6603356ee2287e9008.png](https://i-blog.csdnimg.cn/blog_migrate/dd669c8fcca4bd4f0114b74d0dad9d34.jpeg)
以及右键一个资源出现与该资源相关的一些操作:
![dc49ae7ef2bbeb31aa891fc7db67eda6.png](https://i-blog.csdnimg.cn/blog_migrate/1ea773432b63fb60f96ec388344cb178.jpeg)
该类中还会指定打开该资源的编辑器,例如我们的EditorWidget双击后打开的编辑器就是专门设计的编辑器:
![8c20362ff97501ac6c44af6a8c5f1d5b.png](https://i-blog.csdnimg.cn/blog_migrate/a945999db4d529ed9db7476f89acf6ba.jpeg)
看一下EditorWidget关于该类的源码:
![02688141fe33bd4096494bc881533cc8.png](https://i-blog.csdnimg.cn/blog_migrate/571f0a122b9433c23804c0c244f96790.jpeg)
接下来挑几个重要的也是必须重写的方法介绍下:
1. GetName是用来显示该资源的类型名,由于被指定为EditorWidget
![373ca78358b3293b103b8e00ebce22d5.png](https://i-blog.csdnimg.cn/blog_migrate/d6917448623b62d3732ea1e36b40b0c1.png)
所以当我们将鼠标悬浮在该类型资源上时Tip中会显示对于的类型名:
![4c83f2992e99af5764cb6075964a9754.png](https://i-blog.csdnimg.cn/blog_migrate/be3ddd6bc66e0d4d9554c69b51270730.jpeg)
2. GetTypeColor是该资源的图标颜色:
![f9f8b31202d31e5752949051db17e7bb.png](https://i-blog.csdnimg.cn/blog_migrate/272ed6982fbc7dde2ba38eb9ea03243f.jpeg)
3. GetSupportedClass指定了资源对应的类:
![fdbae134d45e54d0951bd7675224ced6.png](https://i-blog.csdnimg.cn/blog_migrate/ef3e2c9754db7ac2271fe1914e4e8b7d.png)
4. HasActions指定该资源是否有相应的右键操作选项:
![e55d8d288b3487e6253aad5acd9721e8.png](https://i-blog.csdnimg.cn/blog_migrate/19315a366517eac7689bd536f9cb3898.png)
5. GetActions定义了右键操作选项:
![129c3554908711d069bf6cb547d79b73.png](https://i-blog.csdnimg.cn/blog_migrate/8a74f7e0d20e0f1e660f4e6bb0d54192.jpeg)
从图中可以看出EditorWidget中定义了一个右键操作选项:Run Editor Utility Widget,也就是在窗口中预览界面:
![a84bb5a1291da4502d64025563ec3690.png](https://i-blog.csdnimg.cn/blog_migrate/d4d2bd73b6c07e647bed8ab274e5857e.jpeg)
6. OpenAssetEditor中指定了打开资源的编辑器,具体查看源码
7. GetCategories中指定了创建资源的选项在右键菜单中的分组:
![cb584987d83ee78b7612bc8541430c37.png](https://i-blog.csdnimg.cn/blog_migrate/0597cee545ad075cb84f09fa8355596a.jpeg)
- 资源类
EditorWidget的资源类为UEditorUtilityWidgetBlueprint:
![d33199cad9125286decbba7062c36183.png](https://i-blog.csdnimg.cn/blog_migrate/983118b2017acd5606f64855aaa6c2c9.jpeg)
同样地介绍下主要的方法:
1. GetReparentingRules方法指定了更改蓝图资源ParentClass的规则:
![d045c4ad3dc8ef7d51821e935f26c040.png](https://i-blog.csdnimg.cn/blog_migrate/8f23c580b2e7aaad27ccbdc16d64b21d.jpeg)
从图中可见代码中定死了只能使用UEditorUtilityWidget及其子类:
![00061d5c4fee7542a18ca750e5092562.png](https://i-blog.csdnimg.cn/blog_migrate/d35f4799ec19152af57e60a8720d3e23.jpeg)
- Factory
每个自定义资源还需要有一个创建该资源的工厂:
![a7375949fb403e6015ff9ef7a12891c1.png](https://i-blog.csdnimg.cn/blog_migrate/3ec969a7d2a4d2ccc6d1c4775bff5d6c.jpeg)
构造函数中需要进行一些设置:
![91e2aeb710e64ea54b83f98456f06cf8.png](https://i-blog.csdnimg.cn/blog_migrate/ba8f710521bec817d0d44101b5c97365.jpeg)
SupportClass就是资源类,ParentClass就是蓝图父类。
同样我们需要重写FactoryCreateNew方法,里面主要创建蓝图资源并指定其父类,具体可参考源码。FactoryCreateNew方法的调用时机在资源命名完毕并提交的时候。
注册AssetCategory
自定义资源在右键创建入口需要有新的分组则需要注册,例如EditorWidget就在BlutilityModule模块启动函数中注册了EditorUtilities组:
![9f9fbc6cea6d7b3080f9a64795c82660.png](https://i-blog.csdnimg.cn/blog_migrate/1c7cf1c5ffa647e29b88f613b9b1c582.png)
注意不要忘记了在AssetTypeActions的GetCategories方法中指定自定义分组:
![1e207e28b096f45d990b34636a2d840c.png](https://i-blog.csdnimg.cn/blog_migrate/632360f65e708aa1b701d98e2afa3416.png)
源码中是将注册的资源分组存在模块的EditorUtilityCategory变量中,AssetTypeActions类中就通过模块获得资源分组。
需要注意的是有一种更为方便的方法就是通过AssetTool的FindAdvancedAssetCategory方法获取:
![571b0b8ae7db1698e37c1074b54c1844.png](https://i-blog.csdnimg.cn/blog_migrate/e099524bc01702d447743633b88309db.png)
注册AssetTypeActions
为保证AssetTypeActions生效还得将其注册,源码是在BlutilityModule模块启动时注册的:
![747d468c78087d39a067b6f411926d4f.png](https://i-blog.csdnimg.cn/blog_migrate/9a31312331bd1cf73245431f71eb3510.png)
注销AssetTypeActions
BlutilityModule关闭时会注销AssetTypeActions:
![744efe21e8d798b9f1e9bd30ee686401.png](https://i-blog.csdnimg.cn/blog_migrate/7cf86c01c058038790ef62f142a28149.jpeg)
实践
笔者需要创建一个默认父类为MyUserWidget的Widget资源,所以就仿照Editor Widget实现了,不妨看下效果:
![1ce0969f92b3da6227d6a1e0474d6f05.png](https://i-blog.csdnimg.cn/blog_migrate/280508891fc3d5cf05874d4fdeeaf34d.jpeg)
![1334a24398ba95d5aefe52c4112724f3.png](https://i-blog.csdnimg.cn/blog_migrate/cb67f79d8a7b9f3d2ff71e9362ef7cf8.png)
![652a120e254eb1ef8e5886e01fca6520.png](https://i-blog.csdnimg.cn/blog_migrate/eedaf9ccea0dc70a682db67bc64f51c4.jpeg)
![a1aa93af5374b2eb8a17a45b0ba8d066.png](https://i-blog.csdnimg.cn/blog_migrate/5801819f238c5d8ff63d3fa4cc6feae4.jpeg)
在实际操作中遇到两个问题:
1.在Widget编辑器中嵌套使用UserWidget不显示的问题
发现是WidgetTree->RootWidget为空,需要在模块启动时对自定义蓝图资源注册器。
FKismetCompilerContext::RegisterCompilerForBP(UMyWidgetBlueprint::StaticClass(), &UWidgetBlueprint::GetCompilerForWidgetBP);
2.在编辑器中以Mobile Preview模式启动崩溃问题
崩溃日志:
LogStreaming: Error: Couldn't find file for package /Script/FrameworkEditor requested by async loading code. NameToLoad:/Script/FrameworkEditor
LogLinker: Warning: Unable to load InforItemUI with outer Package /Game/UserInterface/Widget/GMUI/GMLogin/InforItemUI because its class does not MakeSureDirExists
Fatal error: [File:D:Build++UE4SyncEngineSourceRuntimeCoreUObjectPrivateTemplatesCasts.cpp] [Line: 10]
Error: Cast of LinkerPlaceholderExportObject /Game/UserInterface/Widget/GMUI/GMLogin/InforItemUI.PLACEHOLDER-INST_of_PLACEHOLDER-CLASS__MyWidgetBlueprint_1 to Blueprint failed.
原因是由于MyWidgetBlueprint定义在FrameworkEditor模块中,而界面InforItemUI的蓝图资源为自定义的MyWidgetBlueprint。当以Mobile Preview模式启动时FrameworkEditor模块未加载导致未能正确找到MyWidgetBlueprint,从而在BlueprintGeneratedClass::GetAuthoritativeClass()中将ClassGeneratedBy转化为UBlueprint失败从而导致崩溃。解决方法为在运行时模块启动时手动载FrameworkEditor模块。
void FFrameworkModule::StartupModule()
{
#if WITH_EDITOR
FModuleManager::Get().LoadModule(TEXT("FrameworkEditor"));
#endif
}
之前纳闷的地方在于我写的MyWidgetBlueprint是继承引擎的UWidgetBlueprint的,而UWidgetBlueprint是属于UMGEditor模块的。它之所以没事是因为FEngineLoop::LoadStartupCoreModules()中手动加载了UMG模块,而当UMG模块启动又加载了UMGEditor模块。
本文介绍的是蓝图类资源的创建流程,了解了流程就可以很方便地自定义非蓝图类的资源,这里就不再详细介绍了。