How to get a Texture2D (by name) in c++ and/or Blueprint?

So I know how to get objects by resource name in c++ using the following code: (example loading a DataTable)

ConstructorHelpers::FObjectFinder Resource(TEXT("DataTable'/Game/Data/name.name'"));

However, I believe (and maybe I’m wrong) you have to execute this code in a constructor. In my game I have level information stored in a DataTable. One of the columns in the DataTable is an FString that represents a texture resource that is to be used as a backdrop in the level. Now, at a later point in time when I load a level I would like to be able to get the texture by using the resource name. This code would not be part of a constructor but rather a generic method I create in my gameMode to get a Texture2D by resource name like so:

UTexture2D* MyGameMode::GetTextureResource(const FString resourceName) { return ??? }

I’m not sure how to implement such a method.

Another way would be to create a base blueprint that represents my level in C++ but I don’t know how to pass in the resource name as part of the constructor to my c++ class in order to take advantage of the ConstrutorHelper::FObjectFinder.

Am I going about this all wrong?

Thanks,

There might be better ways, but I’ll tell you what I’ve done.
The only downside is that you have to manually add each asset that you want to be able to reference.
It will also work with any type of asset, not just Texture2D.

Inside of my game instance (or in your case I suppose game mode should work too), I have the following properties:

UPROPERTY(EditAnywhere, Meta = (Category = "Asset Loader"))
TArray<UObject*> Assets;

// This will also allow you to get classes by name, can be left out if you don't want it.
UPROPERTY(EditAnywhere, Meta = (Category = "Asset Loader"))
TArray<TSubclassOf<UObject>> Classes;

Next extend your game instance (or game mode) in blueprints. Open up the blueprint and in the class defaults you can now add assets to the arrays.

Then in your Init function (or the equivalent for GameMode, possiblly PreInitializeComponents?)

for (UObject* Obj : Assets)
{
	AssetMap.Add(
		FName( *FStringAssetReference(Obj).GetLongPackageName().RightChop(6) ), // < RightChop: Remove the '/Game/' part from the beginning
		Obj
	);
}

// Also ignore this if you don't want to be able to load classes
for (TSubclassOf<UObject> Obj : Classes)
{
	AssetMap.Add(
		FName( *FStringAssetReference(Obj).GetLongPackageName().RightChop(6) ), // < RightChop: Remove the '/Game/' part from the beginning
		*Obj
	);
}

Then add the following functions:

// Simply returns a UObject pointer or null
static FORCEINLINE UObject* GetAsset(FName Path)
{
	UObject** Asset = Instance->AssetMap.Find(Path);
	return Asset ? *Asset : nullptr;
}

// A template version with an automatic cast.
template <class T>
static FORCEINLINE T* GetAsset(FName Path)
{
	UObject** Asset = Instance->AssetMap.Find(Path);
	return Asset ? Cast<T>(*Asset) : nullptr;
}

// A version for UClass
static FORCEINLINE UClass* GetAssetClass(FName Path)
{
	UObject** Class = Instance->AssetMap.Find(Path);
	return Class ? Cast<UClass>(*Class) : nullptr;
}

Now to get an asset call the function with the asset’s path relative to your content folder, eg:

// Plain:
UObject* Obj1 = UGame1Instance::GetAsset( FName(TEXT("Textures/Env/TEXTURE_NAME")) );
// Returns null:
UObject* Obj3 = UGame1Instance::GetAsset(FName( TEXT("This/Path/Does/Not/Exist")) );
// Templated:
UTexture2D* Obj2 = UGame1Instance::GetAsset<UTexture2D>( FName(TEXT("Textures/Env/TEXTURE_NAME")) );
// Same for classes:
UClass* Cls1 = UGame1Instance::GetAssetClass( FName(TEXT("Blueprints/Env/MyBlueprint")) );