UE5: Content browser工具编写02

news2024/9/27 23:21:49

DebugHeader.h 中的全局变量,已经在一个cpp file中被include了,如果在另一个cpp file中再include它,就会有一些conflicts。先全部给加一个static

  • Add static keyword to debug functions
  • Wrap all the functions inside of a namespace
  • print message to the screen when pressing delete unused assets
#pragma once
#include "Misc/MessageDialog.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Framework/Notifications/NotificationManager.h"

static void Print(const FString& Message, const FColor& Color)
{
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 8.f, Color, Message);
	}
}

static void PrintLog(const FString& Message)
{
	UE_LOG(LogTemp, Warning, TEXT("%s"),*Message);
}

static EAppReturnType::Type ShowMsgDialog(EAppMsgType::Type MsgType, const FString& Message,
bool bShowMsgAsWarning = true) 
{
	if (bShowMsgAsWarning == true) 
	{
		FText MsgTitle = FText::FromString(TEXT("Warning"));

		return FMessageDialog::Open(MsgType, FText::FromString(Message), &MsgTitle);
	//return FMessageDialog::Open(MsgType, FText::FromString(TEXT("Warning: ") + Message));
	}

	else
	{
		return FMessageDialog::Open(MsgType, FText::FromString(Message));
	}
}

static void ShowNotifyInfo(const FString& Message)
{
	FNotificationInfo NotifyInfo(FText::FromString(Message));
	NotifyInfo.bUseLargeFont = true;
	NotifyInfo.FadeOutDuration = 7.f;

	FSlateNotificationManager::Get().AddNotification(NotifyInfo);
}

EditorAssetLibrary.h里面,ListAssets的功能

UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Asset")
static TArray<FString> ListAssets(const FString& DirectoryPath, bool bRecursive = true, bool bIncludeFolder = false);

ListAssets 函数中,bRecursive 是一个布尔变量,通常用来控制函数是否以递归方式列出某个目录下的所有资产(Assets)。(if we go inside of the subfolder)

  • bRecursivetrue 时,函数会递归遍历指定目录以及其所有子目录,并列出其中的所有资产。
  • bRecursivefalse 时,函数只会列出指定目录中的资产,而不会深入子目录。

bool bIncludeFolder

假设你的资产结构目录如下

/Assets
├── File1.uasset
├── Folder1
│   └── File2.uasset
└── Folder2
  • bIncludeFolder = true:
    输出可能是:File1.uasset, Folder1/, Folder1/File2.uasset, Folder2/

  • bIncludeFolder = false:
    输出可能是:File1.uasset, Folder1/File2.uasset

在content folder里面,不要碰collections和developers目录。DO NOT TOUCH ROOT FOLDER (COLLECTIONS/DEVELOPERS)

示例代码

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

#include "SuperManager.h"
#include "ContentBrowserModule.h"
#include "DebugHeader.h"
#include "EditorAssetLibrary.h"
#include "ObjectTools.h"//DeleteAssets()

#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

		FolderPathsSelected = SelectedPaths;//then we have access to the folders that the user has currently selected
	};

	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()
{
	if (FolderPathsSelected.Num() > 1)//delete stuff in multiple folders will cause some unexpected behaviors
	{
		DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("Please select ONE folder"));
		return;
	}

	//DebugHeader::Print(TEXT("Currently selected folder: ")+ FolderPathsSelected[0], FColor::Green);
	//pure debug, can be deleted later

	TArray<FString> AssetsPathName = UEditorAssetLibrary::ListAssets(FolderPathsSelected[0]);

	if (AssetsPathName.Num() == 0)
	{
		DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("No Asset found under selected folder"));
		return;
	}

	//not finished yet
	//DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("Delete Unused Assets"));
	//DebugHeader::Print(TEXT("Unused Assets Deleted"), FColor::Red);

	FString Message = FString::Printf(TEXT("A total of %d found.\nConfirm deletion?"), AssetsPathName.Num());
	//%d 是一个格式说明符,用于表示一个整数(int 类型)
	EAppReturnType::Type ConfirmResult =
	DebugHeader::ShowMsgDialog(EAppMsgType::OkCancel, Message);

	//error   error   //EAppReturnType::Type ConfirmResult = DebugHeader::ShowMsgDialog(EAppMsgType::OkCancel, TEXT("A total of ") + FString::Printf(AssetsPathName.Num()) + TEXT(" found.\nConfirm deletion?"));

	if (ConfirmResult == EAppReturnType::Cancel)return;

	TArray<FAssetData>UnusedAssetsDataArray;

	for (const FString& AssetPathNameWWWWW : AssetsPathName)
	{
		if (AssetPathNameWWWWW.Contains(TEXT("Collections")) || AssetPathNameWWWWW.Contains(TEXT("Developers")))
		{
			continue;//不执行操作,继续循环
		}
		//DO NOT TOUCH ROOT FOLDER (COLLECTIONS/DEVELOPERS)

		if (!UEditorAssetLibrary::DoesAssetExist(AssetPathNameWWWWW)) continue;
		//if we don't do this, sometimes we will get some error message saying that Assets does not exist

		TArray<FString> AssetReferencers = 
		UEditorAssetLibrary::FindPackageReferencersForAsset(AssetPathNameWWWWW);
		//it is same with(delete asset from selection)

		if (AssetReferencers.Num() == 0)//this asset is unused
			//error   error   E0137	expression must be a modifiable lvalue
			//报错的中文意思是:“表达式必须是可修改的左值。”
			//这是因为在这段代码里,你错误地使用了单等号 = ,而不是双等号 == ,在比较语句中使用了赋值运算符。
		{
			const FAssetData UnusedAssetData = UEditorAssetLibrary::FindAssetData(AssetPathNameWWWWW);
			UnusedAssetsDataArray.Add(UnusedAssetData);
		}
		//塞入“无用资产”动态数组

		if (UnusedAssetsDataArray.Num() > 0)
		{
			ObjectTools::DeleteAssets(UnusedAssetsDataArray);
		}

		else
		{
			DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("No Unused Asset found under selected folder"));
		}
	}

}

#pragma endregion



#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FSuperManagerModule, SuperManager)

可以将 C++ 中的 delegate 比作一个 “邮递员”

比喻解释:

  1. 邮递员:邮递员负责将信件从一个地方送到另一个地方。你把信件(消息、请求等)交给邮递员,告诉他该去哪个地址送达。

  2. 委托的角色

    • 发送者:在你的代码中,发送者(或发布者)就像是给邮递员信件的人。发送者生成一个事件(例如某个操作完成)并把它交给邮递员(委托)。
    • 邮递员(委托):邮递员不处理信件的内容,只负责把信件送到指定的地址。在委托的情况下,它会将事件传递给事先注册好的接收者(或订阅者)。
    • 接收者:接收者就像是收件人,当邮递员把信件送到他们那里时,他们会根据信件的内容采取相应的行动。

具体流程:

  • 注册接收者:想象一下,收件人提前告诉邮递员自己的地址(注册委托)。这样,当邮递员接收到信件时,他就知道要把信送到哪个地方。
  • 发送事件:当某个事件发生(例如按钮被点击),发送者会把这个事件的信息交给邮递员。
  • 处理事件:邮递员把信息传递给注册的接收者,接收者根据收到的信息执行相应的操作(例如,更新界面、执行某个功能等)。

优点:

  • 解耦合:使用邮递员的方式可以避免发送者和接收者之间的直接联系。发送者不需要知道接收者是谁,只需把事件交给邮递员。这样可以让代码更灵活,方便维护和扩展。
  • 多个接收者:就像一个邮递员可以把同一封信件送到多个收件人一样,委托可以有多个绑定的接收者,所有的接收者都会在事件发生时被通知。

总结:

将 C++ 中的 delegate 比作“邮递员”可以帮助你理解它的工作原理和用途:通过一个中介,将事件或消息从发送者传递给接收者,同时保持二者之间的解耦。这种方式在事件驱动编程和回调机制中非常有效。

使用 C++ 中的 delegate 可以实现一些直接调用函数所无法实现的功能:

1. 解耦合

  • 使用直接调用:如果直接调用一个函数,调用者需要了解被调用函数的具体实现和参数。这使得代码之间的耦合度增加。
  • 使用委托:通过委托,发送者和接收者之间没有直接关系。发送者只需要发布事件,接收者可以在不影响发送者的情况下独立处理事件。这种解耦合使得代码更加灵活,方便修改和扩展。

2. 动态绑定

  • 使用直接调用:在编译时,调用的函数是固定的,无法在运行时改变。
  • 使用委托:委托允许在运行时动态绑定多个回调函数。你可以根据不同的条件选择不同的回调,这种动态性在事件处理和响应用户输入时非常有用。

3. 多重订阅

  • 使用直接调用:直接调用通常只能调用一个函数。
  • 使用委托:一个委托可以绑定多个接收者,所有订阅这个委托的函数都会被调用。这种特性非常适合于需要通知多个对象的情况,如用户界面事件(按钮点击、状态变化等)。

4. 异步处理

  • 使用直接调用:直接调用函数通常是同步的,即调用者会等待函数执行完成后才能继续执行下一步。
  • 使用委托:委托可以与异步编程结合使用,允许事件在后台线程中处理,调用者可以继续执行其他任务,而不必等待事件处理完成。

5. 简化事件处理

  • 使用直接调用:如果要处理多种类型的事件,代码中会有大量的条件语句,增加了复杂性。
  • 使用委托:通过委托,可以创建更为简洁和结构化的事件处理代码,注册和注销事件处理器变得简单直观。

6. 回调机制

  • 使用直接调用:没有灵活的回调机制,调用者在调用函数时必须事先知道它将要做什么。
  • 使用委托:允许在某些操作完成时进行回调,例如在异步操作完成后执行特定的代码,或在游戏状态改变时更新界面。

例子:

假设你有一个游戏,玩家按下按钮时要播放音效并更新得分。使用直接调用时,按钮的点击处理代码需要直接调用音效播放函数和得分更新函数,这会使得代码紧密耦合。使用委托,你可以在按钮被点击时发布一个事件,多个处理函数(音效播放、得分更新)可以同时订阅这个事件并处理。

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

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

相关文章

Linux入门攻坚——34、nsswitch、pam、rsyslog和loganalyzer前端展示工具

nsswitch&#xff1a;network service switch 名称解析&#xff1a;name <---> id 认证服务&#xff1a;用户名、密码验证或token验证等 名称解析和认证服务都涉及查找位置&#xff0c;即保存在哪里。如linux认证&#xff0c;passwd、shadow&#xff0c;是在文件中&…

Linux标准IO(五)-I/O缓冲详解

1.简介 出于速度和效率的考虑&#xff0c;系统 I/O 调用&#xff08;即文件 I/O&#xff0c;open、read、write 等&#xff09;和标准 C 语言库 I/O 函数&#xff08;即标准 I/O 函数&#xff09;在操作磁盘文件时会对数据进行缓冲&#xff0c;本小节将讨论文件 I/O 和标准 I/…

20 vue3之自定义hooks

Vue3 自定义Hook的作用 主要用来处理复用代码逻辑的一些封装 Vue3 的 hook函数 相当于 vue2 的 mixin, 不同在与 hooks 是函数Vue3 的 hook函数 可以帮助我们提高代码的复用性, 让我们能在不同的组件中都利用 hooks 函数 这个在vue2 就已经有一个东西是Mixins mixins就是将…

8,STM32CubeMX配置SPI工程(读取norflash的ID)

1&#xff0c;前言 单片机型号&#xff1a;STM32F407 编程环境 &#xff1a;STM32CubeMX Keil v5 硬件连接 &#xff1a;SPI1&#xff0c;CS/SS--->PB14 注&#xff1a;本工程在1&#xff0c;STM32CubeMX工程基础&#xff08;配置Debug、时钟树&#xff09;基础上完…

ARM点灯---看手册

知识点&#xff1a; 一个程序可能会遇到内存泄漏问题&#xff0c;可能一次运行泄漏几M大小&#xff0c;执行几个小时才会泄漏到站崩溃&#xff0c;所以要查看是否有内存泄漏。 查看手册教程 0927-上午 视频1&#xff1a;25&#xff1b;00 硬件程序开发流程 最小系统:单片…

AI驱动的智能运维:行业案例与挑战解析

华为、蚂蚁、字节跳动如何引领智能运维&#xff1f; ©作者|潇潇 来源|神州问学 引言 OpenAI 发布的 ChatGPT 就像是打开了潘多拉的魔盒&#xff0c;释放出了生产环境中的大语言模型&#xff08;LLMs&#xff09;。一些新的概念&#xff1a;“大语言模型运维 (LLMOps)”…

边裁员边收购,思科逐渐变身软件并购之王

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

计算机毕业设计党建学习网站查看发布党建评论留言搜索部署安装/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

目录 ‌开发背景‌&#xff1a; ‌开发意义‌&#xff1a; ‌开发目标‌&#xff1a; 部署安装 主要功能 功能图 界面介绍 技术介绍 需求分析 1. 用户角色分析 2. 功能需求分析 3. 性能需求分析 4. 界面设计需求 5. 其他需求 ‌党建学习网站的开发背景、意义与目…

史上最详细论文word排版格式指导保姆级教学!

一、前言 首先&#xff0c;每个学校的论文排版格式都是不太相同的&#xff0c;但大体上都是相似的。 正常来说&#xff0c;论文的排版操作是十分枯燥并且重复的&#xff0c;但是word中的样式工具使得论文排版会变得容易。 接下来我将以某个学校论文格式要求为例&#xff0c;…

新手做TikTok用什么手机?当然是海外云手机!

在当今数字化时代&#xff0c;TikTok已成为全球领先的短视频平台&#xff0c;吸引了大量个人和企业入驻&#xff0c;希望在这一广阔的市场中脱颖而出。然而&#xff0c;对于刚刚进入TikTok的新手来说&#xff0c;选择合适的工具是开启TikTok之路的第一步。近年来&#xff0c;海…

开关电源频率是什么?正常范围是多少?

在电子设备的小型化和高效化趋势下&#xff0c;开关电源的频率选择显得尤为重要。本文将详细阐述开关电源频率的范围及其测试方法。  一、开关电源频率的范围 开关电源是一种高效的电力转换装置&#xff0c;为电子设备提供稳定的电源。而作为评估开关电源性能的指标之一&#…

统信服务器操作系统【开机自启动】配置方法

开机自启动的四种配置方法,包括systemctl命令、rc.local文件、crontab任务,通过desktop配置开机自动,前三种方法适合后台程序或者脚本启动,最后一种方法适合图形化程序启动。 文章目录 准备环境配置方法一、通过编写service的方法,使用systemctl配置开机自启二、通过rc.lo…

[EBPF] 实时捕获DM数据库是否存在SQL阻塞

1. 介绍 eBPF&#xff08;extened Berkeley Packet Filter&#xff09;是一种内核技术&#xff0c;它允许开发人员在不修改内核代码的情况下运行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter&#xff08;BPF&#xff09;&#xff0c;后者是由贝尔实验室开发的一种网…

安卓开发板_MTK开发板_联发科开发评估套件Demo板接口介绍

开发板是一种功能丰富的电路平台&#xff0c;专为开发人员设计&#xff0c;集成了多种传感器、扩展接口和通信模块。这使得开发者能够高效进行原型设计和功能验证&#xff0c;极大地简化了软硬件开发的过程。 此次介绍的安卓开发板由MT8788核心板与底板构成&#xff0c;特别之处…

mac怎么设置ip地址映射

最近开发的项目分为了两种版本&#xff0c;一个自己用的&#xff0c;一个是卖出去的。 卖出的域名是和自己的不一样的&#xff0c;系统中有一些功能是只有卖出去的版本有的&#xff0c;但我们开发完之后还得测试&#xff0c;那就需要给自己的电脑配置一个IP地址映射了&#xf…

力扣面试150 寻找峰值 二分

Problem: 162. 寻找峰值 &#x1f468;‍&#x1f3eb; 参考图解 class Solution {public int findPeakElement(int[] nums) {int l 0; // 初始化左边界&#xff0c;表示数组的起始位置int r nums.length - 1; // 初始化右边界&#xff0c;表示数组的结束位置// 当左边界和…

“AI+Security”系列第3期(六):打造最懂安全的智能体-无极AI安全智能体平台落地与实践

近日&#xff0c;由安全极客、Wisemodel 社区、InForSec 网络安全研究国际学术论坛和海升集团联合主办的 “AI Security” 系列第 3 期技术沙龙&#xff1a;“AI 安全智能体&#xff0c;重塑安全团队工作范式” 活动顺利举行。此次活动吸引了线上线下超过千名观众参与。 活动…

基于微信开发助手企鹅音乐微信小程序的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

pyside6与协程

目录 一、常见错误 错误一、使用协程函数作为槽函数。 错误二、在Qt循环中创建新的loop 二、解决方法&#xff1a; ①安装库qasync ②修改Qt入口 ③异步槽函数 ④异步函数 ⑤整体示例 一、常见错误 错误一、使用协程函数作为槽函数。 这样是肯定是不行&#xff…

BufferQueue低延迟优化,以及SurfaceView帧率上限问题解决

目录 了解BufferQueue 为什么会出现问题&#xff1f; 如何优化&#xff1f; 最近在做一个与音视频播放相关的项目&#xff0c;使用到了MediaCodec解码后送到SurfaceView播放场景。发现SurfaceView播放上限是60HZ&#xff0c;不符合项目需求&#xff0c;故而进行了研究并找到…