UE5: Content browser工具编写02

news/2024/10/4 4:25:53/

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.ppmy.cn/news/1533422.html

相关文章

【设计模式-命令】

定义 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使您能够使用不同的请求、排队请求或记录请求&#xff0c;并支持可撤销的操作。该模式通过将请求与其执行分离&#xff0c;使得请求者和接收者之间…

【MAUI】CommunityToolkit社区工具包介绍

一、为什么需要声明式开发 .NET的MVVM,始于WPF,很古典,它甚至可能是现代前端框架“声明式开发”的鼻祖。声明式开发,之所以出现,是因为命令式开发在UI层和代码层上无法解耦的问题。如下图所示: 1、命令式开发:后台代码需要调用UI层的控件(label.Text),如果更新UI层…

高精度(1)——高精度减法

题目描述 给定两个正整数&#xff08;不含前导 0&#xff09;&#xff0c;计算它们的差&#xff0c;计算结果可能为负数。 输入格式 共两行&#xff0c;每行包含一个整数。 输出格式 共一行&#xff0c;包含所求的差。 数据范围 1≤整数长度≤100000 输入样例 32 11 …

【2024】前端学习笔记11-网页布局-弹性布局flex

学习笔记 网页布局弹性布局&#xff1a;flex案例&#xff1a;flex布局案例 网页布局 在页面布局中&#xff0c;display属性用于设置一个元素的显示方式。它可以指定元素是作为块级元素、内联元素还是充当表格元素显示。 display的常见属性值&#xff1a; block&#xff1a;将…

解决$‘r‘ command not found或者文件夹显示’tvsf 33‘$‘r‘

问题现象: 某客户反馈在执行脚本的时候文件夹显示存在问题,如下图: 但是脚本文件中的内容并没有\r字符,如下图: 也有客户反馈如下: 问题分析: $\r’是回车符的转义表示。在Unix和Linux系统中,回车符是一个不可见的控制字符,它通常用于文本文件中的行结尾。以上…

Docker的实践应用举例

Docker作为一种流行的容器化技术&#xff0c;已经广泛应用于软件开发、部署、运维等多个领域。它通过将应用程序及其依赖项打包到可移植的容器中&#xff0c;极大地简化了应用的部署和管理过程。以下将详细探讨Docker的实践与应用&#xff0c;并通过多个具体例子来展示其强大的…

【d57】【sql】1661. 每台机器的进程平均运行时间

思路 一方面考察自连接&#xff0c;另一方面考察group by 这里主要说明 group by 用法&#xff1a; 1.在 SQL 查询中&#xff0c;GROUP BY 子句用于将结果集中的行分组&#xff0c;目的通常就是 对每个组应用聚合函数&#xff08;如 SUM(), AVG(), MAX(), MIN(), COUNT() 等…

python-ds:Python 中的数据结构库(适用于面试的数据结构和算法合集)

在软件开发中&#xff0c;数据结构是组织和存储数据的方式&#xff0c;对算法的效率和程序的性能至关重要。Python 提供了许多内置的数据结构&#xff0c;但在一些复杂的应用场景中&#xff0c;原生数据结构可能无法满足特定需求。这时&#xff0c;一个功能强大、易于使用的数据…