Hey Guys. I have a bunch of textures within a particular folder in my project, and I want to store the contents of that folder into an array in my blueprint. I know I can create a public variable for the array and add them all by hand, but I’d like a better way of doing things (I’m going to have dozens of textures in there so adding them all by hand would be a pain). Is there a way I can give my blueprint a folder name or directory or something and have the blueprint automatically go through the folder and add all the textures to the array for me?
for numbers I know there is a possibility to select them all from a .txt file and just paste them in the array. he creates all the elements he needs if its separated by a ,
try to select all the textures, copy them, and paste them into an empty array of same type. maybe this works for textures also but im not sure
No, but you can write a C++ thing for it. I’ll share my code with you that does this exact thing if you’d like, are you comfortable with adding C++ functions?
header:
UFUNCTION(BlueprintCallable, Category = "Sweet|Utilities")
static TArray<UObject*> DynamicLoadContentFromPath(FString PathFromContentFolder = "Audio/Music", UClass* AssetClass = nullptr, bool LoadFromSubfolders = false);
source:
TArray<UObject*> USweetFunctionLibrary::DynamicLoadContentFromPath(FString PathFromContentFolder /*= "Audio/Music"*/, UClass* AssetClass, bool LoadFromSubfolders)
{
TArray<UObject*> Array;
FString RootFolderFullPath = FPaths::ConvertRelativePathToFull(FPaths::GameDir()) + "Content/" + PathFromContentFolder + "/";
Print("RootFolderPath = " + RootFolderFullPath);
//FPaths::NormalizeDirectoryName(RootFolderFullPath);
//Print("Normalized RootFolderPath = " + RootFolderFullPath);
IFileManager& FileManager = IFileManager::Get();
TArray<FString> Files;
FString Ext;
if (LoadFromSubfolders)
{
if (!Ext.Contains(TEXT("*")))
{
if (Ext == "")
{
Ext = "*.*";
}
else
{
Ext = (Ext.Left(1) == ".") ? "*" + Ext : "*." + Ext;
}
}
FileManager.FindFilesRecursive(Files, *RootFolderFullPath, *Ext, true, false);
}
else
{
if (!Ext.Contains(TEXT("*")))
{
if (Ext == "")
{
Ext = "*.*";
}
else
{
Ext = (Ext.Left(1) == ".") ? "*" + Ext : "*." + Ext;
}
}
FileManager.FindFiles(Files, *(RootFolderFullPath + Ext), true, false);
}
for (int32 i = 0; i < Files.Num(); i++)
{
FString Path;
if (LoadFromSubfolders)
{
int32 LastForwardSlash = Files*.Find("/", ESearchCase::IgnoreCase, ESearchDir::FromEnd);
FString File = Files*.RightChop(LastForwardSlash + 1);
FString Folder = Files*.RightChop(Files*.Find(PathFromContentFolder, ESearchCase::CaseSensitive, ESearchDir::FromEnd) + PathFromContentFolder.Len());
Folder = Folder.LeftChop(File.Len() + 1);
File = File.Left(File.Find(".", ESearchCase::IgnoreCase, ESearchDir::FromEnd));
Path = AssetClass->GetFName().ToString() + "'/Game/" + PathFromContentFolder + Folder + "/" + File + "." + File + "'";
}
else
{
Path = AssetClass->GetFName().ToString() + "'/Game/" + PathFromContentFolder + "/" + Files*.LeftChop(7) + "." + Files*.LeftChop(7) + "'";
}
UObject* LoadedObj = StaticLoadObject(AssetClass, NULL, *Path);
Array.Add(LoadedObj);
}
for (int32 i = 0; i < Array.Num(); i++)
{
if (Array.Num() > 0 && Array* != nullptr)
{
Print(Array*->GetFName().ToString());
}
else
{
Print("Array is empty");
}
}
return Array;
}
Sort of a hacky job borrowing some of Rama’s code and working it into a way that works for me.
“Path from content folder” is exactly what it sounds like. The the folder structure starting AFTER “Content.” So if your textures are in “GameFolder/Content/Textures/SpecialTextures” then you’ll just write “Textures/SpecialTextures” here.
Specify the asset class you want to find and load. This filters out unwanted objects. In this example, I’m looking for Soundwaves. You can also specify whether or not you want to search subfolders.
You can only return one type from C++, so you’ll have to cast each of the UObjects that come back when placing them into your array.
This can run anytime - during the game at runtime, or before the game in the construction script or as part of a blutility. It all depends on when you want it to be loaded. You can also load them at runtime and copy the filled array from the inspector then paste it into the array variable.
This is a handy node - maybe you could offer it to Rama for the Victory plugin? This would have helped me out so much a few months ago.
Thanks so much for that ! This is exactly what I need!
I’m trying to add this code to my project but I keep getting an error ever time I compile. The error reads ‘identifier “Print” is undefined’.
I only know the basics of programming so I’m not too sure what’s going, and rampant google searching has provided no solutions. Do you know whats going on?
Oh right, sorry about that. I have another method I use to print to the screen that simplifies the call to the KismetSystemLibrary. You can just remove all of the lines that reference “Print().”
basically just this line near the top:
Print("RootFolderPath = " + RootFolderFullPath);
And this for loop:
for (int32 i = 0; i < Array.Num(); i++)
{
if (Array.Num() > 0 && Array* != nullptr)
{
Print(Array*->GetFName().ToString());
}
else
{
Print("Array is empty");
}
}
There’s another line in there that uses Print but it’s commented out. Remove it if you’d like but it doesn’t hurt anything.
Sorry to be a pain but I have another question haha. It’s working, but what its doing is loading the entire contents of the folder as object references. Anything that doesn’t match the specified asset class is added with a value of ‘None’. I’ve got a way of filtering out the things I don’t want, so that I am left with an array of just my textures, but they’re still object references, instead of texture references. Is there a way to modify the code so that the type of array it outputs is the same as the one specified in the “Asset Class” Parameter? Or even just convert the object array to my desired type?
I’d go with FAssetRegistry
Nope, which is why you have to run a for loop on the object reference array, cast each to your desired class, the pop that into your array.
this looks very interesting so this code goes only in the header file, im sorry im new to fiddling with c++ can you be so kind as to explain where this code would go please?
Code under ‘header’ goes in your .h file and code under ‘source’ goes in your .cpp file which references your header file.
There are many youtube tutorials for helping you get the basics working! Just make sure you use the code without the ‘Print’ references or it won’t compile, like so:
TArray<UObject*> USweetFunctionLibrary::DynamicLoadContentFromPath(FString PathFromContentFolder /*= "Audio/Music"*/, UClass* AssetClass, bool LoadFromSubfolders) { TArray<UObject*> Array; FString RootFolderFullPath = FPaths::ConvertRelativePathToFull(FPaths::GameDir()) + "Content/" + PathFromContentFolder + "/"; //FPaths::NormalizeDirectoryName(RootFolderFullPath); IFileManager& FileManager = IFileManager::Get(); TArray<FString> Files; FString Ext; if (LoadFromSubfolders) { if (!Ext.Contains(TEXT("*"))) { if (Ext == "") { Ext = "*.*"; } else { Ext = (Ext.Left(1) == ".") ? "*" + Ext : "*." + Ext; } } FileManager.FindFilesRecursive(Files, *RootFolderFullPath, *Ext, true, false); } else { if (!Ext.Contains(TEXT("*"))) { if (Ext == "") { Ext = "*.*"; } else { Ext = (Ext.Left(1) == ".") ? "*" + Ext : "*." + Ext; } } FileManager.FindFiles(Files, *(RootFolderFullPath + Ext), true, false); } for (int32 i = 0; i < Files.Num(); i++) { FString Path; if (LoadFromSubfolders) { int32 LastForwardSlash = Files*.Find("/", ESearchCase::IgnoreCase, ESearchDir::FromEnd); FString File = Files*.RightChop(LastForwardSlash + 1); FString Folder = Files*.RightChop(Files*.Find(PathFromContentFolder, ESearchCase::CaseSensitive, ESearchDir::FromEnd) + PathFromContentFolder.Len()); Folder = Folder.LeftChop(File.Len() + 1); File = File.Left(File.Find(".", ESearchCase::IgnoreCase, ESearchDir::FromEnd)); Path = AssetClass->GetFName().ToString() + "'/Game/" + PathFromContentFolder + Folder + "/" + File + "." + File + "'"; } else { Path = AssetClass->GetFName().ToString() + "'/Game/" + PathFromContentFolder + "/" + Files*.LeftChop(7) + "." + Files*.LeftChop(7) + "'"; } UObject* LoadedObj = StaticLoadObject(AssetClass, NULL, *Path); Array.Add(LoadedObj); } return Array; }
Sorry for reviving a dead thread, I am wondering what this “Files*.Find()” syntax, I haven’t seen that before and its giving me compiler errors. The code seems to be compiling fine apart from this but google hasn’t pulled up any results when trying to find out what it is/how to fix it.
Honestly I haven’t attempted to fix that method since it broke some engine versions ago. Now I just use Rama’s plugin.
Runtime DataTable : Import and export game data to and from CSV or Google Sheets while your game is running!
easyCSV : Read data from any CSV and put them in an easy-to-access string map!
iTween : Free, smooth, procedural object animation
Necro’ing an old thread but this still comes up in searches.
Asset registry is generally what you want to use. Get the asset registry, get assets by path, iterate through those asset data entries to get the individual assets, cast them and stash them in your array. Something like this will work.
Apologies to once again Necro this old thread, but I managed to translate the newer syntax while also completely missing the newer replies offering other methods. It still comes up in search so I figured I may as well possibly help the next person to come along. I do remember having some issues with Asset Registry though I don’t remember what exactly.
the “Files*.Find()” syntax should now be Files[i].Find(). you’ll also need to change Array* to &Array and the final print statement became UE_LOG(LogTemp, Log, TEXT("arrlog array: %s"), *Array[j]->GetName())
another important note is that the StaticLoadObject() function now seems to take an extra parameter. I just copied the same parameter twice, final code being StaticLoadObject(AssetClass, nullptr, *Path, *Path)
GameDir() is depreceated, so I now use GameSourceDir() and changed the subsequent string to “…/Content”. I think I’m missing one or two further changes and I won’t post my full code since I’m sure I made other sloppy mistakes elsewhere that’ll affect performance or general code cleanliness. But if you still want to use the C++ code snippet provided then those are the major steps to updating it to 2024