目录
[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 前缀的含义)
前言:
移动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
可以确保在运行时动态加载所需的模块。例如,当你需要使用 AssetToolsModule
或 AssetRegistryModule
的功能时,首先要确保这些模块已经被加载。
F
前缀的含义
在 Unreal Engine 中,F
前缀通常用于表示 结构体 (struct)或 类(class),是 Unreal Engine 的编码约定之一。这种约定旨在帮助开发者区分不同类型的对象。
-
F 表示 "Fundamental" 或 "Type" :
F
主要用来标识基本类型、数据结构或类的定义。它与其他前缀(如U
或A
)结合使用,有助于理解对象的角色。 -
类型区分:
F
开头:表示一个普通的 C++ 结构体或类,通常不具有与 Unreal 引擎的对象系统相关的特性(如反射、序列化等)。例如,FVector
、FString
、FAssetData
。U
开头:表示一个 Unreal Engine 的 UObject 派生类,具有引擎的对象特性,如反射和序列化。比如UActor
、UObject
。A
开头:表示一个 Unreal Engine 的 Actor 类,通常用来表示游戏世界中的对象,例如ACharacter
、APlayerController
。
示例
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
或其他类似函数)时使用,以限制返回的结果。