UE5 C++: 插件编写06 | 移动文件时自动Fix up redirectors

目录

前言:

本文内容:

[WHY WHAT HOW](#WHY WHAT HOW)

详细步骤

代码解析

[1. Build.cs file中](#1. Build.cs file中)

[2. QuickAssetAction.cpp中](#2. QuickAssetAction.cpp中)

[IMPORTANT NOTES](#IMPORTANT NOTES)

中文解释:

[使用 AssetToolsModule 来修复重定向器](#使用 AssetToolsModule 来修复重定向器)

[使用 AssetRegistryModule 来过滤所有重定向器](#使用 AssetRegistryModule 来过滤所有重定向器)

[使用 FModuleManager 来加载不同的模块](#使用 FModuleManager 来加载不同的模块)

[F 前缀的含义](#F 前缀的含义)

FModuleManager的作用

FARFilter


前言:

移动asset时需要手动地fix up redirectors,我们不指望引擎用户能每次都记得操作,而忘记操作会导致不确定的data lost。

本文内容:

在代码中实现,移动文件时自动fix up redirectors


WHY WHAT HOW

why:Avoid data lost

++what:Fix up redirectors from code++

how:找到Fix up redirectors in folder这个按钮,并确定在点击这个按钮时何种function被调用。

the key function to achieve this is in, AssetToolsModule and AssetRegistryModule

  • AssetToolsModule: FixUpReferences(TArray<UObjectRedirectors*>)
  • AssetRegistryModule: GetAssets(FARFilter) (to get all the redirectors)
  • (to use the functions inside of these two different modules) FModuleManager ::LoadModuleChecked <>()

详细步骤

在QuickAssetAction.h的private部分里调用

cpp 复制代码
void FixUpRedirectors();

在QuickAssetAction.cpp里创建声明

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "AssetAction/QuickAssetAction.h"
#include "DebugHeader.h"
#include "EditorUtilityLibrary.h"
#include "EditorAssetLibrary.h"
#include "ObjectTools.h"
#include "AssetRegistryModule.h"//fix up
#include "AssetToolsModule.h"//use it when we want to create assets from code


//void UQuickAssetAction::TestFuckingFunc()
//{
//	Print(TEXT("I hate the plugin 2"), FColor::Cyan);
//	PrintLog(TEXT("杀杀杀"));
//
//	//if (GEngine) 
//	//{
//	//	GEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Yellow, TEXT("I hate this plugin"));
//	//}
//}

void UQuickAssetAction::BatchDuplication(int32 NumOfDuplicates)
{
	if (NumOfDuplicates<=0) {

		DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("Please enter a VALID number"));
		return;
	}

	TArray<FAssetData>SelectedAssetsData = UEditorUtilityLibrary::GetSelectedAssetData();
	uint32 Counter = 0;

	for (const FAssetData& SelectedAssetData : SelectedAssetsData)
	{
		for (int32 i = 0; i < NumOfDuplicates; i++) {
			const FString SourceAssetPath = SelectedAssetData.ObjectPath.ToString();
			const FString NewDuplicatedAssetName = SelectedAssetData.AssetName.ToString() + TEXT("_") + FString::FromInt(i + 1);
			const FString NewPathName = FPaths::Combine(SelectedAssetData.PackagePath.ToString(), NewDuplicatedAssetName);

			if(UEditorAssetLibrary::DuplicateAsset(SourceAssetPath, NewPathName))
			{
				UEditorAssetLibrary::SaveAsset(NewPathName, false);
				++Counter;
			}
		}
	}

	if (Counter > 0)
	{
		DebugHeader::ShowNotifyInfo(TEXT("Successfully duplicated " + FString::FromInt(Counter) + " files"));
		//Print(TEXT("Successfully duplicated " + FString::FromInt(Counter) + " files"), FColor::Green);
	}
		

}

void UQuickAssetAction::AddPrefixes()
{
	TArray<UObject*>SelectedObjects = UEditorUtilityLibrary::GetSelectedAssets();
	uint32 Counter = 0;

	for (UObject* SelectedObject : SelectedObjects)
	{
		if (!SelectedObject) continue;//空指针检查  SelectedObject 是 nullptr
		FString* PrefixFound = PrefixMap.Find(SelectedObject->GetClass());
		//这一行查找 SelectedObject 对象对应的类(SelectedObject->GetClass())在 PrefixMap 中的值。
		// PrefixMap 是一个自己命名的TMap<UClass*, FString>键值对的映射,
		// 其中键是 UClass*(对象的类)如material,值是 FString(M_)。

		if (!PrefixFound || PrefixFound->IsEmpty())
		{
			DebugHeader::Print(TEXT("Failed to find prefix for class ") + SelectedObject->GetClass()->GetName(), FColor::Red);
			continue;
		}

		FString OldName = SelectedObject->GetName();

		if (OldName.StartsWith(*PrefixFound))
		{
			DebugHeader::Print(OldName + TEXT(" already has prefix added"), FColor::Red);
			continue;
		}

		//如果类型是MI_old name里有M_和_Inst,移除他们。
		if (SelectedObject->IsA<UMaterialInstanceConstant>())
		{
			OldName.RemoveFromStart(TEXT("M_"));
			OldName.RemoveFromEnd(TEXT("_Inst"));
		}



		const FString NewNameWithPrefix = *PrefixFound + OldName;
		UEditorUtilityLibrary::RenameAsset(SelectedObject, NewNameWithPrefix);

		++Counter;//将计数器 Counter 的值增加 1。它用于记录处理了多少个对象。
		//在脚本完成后,下面那串代码输出一条信息,例如 "成功处理了 X 个对象"。
	}
	if(Counter>0)
	{
		DebugHeader::ShowNotifyInfo(TEXT("Successfully renamed " + FString::FromInt(Counter) + " assets"));
	}
}

void UQuickAssetAction::RemoveUnusedAssets()
{
	TArray<FAssetData> SelectedAssetsData = UEditorUtilityLibrary::GetSelectedAssetData();
	TArray<FAssetData> UnusedAssetsData;

	FixUpRedirectors();

	for (const FAssetData& SelectedAssetData : SelectedAssetsData) 
	{
		TArray<FString> AssetReferencers =
			UEditorAssetLibrary::FindPackageReferencersForAsset(SelectedAssetData.ObjectPath.ToString());

		if (AssetReferencers.Num() == 0) {
			UnusedAssetsData.Add(SelectedAssetData);
		}//把它加入废物列表
	}

	if (UnusedAssetsData.Num() == 0) {
		DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("No unused asset found among selected assets"), false);
		return;
	}
	//查找未使用资产


	//有未使用资产
	else {
		const int32 NumOfAssetsDeleted = ObjectTools::DeleteAssets(UnusedAssetsData);//执行删除
		if (NumOfAssetsDeleted > 0) {
			DebugHeader::ShowNotifyInfo(TEXT("Successfully deleted " + FString::FromInt(NumOfAssetsDeleted) + TEXT(" unused assets.")));
		}
		else {
			DebugHeader::ShowNotifyInfo(TEXT("No assets were deleted.")); // 只有在取消删除资产时显示这条消息
		}
	}

	//const int32 NumOfAssetDeleted = ObjectTools::DeleteAssets(UnusedAssetsData);//执行删除
	//if (NumOfAssetDeleted == 0)return;
	//ShowNotifyInfo(TEXT("Successfully deleted " + FString::FromInt(NumOfAssetsDeleted) + TEXT(" unused assets.")));
}

void UQuickAssetAction::FixUpRedirectors()
{
	TArray<UObjectRedirector*> RedirectorsToFixArray;//fixup

	FAssetRegistryModule& AssetRegistryModule =
	FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));

	FARFilter Filter;
	Filter.bRecursivePaths = true;
	Filter.PackagePaths.Emplace("/Game");
	Filter.ClassNames.Emplace("ObjectRedirector");

	TArray<FAssetData> OutRedirectors;

	AssetRegistryModule.Get().GetAssets(Filter,OutRedirectors);

	for (const FAssetData& RedirectorData : OutRedirectors)
	{
		if (UObjectRedirector* RedirectorToFix = Cast<UObjectRedirector>(RedirectorData.GetAsset()))
			//cast from UObject* to UObjectRedirector*
		{
			RedirectorsToFixArray.Add(RedirectorToFix);
		}
	}

	FAssetToolsModule& AssetToolsModule =
	FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));

	AssetToolsModule.Get().FixupReferencers(RedirectorsToFixArray);
}

代码解析


1. Build.cs file中

插件的build.cs file里加入"AssetTools"的PublicDependency

cpp 复制代码
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core","Blutility","EditorScriptingUtilities","UMG","Niagara","UnrealEd","AssetTools"//,"ContentBrowser"
				// ... add other public dependencies that you statically link with here ...
			}
			);

2. QuickAssetAction.cpp中

#include "AssetRegistryModule.h"//fix up

#include "AssetToolsModule.h"//use it when we want to create assets from code

在void UQuickAssetAction::RemoveUnusedAssets()里调用一下修复重定向器功能

FixUpRedirectors();


QuickAssetAction.cpp的修复重定向功能定义

cpp 复制代码
void UQuickAssetAction::FixUpRedirectors()
//定义一个名为 FixUpRedirectors 的函数,属于 UQuickAssetAction 类。这个函数的目的是修复重定向器。
{
	TArray<UObjectRedirector*> RedirectorsToFixArray;//fixup
	//创建一个数组 RedirectorsToFixArray,用来存储需要修复的重定向器对象的指针。

	FAssetRegistryModule& AssetRegistryModule =
	FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
	//使用 FModuleManager 加载 AssetRegistryModule 模块,并将其引用存储在 AssetRegistryModule 中。
	// 如果模块加载失败,将引发一个错误。*注意拼写

	FARFilter Filter;
	//创建一个 FARFilter 对象,用于定义资产过滤条件。
	Filter.bRecursivePaths = true;
	//设置过滤器的 bRecursivePaths 属性为 true,表示在指定路径下递归搜索资产。

	Filter.PackagePaths.Emplace("/Game");
	//将 /Game 路径添加到过滤器中,这意味着在该路径下寻找资产。
	Filter.ClassNames.Emplace("ObjectRedirector");
	//将 ObjectRedirector 类添加到过滤器中,表示只查找这种类型的资产。

	TArray<FAssetData> OutRedirectors;
	//创建一个数组 OutRedirectors,用来存储从资产注册表中检索到的资产数据。

	AssetRegistryModule.Get().GetAssets(Filter,OutRedirectors);
	//调用 AssetRegistryModule 中的 GetAssets 函数,
	// 传入定义的过滤器 Filter 和输出数组 OutRedirectors,以获取符合条件的重定向器资产数据。

	for (const FAssetData& RedirectorData : OutRedirectors)
		//遍历 OutRedirectors 中的每个 FAssetData 对象,命名为 RedirectorData。
	{
		if (UObjectRedirector* RedirectorToFix = Cast<UObjectRedirector>(RedirectorData.GetAsset()))
			//cast from UObject* to UObjectRedirector*
			// 中文解释
			//尝试将 RedirectorData.GetAsset() 返回的资产转换为 UObjectRedirector* 类型。
			// 如果转换成功,RedirectorToFix 将是一个有效的指针。
		{
			RedirectorsToFixArray.Add(RedirectorToFix);
			//如果转换成功,将指向重定向器的指针 RedirectorToFix 添加到 RedirectorsToFixArray 中。
		}
	}

	FAssetToolsModule& AssetToolsModule =
	FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
	//加载 AssetToolsModule 模块,并将其引用存储在 AssetToolsModule 中。

	AssetToolsModule.Get().FixupReferencers(RedirectorsToFixArray);
	//调用 FixupReferencers 方法,传入 RedirectorsToFixArray,以修复所有收集到的重定向器。
}

IMPORTANT NOTES

  • Use AssetToolsModule to fix up redirectors
  • Use AssetRegistryModule to filter out all the redirectors
  • Use FModuleManager to load different modules

中文解释:

使用 AssetToolsModule 来修复重定向器

AssetToolsModule 提供了一些工具和功能,帮助开发者管理和处理资产,特别是关于重定向器(Redirector)的操作。重定向器通常在资产被移动或重命名时自动生成,使用 AssetToolsModule 可以帮助修复这些重定向器,以确保资产能够正确引用。

使用 AssetRegistryModule 来过滤所有重定向器

AssetRegistryModule 负责管理引擎中的资产注册表,允许你查询和过滤资产信息。通过这个模块,可以轻松获取项目中的所有资产,包括重定向器,并根据特定条件进行过滤。这有助于确保你只处理所需的资产类型,避免无用的重定向器干扰你的逻辑。

使用 FModuleManager 来加载不同的模块

FModuleManager 是 Unreal Engine 的模块管理器,负责加载和卸载模块。由于模块可以按需加载,使用 FModuleManager 可以确保在运行时动态加载所需的模块。例如,当你需要使用 AssetToolsModuleAssetRegistryModule 的功能时,首先要确保这些模块已经被加载。

F 前缀的含义

在 Unreal Engine 中,F 前缀通常用于表示 结构体 (struct)或 (class),是 Unreal Engine 的编码约定之一。这种约定旨在帮助开发者区分不同类型的对象。

  1. F 表示 "Fundamental""Type"F 主要用来标识基本类型、数据结构或类的定义。它与其他前缀(如 UA)结合使用,有助于理解对象的角色。

  2. 类型区分

    • F 开头:表示一个普通的 C++ 结构体或类,通常不具有与 Unreal 引擎的对象系统相关的特性(如反射、序列化等)。例如,FVectorFStringFAssetData
    • U 开头:表示一个 Unreal Engine 的 UObject 派生类,具有引擎的对象特性,如反射和序列化。比如 UActorUObject
    • A 开头:表示一个 Unreal Engine 的 Actor 类,通常用来表示游戏世界中的对象,例如 ACharacterAPlayerController

示例

  • FVector:表示一个三维向量,常用于位置、方向等。
  • FString:表示一个字符串,通常用于文本处理。
  • FAssetData:表示资产的元数据,通常用于查询资产信息。

FModuleManager的作用

UE中,模块是逻辑上组织代码的单元。每个模块可以提供特定的功能,而 FModuleManager 是用于管理这些模块的类。

  • 动态加载:Unreal Engine 的模块可以根据需要加载,FModuleManager 提供了接口来加载和卸载这些模块,而不需要在编译时静态链接。这意味着你可以根据具体需要来加载相应的模块,从而优化资源使用。

  • 模块管理:FModuleManager 负责管理模块的生命周期,包括模块的初始化、清理等。当一个模块被加载时,FModuleManager 会确保模块的所有依赖项都已被正确处理。

  • 提高灵活性:通过 FModuleManager,你可以在运行时决定是否加载某个模块,这样可以实现更灵活的程序设计,允许你根据条件或配置来控制哪些功能被启用。

FARFilter

FARFilter 是 Unreal Engine 中用于定义和处理 Actor 的过滤器的结构体。(FARFilter 中的 "FAR" 是 F ilter A sset Registry 的缩写。)它通常在进行查找(例如通过 GetAllActorsOfClass 或其他类似函数)时使用,以限制返回的结果。

相关推荐
Ddddddd_1581 小时前
C++ | Leetcode C++题解之第504题七进制数
c++·leetcode·题解
J_z_Yang1 小时前
LeetCode 202 - 快乐数
c++·算法·leetcode
Y.O.U..4 小时前
STL学习-容器适配器
开发语言·c++·学习·stl·1024程序员节
lihao lihao4 小时前
C++stack和queue的模拟实现
开发语言·c++
姆路5 小时前
QT中使用图表之QChart概述
c++·qt
西几5 小时前
代码训练营 day48|LeetCode 300,LeetCode 674,LeetCode 718
c++·算法·leetcode
风清扬_jd5 小时前
Chromium HTML5 新的 Input 类型week对应c++
前端·c++·html5
南东山人6 小时前
C++静态成员变量需要在类外进行定义和初始化-error LNK2001:无法解析的外部符号
c++
lqqjuly6 小时前
C++ 中回调函数的实现方式-函数指针
开发语言·c++
2401_871120356 小时前
数组与指针基础
c++