跳至主要內容

Slate1.编辑器工具插件TODO

Mr.Si大约 10 分钟unreal

闲聊

头像
Baba!我记不住这么多委托函数的写法怎么办?
头像
为什么不用蓝图直接开发呢!
头像
总要提升挑战一下自己吧!有没有办法呢?
头像
也许你可以写一个可视化界面辅助我们写这个宏函数。比如这种:
头像
对哦!可是我们该选择什么框架来做呢?
头像
方法有很多啊:
1.WPF/QT写一个独立程序
2.JS写成Web端
3.UE用蓝图编辑器控件
4.UE中用基于Slate的编辑器插件
头像
WPF/QT太重了!很多UE代码库没法直接用啊。JS呢虽然也不能直接链接UE,但随时随地可用!是个备选方案! 至于蓝图编辑器控件这个总觉得像在写UMG还是高级点的Slate来做吧!
头像
安排!

新建插件开始

头像
点击插件,Baba用的是UE5.2
头像
新建编辑器插件
头像
注意名称不要和现有插件重名。

等待IED编译后点击窗口-就能看到你创建的插件了

头像
此时回到编辑器就能看到我们的插件了

UI扩展点

头像
你的编辑器怎么有这些绿色的字啊?有什么用呢?
头像
这个呢叫UI扩展点,你可以在编辑器偏好设置中找到。见名知意就是方便咱确认UI插件启动按钮该放哪里用的。

插件基本结构

头像
下一步之前我们复习一下基本的插件目录结构
YourPluginName/
|-- Config/--配置文件
|   |-- DefaultGame.ini
|-- Content/--目录文件
|   |-- YourPluginName/
|       |-- Materials/
|       |-- Textures/
|-- Resources/--图标
|-- Source/--源文件
|   |-- YourPluginName/
|       |-- YourPluginName.Build.cs
|       |-- Private/
|           |-- YourPluginName.cpp
|       |-- Public/
|           |-- YourPluginName.h
|-- YourPluginName.uplugin

.Buid.cs

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

using UnrealBuildTool;

public class ExorcistTool : ModuleRules
{
	public ExorcistTool(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
		
		PublicIncludePaths.AddRange(
			new string[] {
				// ... add public include paths required here ...
			}
			);
				
		
		PrivateIncludePaths.AddRange(
			new string[] {
				// ... add other private include paths required here ...
			}
			);
			
		
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				// ... add other public dependencies that you statically link with here ...
			}
			);
			
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"Projects",
				"InputCore",
				"EditorFramework",
				"UnrealEd",
				"ToolMenus",
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				// ... add private dependencies that you statically link with here ...	
			}
			);
		
		
		DynamicallyLoadedModuleNames.AddRange(
			new string[]
			{
				// ... add any modules that your module loads dynamically here ...
			}
			);
	}
}

头像
没啥可说的,就是一些必备基础模块。

.uplugin解析

头像
基操先看.uplugin
ExorcistTool.uplugin
{
	"FileVersion": 3,
	"Version": 1,
	"VersionName": "1.0",
	"FriendlyName": "ExorcistTool",
	"Description": "a tool for code help",
	"Category": "Other",
	"CreatedBy": "sigaohe",
	"CreatedByURL": "",
	"DocsURL": "",
	"MarketplaceURL": "",
	"SupportURL": "",
	"CanContainContent": false,
	"IsBetaVersion": false,
	"IsExperimentalVersion": false,
	"Installed": false,
	"Modules": [
		{
			"Name": "ExorcistTool",
			"Type": "Editor",
			"LoadingPhase": "Default"
		}
	]
}
头像
除了Modules所有都是单一键值对,重点关注一下这个Modules。
  1. Modules是个数组,也就是说插件中可以有多个模块。
  2. Name:没啥好说的模块名
  3. Type:模块类型,翻看源码open in new window可以确定是个枚举值。
EHostType::Type
namespace EHostType
{
    enum Type
    {
        Runtime,
        RuntimeNoCommandlet,
        RuntimeAndProgram,
        CookedOnly,
        UncookedOnly,
        Developer,
        DeveloperTool,
        Editor,
        EditorNoCommandlet,
        EditorAndProgram,
        Program,
        ServerOnly,
        ClientOnly,
        ClientOnlyNoCommandlet,
        Max,
    }
}

4.LoadingPhase:模块加载阶段 依然是个枚举源码APIopen in new window

ELoadingPhase::Type
namespace ELoadingPhase
{
    enum Type
    {
        EarliestPossible,
        PostConfigInit,
        PostSplashScreen,
        PreEarlyLoadingScreen,
        PreLoadingScreen,
        PreDefault,
        Default,
        PostDefault,
        PostEngineInit,
        None,
        Max,
    }
}

插件初探

头像
现在来看看这个全新的插件目录结构
头像
系统为我们生成了三个头/源文件
ExorcistTool.h
#pragma once

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

class FToolBarBuilder;
class FMenuBuilder;

class FExorcistToolModule : public IModuleInterface
{
public:

    /** IModuleInterface 实现 */
    virtual void StartupModule() override;               // 模块启动时的回调
    virtual void ShutdownModule() override;              // 模块关闭时的回调
    
    /** 该函数将绑定到 Command(默认情况下将打开插件窗口) */
    void PluginButtonClicked();
    
private:

    void RegisterMenus();                                // 注册菜单回调

    TSharedRef<class SDockTab> OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs);  // 生成插件标签页回调

private:
    TSharedPtr<class FUICommandList> PluginCommands;     // 插件命令列表
};
  1. ExorcistTool

    • 作为插件的主要模块接口,实现了模块的启动和关闭功能。
    • 在这个文件中,通常定义了模块的生命周期函数以及模块的重要成员,比如用于创建菜单、工具栏等。
  2. ExorcistToolCommands

  • 定义了插件的命令集合,这些命令可以被UE编辑器或者蓝图调用。
  • 这里的 OpenPluginWindow 是一个打开插件窗口的命令。
  1. ExorcistToolStyle
  • 是插件的样式管理类,用于管理插件的Slate样式。Slate是UE的UI框架,
  • 该文件定义了样式的初始化、关闭以及重新加载等功能。

ExorcistTool解析

头像
为什么没有构造函数?
class FExorcistToolModule : public IModuleInterface
头像
在C++中,类不是必须要有构造函数的,尤其是对于实现接口的类。接口是一种抽象规范,而不是具体的实例。这里`IModuleInterface` 是一个标记接口,用于标识该类为模块接口, 而不需要实例化。因此,它并不需要一个显式的构造函数。

/** 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();
头像
很显然,按钮触发时的回调函数。

void RegisterMenus();
头像
注册菜单回调,具体点就是告诉编辑器这个按钮要显示在哪个位置,可以用之前的 UI扩展点定位参考。
TSharedRef<class SDockTab> OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs);

OnSpawnPluginTab是一个函数,返回类型是 TSharedRef<class SDockTab>,接受一个 const FSpawnTabArgs& 类型的参数 SpawnTabArgs

头像
具体地说,这个函数声明是用于生成插件的SDockTab对象,用于创建插件模块的Dockable Tab。
头像
你在说啥?我怎么开始迷糊了!
头像
别急,既然接触新的知识肯定要先接受一些新的术语,就好像之前的TSharedRef、TSharedPtr等一样。
头像
问题是你说的这个TSharedRef我就没理解啊!
头像
那就先来理解理解这个TSharedRef,咱的模板-智能指针中有具体的介绍,这里只做初步理解。

官网open in new window

TSharedRef回顾

TSharedRef 是 UE中的引用计数智能指针(Reference Counted Smart Pointer)的模板类。 这个类提供了自动内存管理,确保在不再需要对象时正确地释放内存。

  1. 引用计数: TSharedRef 使用引用计数机制来追踪对象的引用次数。当创建 TSharedRef 时,引用计数初始化为 1。每当有新的 TSharedRef 对象指向同一块内存时,引用计数会递增。当引用计数降为零时,内存将被释放。

  2. 强引用: TSharedRef 是强引用,这意味着它要求至少有一个 TSharedRef 对象在引用该内存块,否则内存将被释放。这确保了在对象的生命周期中至少有一个强引用。

  3. 构造函数: 可以使用 MakeSharedMakeShareable 函数来创建 TSharedRef。这些函数确保引用计数的正确管理。

  4. 不能为空: TSharedRef 不能为空。必须在构造时为其提供有效的对象。

  5. 线程安全: TSharedRef 对象是线程安全的。多个线程可以安全地访问引用计数,并且引用计数的操作是原子的。

头像
完了!你在说啥啊?
头像
推荐你康康Baba写的C++inside指针你就明白了!这里咱们先继续介绍一下SDockTab和Dockable
  1. SDockTab:

    • SDockTab 是 Slate UI 框架中的一个类,用于创建 Dockable Tab。
    • SDockTab 允许你定义一个可以通过拖拽重新排列、关闭、最小化等操作的标签页。
    • 你可以通过 SNew(SDockTab) 来创建 SDockTab 的实例,并通过链式调用设置标签页的属性。
    • 通常用于创建插件或编辑器中的标签页,以组织和展示特定的UI元素。
  2. Dockable Tab:

    • "Dockable Tab" 是 UE编辑器中一个可以被停靠(拖拽到主编辑器窗口的边缘)的标签页。
    • 这些标签页可以包含插件的自定义UI、视图或工具。
    • "Dockable Tab" 允许用户自定义编辑器布局,将不同的标签页放置在编辑器窗口的不同区域,以满足其工作流需求。
    • "Dockable Tab" 在 UE 编辑器中提供了一种有效的组织和访问工具和信息的方式。

参考文档

UE扩展开发,插件的基础结构解析open in new window