UE5: Content browser工具编写

news2024/9/28 15:19:31

Extend content browser

创建自定义菜单入口的步骤:create custom menu entry. steps:

Load content browser module ->

PathViewContextMenuExtenders ->

Add in our own delegate ->

Bind to our own member functions

基础概念(本文实时穿插蓝色词条概念解释,可跳过)


Delegateshttps://dev.epicgames.com/documentation/en-us/unreal-engine/delegates?application_version=4.27icon-default.png?t=O83Ahttps://dev.epicgames.com/documentation/en-us/unreal-engine/delegates?application_version=4.27why:

委托(Delegates) 是一种允许以类型安全的方式调用C++对象成员函数的机制。它们可以动态绑定到任意对象的成员函数,并在未来调用该函数,而不需要事先知道对象的具体类型。

假设你有一个类Car,类中定义了几个成员变量(例如汽车的颜色和速度),还有一个成员函数Drive。这个函数是属于Car类的,它可以操作Car对象的状态。

Drive()函数就是Car类的一个成员函数。每个Car类的对象(比如一辆汽车)都可以调用Drive()函数来启动驾驶。

what:

  • 单播(Single Delegate):只能绑定一个函数,通常用于一对一的函数调用场景。
  • 多播(Multicast Delegate)可以绑定多个函数,调用时会依次执行所有绑定的函数,常用于需要通知多个观察者的情况。
  • 动态委托(Dynamic Delegate):这些委托适用于UObject,支持序列化,能在蓝图中使用。

how:

声明委托

声明委托时,你需要使用虚幻提供的宏,这些宏会根据你要绑定的函数的返回类型和参数来生成相应的委托类型。以下是一些常见的声明委托的宏:

  • DECLARE_DELEGATE(DelegateName):声明一个无参数、无返回值的委托。
  • DECLARE_DELEGATE_OneParam(DelegateName, Param1Type):声明一个带一个参数的委托。
  • DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type):声明一个带两个参数的委托。

对于动态和多播委托,宏的格式略有不同,比如:

  • DECLARE_DYNAMIC_MULTICAST_DELEGATE... 用于动态多播委托,支持蓝图。
  • DECLARE_DYNAMIC_DELEGATE... 用于动态单播委托。

绑定委托

在虚幻引擎中,委托可以通过多种方式绑定到函数:

  • BindStatic():绑定到全局静态函数。
  • BindRaw():绑定到原始C++指针,但使用时要注意内存管理问题。
  • BindLambda():绑定到Lambda表达式,适合简洁的函数。
  • BindSP():绑定到共享指针(shared pointer)的成员函数。
  • BindUObject():绑定到UObject类型的成员函数,虚幻引擎会自动处理UObject的生命周期管理。

执行委托

执行委托时,通常会使用Execute()ExecuteIfBound()方法。Execute()会直接执行绑定的函数,如果委托未绑定则会触发断言,而ExecuteIfBound()会在执行前检查委托是否绑定了函数。

示例:当用户点击按钮时,委托可以动态绑定不同的处理函数,实现按钮点击的不同反应。

Button->OnClick.AddDynamic(this, &MyClass::OnButtonClicked);

Bulk Operation

Bulk Operation(批量操作)

  • 重点:主要关注一次处理大量数据的操作。
  • 应用:通常用于需要高效处理大量数据或资源的场景中,比如数据库操作、文件操作或内存操作。
  • 实时性:批量操作通常是实时执行的,意味着在需要时可以立即开始处理大量项。例如,数据库中的批量插入操作就是在一个事务中立即完成多条数据的插入。
  • 示例:一次性插入多条数据库记录、一次性复制大块内存数据、一次性移动多个文件等。

2. Batch Operation(批处理操作)

  • 重点:主要关注一组任务或作业的批处理,通常是延迟或计划执行的。
  • 应用:批处理操作通常是在后台或非高峰时段执行的,比如在银行系统或数据处理系统中,可能会在夜间批量处理积累的任务或数据。
  • 实时性:批处理操作往往是定时执行的,通常不是实时的。例如,批处理系统可以在一天结束后处理所有待处理的订单、生成报表等。
  • 示例:夜间处理一整天的银行交易、定时运行的报表生成、离线数据分析任务等。

auto

在 C++ 中,auto 关键字用来让编译器根据初始化表达式的类型自动推断变量的类型。也就是说,当你使用 auto 声明一个变量时,你不需要明确地写出它的类型,编译器会根据你赋给这个变量的值推断出类型。

auto ContentBrowserModuleMenuExtenders = ContentBrowserModule.GetAllPathViewContextMenuExtenders();

以reference方式存储变量

	TArray<FContentBrowserMenuExtender_SelectedPaths>& ContentBrowserModuleMenuExtenders = 
	ContentBrowserModule.GetAllPathViewContextMenuExtenders();

When we mark a variable as reference, technically this variable doesn't exist. This is just an alias and this is an alias of the array that we want. for our custom menu entry to work, it is very important that we are accessing this original array and not making a copy.

if without this & we would just be holding this array in another copy and by adding our delegate into that copy, we won't be able to see any changes.

当我们将一个变量标记为引用时,技术上来说,这个变量并不存在。它只是一个别名(alias),是我们想要访问的数组的别名。为了使我们的自定义菜单项正常工作,访问这个原始数组非常重要,而不是创建一个副本。

如果没有使用引用符号(&),我们实际上是在持有这个数组的一个副本,向这个副本中添加我们的委托时,我们将无法看到任何变化。

在这个上下文中,确保操作的是原始数组,而不是副本,是实现功能所必需的。这样才能正确反映出对数组的更改或操作。

#pragma region ContentBrowserMenuExtension
void FSuperManagerModule::InitContentBrowserExtension()
{
	FContentBrowserModule& ContentBrowserModule = 
	FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));

	TArray<FContentBrowserMenuExtender_SelectedPaths>& ContentBrowserModuleMenuExtenders = 
	ContentBrowserModule.GetAllPathViewContextMenuExtenders();

	FContentBrowserMenuExtender_SelectedPaths CustomContentBrowserMenuDelegate;

	ContentBrowserModuleMenuExtenders.Add(CustomContentBrowserMenuDelegate);
	//add our own delegate, but so far it is only an empty delegate, there's no binding

	//we need to bind it to our own member function, in there we can define the title for our menu entry, and the function
}

#pragma endregion

Binding Member Functions

  • First Binding: Define the position for inserting menu entry
  • Second Binding: Define the detals for the menu entry ( Title, Tooltip, Function...)

https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Slate/Framework/MultiBox/FBaseMenuBuilder/AddMenuEntryicon-default.png?t=O83Ahttps://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Slate/Framework/MultiBox/FBaseMenuBuilder/AddMenuEntry

  • Third Binding: the actual function to excute

TShareRef is a type of small pointer

(will go over small pointer more in depth when dealing with slave widgets)

"slave widgets" 通常指的是那些依赖于其他主控组件(通常称为 "master widget")的子组件或控件。它们的行为、状态或外观可能会受到主控组件的影响。

	FContentBrowserMenuExtender_SelectedPaths CustomContentBrowserMenuDelegate;
	CustomContentBrowserMenuDelegate.BindRaw(this, &FSuperManagerModule::CustomContentBrowserMenuExtender);

	ContentBrowserModuleMenuExtenders.Add(CustomContentBrowserMenuDelegate);
//more understandable way

BindRaw: 这个方法用于将一个成员函数绑定到委托上。BindRaw 表示绑定的是一个原始指针(raw pointer),即直接指向一个对象的指针,而不是使用智能指针。这样做时要注意,确保绑定的对象在委托执行时仍然存在。

更简洁的写法:(使用CreateRaw

	ContentBrowserModuleMenuExtenders.Add(FContentBrowserMenuExtender_SelectedPaths::
		CreateRaw(this, &FSuperManagerModule::CustomContentBrowserMenuExtender));
TSharedRef<FExtender> MenuExtender(new FExtender());//We use the new keyword to create an FExtender

be extremely careful when you use the new keyword in unreal, otherwise you'll have memory leak.

这句话的意思是:在 Unreal Engine 中使用 new 关键字时要非常小心,否则可能会导致内存泄漏(memory leak)。

内存泄漏的解释:

内存泄漏是指程序在运行过程中动态分配内存,但未能释放这些内存的情况。具体来说,这意味着:

  1. 分配内存:程序使用 new 或类似方法申请了一块内存空间,用于存储数据。

  2. 未释放内存:当程序结束使用这块内存时,应该调用 delete 或相应的方法来释放它。如果忘记了这一操作,分配的内存将无法被程序重新利用,导致系统可用内存逐渐减少。

  3. 后果:内存泄漏会导致程序占用越来越多的内存,最终可能导致系统性能下降、程序崩溃,甚至在长期运行的情况下可能耗尽系统内存。

在 Unreal Engine 中的注意事项:

  • 使用智能指针:为了避免内存泄漏,Unreal Engine 提供了智能指针(如 TSharedPtrTUniquePtr),它们会自动管理内存的分配和释放。使用这些智能指针可以显著降低内存泄漏的风险。

  • 对象管理:确保在使用 new 创建对象后,适当地管理其生命周期,及时调用 delete,以释放内存。

  • 检查指针:在使用指针之前,始终检查它们是否有效,确保不会访问已释放或未分配的内存。

使用“内存泄漏”(memory leak)这个术语是因为其隐含的特征与物理世界中的“泄漏”有相似之处。以下是为什么使用“泄漏”这个词的原因:

1. 隐喻的意义

  • 泄漏通常指的是液体或气体从容器中意外流出,无法被回收或利用。在编程中,内存泄漏意味着程序分配了一些内存,但由于没有适当地释放或管理,这些内存就无法再被程序使用或回收。

new关键字

new 关键字在 C++ 中用于动态分配内存。具体来说,它的主要作用是:

1. 动态内存分配

  • 当你使用 new 关键字时,它会在堆(heap)上分配一块内存,以存储你所需的对象或数据。
  • 这意味着你可以在程序运行时根据需要分配内存,而不必在编译时确定所需的内存大小。

2. 创建对象

  • new 不仅分配内存,还会调用构造函数来初始化对象。例如:
MyClass* myObject = new MyClass();
  • 在这个例子中,new MyClass() 分配了一块内存,用于存储 MyClass 类型的对象,并调用该对象的构造函数。
  • 3. 返回指针

  • new 关键字会返回一个指向分配内存块的指针。这使得你能够通过该指针访问和操作对象。例如:
int* myArray = new int[10]; // 动态分配一个包含10个整数的数组

4. 释放内存

  • 使用 new 动态分配的内存需要在不再使用时通过 delete 关键字手动释放

TSharedRef<FExtender> FSuperManagerModule::CustomContentBrowserMenuExtender(const TArray<FString>& SelectedPaths)
{
	TSharedRef<FExtender> MenuExtender(new FExtender());

//if check
	return MenuExtender;
}

CustomContentBrowserMenuExtender,whenever this function is called, a TArray of Fstring will be passed in, this will tell as how many folders are currently selected inside of our content browser

Display UI Extension Points


在编辑器偏好设置里启用Display UI Extension Points,并重启编辑器,会得到Extension Hooks

最终示例代码

SuperManager.h

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

#pragma once

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

class FSuperManagerModule : public IModuleInterface
{
public:

	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;

private:

#pragma region ContentBrowserMenuExtension
	void InitContentBrowserExtension();

	TSharedRef<FExtender>CustomContentBrowserMenuExtender(const TArray<FString>& SelectedPaths);
		
	void AddContentBrowserMenuEntry(class FMenuBuilder& MenuBuilder);

	void OnDeleteUnusedAssetButtonClicked();
#pragma endregion
};

SuperManager.cpp 

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

#include "SuperManager.h"
#include "ContentBrowserModule.h"

#define LOCTEXT_NAMESPACE "FSuperManagerModule"

void FSuperManagerModule::StartupModule()
{
	InitContentBrowserExtension();
}


void FSuperManagerModule::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.
}

#pragma region ContentBrowserMenuExtension
void FSuperManagerModule::InitContentBrowserExtension()
{
	FContentBrowserModule& ContentBrowserModule = 
	FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));

	TArray<FContentBrowserMenuExtender_SelectedPaths>& ContentBrowserModuleMenuExtenders = 
	ContentBrowserModule.GetAllPathViewContextMenuExtenders();

	//FContentBrowserMenuExtender_SelectedPaths CustomContentBrowserMenuDelegate;
	//CustomContentBrowserMenuDelegate.BindRaw(this, &FSuperManagerModule::CustomContentBrowserMenuExtender);
	
	//ContentBrowserModuleMenuExtenders.Add(CustomContentBrowserMenuDelegate);
	//***same with the two lines below

	ContentBrowserModuleMenuExtenders.Add(FContentBrowserMenuExtender_SelectedPaths::
		CreateRaw(this, &FSuperManagerModule::CustomContentBrowserMenuExtender));
	
}

TSharedRef<FExtender> FSuperManagerModule::CustomContentBrowserMenuExtender(const TArray<FString>& SelectedPaths)
{
	TSharedRef<FExtender> MenuExtender(new FExtender());

	if (SelectedPaths.Num() > 0) {
		//define the position
		//-> 是一个运算符,用于访问指针所指向对象的成员。

		MenuExtender->AddMenuExtension(FName("Delete"),//name of extension hook
			//we want to insert after DELETE(extension hook)
			EExtensionHook::After,
			TSharedPtr<FUICommandList>(),//no hotkey
			//first binding
            FMenuExtensionDelegate::CreateRaw(this, &FSuperManagerModule::AddContentBrowserMenuEntry));
		    //second binding
	};

	return MenuExtender;
}

void FSuperManagerModule::AddContentBrowserMenuEntry(FMenuBuilder& MenuBuilder)
{//in this function we can define all the details for our menu entry //and we can use the menu builder to do so
	MenuBuilder.AddMenuEntry
	(
		FText::FromString(TEXT("Delete Unused Assets")),
		FText::FromString(TEXT("Safely delete all unused assets under folder")),
		FSlateIcon(),//empty placeholder
		//SECOND BINDING
		FExecuteAction::CreateRaw(this, &FSuperManagerModule::OnDeleteUnusedAssetButtonClicked)
	);
}

void FSuperManagerModule::OnDeleteUnusedAssetButtonClicked()
{
	//not finished yet
}

#pragma endregion



#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FSuperManagerModule, SuperManager)

最终效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2170005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++【类和对象】(拷贝构造与运算符重载)

1. 拷贝构造 如果⼀个构造函数的第⼀个参数是自身类类型的引用&#xff0c;且任何额外的参数都有默认值&#xff0c;则此构造函数也叫做拷贝构造函数&#xff0c;也就是说拷贝构造是⼀个特殊的构造函数。 注意&#xff1a;拷贝构造是用一个已经实例化的对象来初始化一个新对象…

中级职称评审到底需要准备什么材料?

职称评审需要的材料非常非常多&#xff0c;其中涉及到各类表格&#xff0c;这些小资料&#xff0c;看起来简单&#xff0c;实则做起来复杂&#xff0c;不过这种资料只能当年通知出来之后进行整理&#xff0c;今天甘建二跟大家说一下职称评审中需要提前准备的一些重要材料&#…

5.10直方图均衡化

基本概念 直方图均衡化&#xff08;Histogram Equalization&#xff09;是一种常用的图像处理技术&#xff0c;用于改善图像的对比度。通过调整图像中像素值的分布&#xff0c;直方图均衡化可以使图像的动态范围更广&#xff0c;从而增强图像的细节可见度。 直方图均衡化原理…

c++ 杂项

简答题 1、什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数是在类中定义函数时&#xff0c;在函数前加 virtual 关键字。父子类中只有一个该函数。 如果子类中没有重写该虚函数。那么父子类空间中使用的都是父类定义的该函数。 如果子类中重写了该虚函数&#xff…

【Python基础(一)】

学习分享 一、基本语法1、输出print语句2、常量的写法3、运算符 (/) 与(//)4、字符串5、列表5.1、列表查询元素是否存在5.2、列表查询元素是否存在5.3、身份运算符5.4、列表的增删改查 6、元组6.1、tuple() 7、字典8、函数8.1、值传递8.2、引用传递8.3、函数的传参 二、文件的操…

小北的JDK1.8下载、安装和环境配置教程——附件资源

​前言 亲爱的友友们&#xff0c;欢迎来到小北博客&#xff01;今天&#xff0c;我们将一起探索如何下载、安装并配置JDK 1.8&#xff0c;这是Java开发中一个非常关键的步骤。无论你是Java新手还是资深开发者&#xff0c;掌握JDK的正确安装和配置都是必不可少的。Java Download…

828华为云征文 | 基于华为云Flexus云服务器X搭建部署——AI知识库问答系统(使用1panel面板安装)

&#x1f680;对于企业来讲为什么需要华为云Flexus X来搭建自己的知识库问答系统&#xff1f;&#xff1f;&#xff1f; 【重塑知识边界&#xff0c;华为云Flexus云服务器X引领开源问答新纪元&#xff01;】 &#x1f31f; 解锁知识新动力&#xff0c;华为云Flexus云服务器X携…

【工具分享】Jigsaw勒索病毒解密工具

前言 Jigsaw勒索软件首次出现在2016年&#xff0c;以其独特的威胁手段迅速在网络安全界引起广泛关注。该恶意软件因其在勒索信中使用了恐怖电影《电锯惊魂》中的角色Billy the Puppet的图像而得名。Jigsaw不仅会加密受害者的文件&#xff0c;还会逐渐删除这些文件以迫使受害者…

基于nodejs+vue的游戏陪玩系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

大数据毕业设计选题推荐-国潮男装微博评论数据分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

当大模型成为新一代操作系统,我们如何转型AI产品经理?

大模型无疑是最近科技圈最炙手可热的时尚单品&#xff0c;跟AIGC能沾上边的工作岗位都成为行业香饽饽。许多产品经理朋友与斯年讨论如何转型AI产品经理&#xff0c;今天想通过用户体验五要素的逻辑框架&#xff0c;谈谈传统型产品经理 VS. AI型产品经理的差异。最后分享几点在转…

教师工作量数字化管理平台

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

mysql数据库的基本管理

目录 一.数据库的介绍 二.mariadb的安装 三.软件基本信息 四.数据库开启 五.数据库的安全初始化 六.数据库的基本管理 七.数据密码管理 八.用户授权 九.数据库的备份 十.web控制器 一.数据库的介绍 1.什么是数据库 数据库就是个高级的表格软件 2.常见数据库 Mysql Oracl…

Type-C接口相关知识:【总结大全】

Type-c现在非常通用了&#xff0c;所以了解Type-c也变得十分有必要了&#xff0c;还是秉承了解就要了解清楚的原则&#xff0c;我们深入的看看Type-c接口。 Type-c主要是取代上一代Micro usb接口&#xff0c;那么Type-c有什么优点呢&#xff1f; 正反可插&#xff0c;使用时不…

Vite使用vite-plugin-compression打包资源压缩

https://github.com/vbenjs/vite-plugin-compression 安装所需依赖 yarn add vite-plugin-compression -D压缩前 压缩后 使用 vite.config.ts import viteCompression from vite-plugin-compressionexport default defineConfig({plugins: [vue(),viteCompression({verbose: …

【redis-03】redis缓存穿透、缓存击穿、缓存雪崩

redis系列整体栏目 内容链接地址【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756【三】redis缓存穿透、缓存击穿、缓存雪崩htt…

如何用IDEA连接HBase

编写java代码&#xff0c;远程连接HBase进行相关的操作 一、先导依赖 代码如下&#xff1a; 二、连接成功

scroll-view滚动条在ios上没有显示滚动条,安卓上显示,亲测有效果

问题描述 微信小程序的scroll-view在ios上没有显示滚动条&#xff0c;但是如果在安卓设备上会显示一个滚动条解决方案 微信小程序只需要在scroll-view上面添加show-scrollbar“{{false}}” enhanced"{{true}}"即可解决 UniApp则修改成:show-scrollbar“false” enh…

图像背景去除的最佳工具和 PNG 网站

PNG 网站是专业人士、设计师和任何需要具有透明背景的高质量图像的人的重要资源。这些网站提供数百万个 PNG&#xff0c;这对于数字项目很有价值。更不用说&#xff0c;这些格式具有保持质量和支持透明度的能力。在这篇文章中&#xff0c;我们将探讨提供无数满足不同需求的 PNG…