UE5: Content browser工具编写02

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 belowContentBrowserModuleMenuExtenders.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 bindingFMenuExtensionDelegate::CreateRaw(this, &FSuperManagerModule::AddContentBrowserMenuEntry));//second bindingFolderPathsSelected = 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 soMenuBuilder.AddMenuEntry(FText::FromString(TEXT("Delete Unused Assets")),FText::FromString(TEXT("Safely delete all unused assets under folder")),FSlateIcon(),//empty placeholder//SECOND BINDINGFExecuteAction::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 laterTArray<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 existTArray<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_NAMESPACEIMPLEMENT_MODULE(FSuperManagerModule, SuperManager)

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

比喻解释:

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

  2. 委托的角色

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

具体流程:

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

优点:

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

总结:

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

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

1. 解耦合

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

2. 动态绑定

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

3. 多重订阅

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

4. 异步处理

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

5. 简化事件处理

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

6. 回调机制

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

例子:

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

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

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

相关文章

有关Java Stream流中存在null值问题

在 Java(8)开发 中&#xff0c;Stream API 提供了一种处理集合的强大方式&#xff0c;但在处理包含空值的流时&#xff0c;可能会遇到一些意想不到的问题。本文将通过一个简单的 User 实体类来演示如何处理空值&#xff0c;并讨论在实际开发中可能遇到的空指针异常&#xff08;…

锐尔15注册机 锐尔文档扫描影像处理系统15功能介绍

锐尔文档扫描影像处理系统是一款全中文操作界面的文件、档案扫描及影像优化处理软件&#xff0c;是目前国内档案数字化行业里专业且优秀的影像优化处理软件。 无论是从纸质文件制作高质量的影像文件&#xff0c;或是检查已经制作好的影像文件&#xff0c;锐尔文档扫描影像处理…

如何设置路由器最大设备数量以及网速限制

设置路由器的最大设备数量和网速限制可以帮助你更好地管理家庭或办公网络&#xff0c;确保每个设备都能稳定地连接并合理分配带宽。以下是如何设置这两项功能的详细步骤&#xff1a; 一、设置路由器最大设备数量 大多数现代路由器都允许你设置最多能连接的设备数量&#xff0…

Unity DOTS系列之IJobChunk来迭代处理数据

最近DOTS发布了正式的版本, 我们来分享一下System中如何在System中使用IJobChunk来迭代处理World中的数据&#xff0c;方便大家上手学习掌握Unity DOTS开发。 再回顾一次基于ArcheType Chunk内存管理 我们先再次回顾以下基于ArcheType的Chunk内存管理。每一类Entity都是由一些…

【大语言模型_1】VLLM部署Qwen模型

1、模型下载&#xff1a; 魔塔社区&#xff1a;魔搭社区 huggingface&#xff1a;https://huggingface.co/Qwen 2、安装python环境 1、python官网安装python 【推荐要3.8以上版本】 2、安装vllm模块 3、启动模型 CUDA_VISIBLE_DEVICES0,1 /root/vendor/Python3.10.12/bin/pytho…

深度学习自编码器 - 预测稀疏分解(PSD)篇

序言 在数据科学与机器学习的广阔领域中&#xff0c;深度学习作为一股不可忽视的力量&#xff0c;正引领着技术革新的浪潮。其中&#xff0c;自编码器&#xff08; Autoencoder \text{Autoencoder} Autoencoder&#xff09;作为一种特殊的神经网络结构&#xff0c;以其独特的自…

大数据新视界 --大数据大厂之 Druid 实时数据分析平台在大数据中的应用

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

深入浅出:MySQL索引优化指南

一、引言 在数据库应用开发中&#xff0c;性能优化是一个永恒的话题。而索引作为提高数据库查询效率的关键手段&#xff0c;其设计和使用直接影响着系统的性能表现。本文将深入探讨MySQL索引的原理、类型以及优化策略&#xff0c;帮助读者更好地理解和应用索引&#xff0c;从而…

ListNode

ListNode 通常是在数据结构和算法中用到的一个类&#xff0c;它表示链表&#xff08;Linked List&#xff09;中的一个节点&#xff08;Node&#xff09;。在链表中&#xff0c;每个节点包含两个主要的部分&#xff1a;节点的数据&#xff08;值&#xff09;以及指向下一个节点…

充气模块方案——无刷充气泵pcba方案

在方案开发中&#xff0c;充气效率是无刷充气泵PCBA方案开发中的关键问题。一般通过优化电路设计和控制算法&#xff0c;可以实现高效的气体压缩和快速的充气效果。另外&#xff0c;选择合理的电机驱动器和传感器等元器件能够提高打气泵的功率和效率&#xff0c;减少充气时间&a…

在Windows系统上安装的 Arrow C++ 库

在Windows系统上安装的 Arrow C 库 正文第一步第二步第三步第四步注: 检查是否安装成功 吐槽 正文 第一步 git clone gitgithub.com:apache/arrow.git第二步 打开powershell (好像cmd也可以,不过我试了powershell中不报错,cmd中报错,不是很清楚为什么) 打开arrow的目录 cd …

贝锐洋葱头浏览器随时随地访问教务系统,轻松搞定选课

教育网的“拥堵”早已是老生常谈&#xff0c;学生数量庞大、上网时间集中、带宽有限&#xff0c;导致网络速度慢。尤其是从外部网络访问教育网时&#xff0c;更是因为跨运营商的缘故变得缓慢。 而学校内网也是类似的情况&#xff0c;课余时间和上课时间的网络使用情况差别巨大…

xpath在爬虫中的应用、xpath插件的安装及使用

安装 1、打开谷歌浏览器进入扩展程序安装页面(右上角会有"开发者模式按钮")默认是关闭的&#xff0c;当安装此插件时需要把开发者模式打开。 2、下载下来的xpath_helper是zip格式的&#xff0c;需要解压缩即可安装。 3、重启浏览器&#xff0c;再次点击扩展程序即…

C++八股进阶

之前那个只是总结了一下常考点&#xff0c;这个是纯手打记笔记加深理解 这里写目录标题 C的四种智能指针为什么要使用智能指针&#xff1f;四种智能指针&#xff1a; C中的内存分配情况C中的指针参数传递和引用参数传递C 中 const 和 static 关键字&#xff08;定义&#xff0…

NLP 序列标注任务核心梳理

句向量标注 用 bert 生成句向量用 lstm 或 bert 承接 bert 的输出&#xff0c;保证模型可以学习到内容的连续性。此时 lstm 输入形状为&#xff1a; pooled_output.unsqueeze(0) (1, num_sentence, vector_size) 应用场景 词性标注句法分析 文本加标点 相当于粗粒度的分词任…

watchEffect工作原理

watchEffect工作原理 自动依赖收集&#xff1a;watchEffect不需要明确指定要观察的响应式数据&#xff0c;它会自动收集回调函数中用到的所有响应式数据作为依赖。即时执行&#xff1a;watchEffect的回调函数会在组件的setup()函数执行时立即执行一次&#xff0c;以便能够立即…

(done) 声音信号处理基础知识(4) (Understanding Audio Signals for ML)

来源&#xff1a;https://www.youtube.com/watch?vdaB9naGBVv4 模拟信号特点如下 时域连续(x轴) 振幅连续(y轴) 如下是模拟信号的一个例子&#xff1a; 数字信号特点如下&#xff1a; 一个离散值序列 数据点的值域是一系列有限的值 ADC&#xff1a;模拟信号到数字信号的…

[leetcode]77_组合_给定数字范围且输出无重复

给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ] 示例 2&#xff1a; 输入&#xff1a;n 1, k 1 输出…

中间添加一条可以拖拽的分界线,来动态调整两个模块的宽度

在 React 中操作 DOM 元素时&#xff0c;使用 document.querySelector 以及全局事件监听&#xff08;如 addEventListener&#xff09;并不推荐&#xff0c;因为这些方法无法与 React 的生命周期很好地协调&#xff0c;可能会导致内存泄漏或影响性能。 可以改为使用 useRef 和…

【WRF运行第三期】服务器上运行WRF模型(官网案例-Hurricane Matthew)

【WRF运行第三期】运行WRF模型&#xff08;官网案例-Hurricane Matthew&#xff09; 官网案例-Hurricane Matthew介绍0 创建DATA文件夹1 WPS预处理1.1 解压GRIB数据&#xff08;ungrib.exe&#xff09;1.1.1 解压GRIB数据---GFS&#xff08;Matthew案例研究数据&#xff09;1.1…