Zack26
(Zack26)
March 28, 2020, 8:23pm
1
Hello,
Is there a way to get blueprint classes that have a c++ base class?
For example, I have UItem.h and it is a blueprintable class. When we create items, we create a BP class based on UItem in our Items folder.
I can only think of having a datatable that has all of the itemnames and the exact file location in the table. If i have to do that, I’d like to have C++ automate that process. So when the game starts it scans the folder of items and creates the table and location.
I tried https://github.com/kamrann/KantanCod…lasses/Runtime
but it can’t get it to even instantiate a “GatherSubclasses” class. The error states the constructor is private…
Any one have any ideas or code examples for something like this?
Zack26
(Zack26)
March 29, 2020, 1:29am
2
Ok i’m sure someone needs this. The original code is here: http://kantandev.com/ Below is modified for out use but it could help you!
I have a class that is called FOGlobal. It stores some enums and handles a some mappings.
FOGlobal.h:
namespace GatherSubclasses {
namespace Detail {
void GetAllBlueprintSubclasses(TArray<TAssetSubclassOf <class UObject> >& OutSubClass, TSubclassOf<UObject> Base, FString const& Path);
}
template <typename TBase>
void GetAllBlueprintSubclasses(TArray <TAssetSubclassOf< TBase> >& Subclasses, TSubclassOf < TBase > Base, FString const& Path) {
TArray<TAssetSubclassOf < UObject > > Classes;
Detail::GetAllBlueprintSubclasses(Classes, Base, Path);
for(auto const& Cls : Classes) {
Subclasses.Add(Cls);
}
}
}
UCLASS()
class FORLORNOUTCAST_API UFOGlobal : public UObject {
GENERATED_UCLASS_BODY()
public:
static const UDataTable* GameData;
static const UDataTable* EffectData;
static int32 testInt;
private:
public:
static const FGameDataTable* GetGameDataRow(const FString Row);
static const FEffectDataTable* GetEffectRow(const FString Row);
static void GatherEffects(TSubclassOf<UObject> BaseClass);
static const float GetGameData(const FString Row, EMagicLevel MagicLevel);
};
FOGlobal.cpp:
#include "FOGlobal.h"
#include <UObject/ConstructorHelpers.h>
#include <Engine/DataTable.h>
#include "AssetRegistryModule.h"
#include "Ability/FOEffect.h"
#include "Engine/Blueprint.h"
#include "Modules/ModuleManager.h"
#include "UObject/SoftObjectPtr.h"
int32 UFOGlobal::testInt;
const UDataTable* UFOGlobal::GameData;
const UDataTable* UFOGlobal::EffectData;
namespace GatherSubclasses {
namespace Detail {
void GetAllBlueprintSubclasses(TArray< TAssetSubclassOf< UObject > >& Subclasses, TSubclassOf <UObject> Base, FString const& Path) {
static const FName GeneratedClassTag = TEXT("GeneratedClass");
static const FName ClassFlagsTag = TEXT("ClassFlags");
//get the registry
check(Base);
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(FName("AssetRegistry"));
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
//add any paths you want scanned
TArray<FString> ContentPaths;
ContentPaths.Add(Path);
AssetRegistry.ScanPathsSynchronous(ContentPaths);
//returns the FName of the UFOEffect
FName BaseClassName = Base->GetFName();
TSet<FName> DerivedNames;
{
TArray<FName> BaseNames;
BaseNames.Add(BaseClassName);
TSet<FName> Excluded;
//names to get, names to exclude, output
AssetRegistry.GetDerivedClassNames(BaseNames, Excluded, DerivedNames);
}
FARFilter Filter;
Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
Filter.bRecursiveClasses = true;
TArray<FAssetData> AssetList;
AssetRegistry.GetAssets(Filter, AssetList);
for(auto const& Asset : AssetList) {
if(auto GeneratedClassPathPtr = Asset.TagsAndValues.Find(GeneratedClassTag)) {
const FString ClassObjectPath = FPackageName::ExportTextPathToObjectPath(*GeneratedClassPathPtr);
const FString ClassName = FPackageName::ObjectPathToObjectName(ClassObjectPath);
if(!DerivedNames.Contains(*ClassName)) {
continue;
}
Subclasses.Add(TAssetSubclassOf<UObject>(FStringAssetReference(ClassObjectPath)));
}
}
}
}
}
UFOGlobal::UFOGlobal(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) {
static ConstructorHelpers::FObjectFinder<UDataTable>
GameDataTable_BP(TEXT("DataTable'/Game/ForlornOutcast/Data/GameData.GameData'"));
GameData = GameDataTable_BP.Object;
static ConstructorHelpers::FObjectFinder<UDataTable>
AbilityDataTable_BP(TEXT("DataTable'/Game/ForlornOutcast/Data/EffectData.EffectData'"));
EffectData = AbilityDataTable_BP.Object;
UE_LOG(LogTemp, Warning, TEXT("current: %d"), testInt);
}
const FGameDataTable* UFOGlobal::GetGameDataRow(const FString Row) {
static const FString ContextString(TEXT("GENERAL"));
FGameDataTable* DataRow = nullptr;
if (GameData) {
return GameData->FindRow<FGameDataTable>(*Row, ContextString);
}
return nullptr;
}
const FEffectDataTable* UFOGlobal::GetEffectRow(const FString Row) {
static const FString ContextString(TEXT("GENERAL"));
if(EffectData) {
return EffectData->FindRow<FEffectDataTable>(*Row, ContextString);
}
return nullptr;
}
void UFOGlobal::GatherEffects(TSubclassOf<UObject> BaseClass) {
TArray<TAssetSubclassOf < UObject> > DerivedSet;
if(BaseClass) {
GatherSubclasses::GetAllBlueprintSubclasses(DerivedSet, BaseClass, TEXT("/Game/ForlornOutcast/Blueprints/Effects"));
UE_LOG(LogTemp, Log, TEXT("Subclasses gathered with base '%s':"), *BaseClass->GetName());
for(auto& AssetClass : DerivedSet) {
auto LoadedClass = AssetClass.LoadSynchronous();
UE_LOG(LogTemp, Log, TEXT("%s %s"), AssetClass.IsNull() ? TEXT("[NONE]") : *AssetClass.GetUniqueID().ToString(), !AssetClass.IsNull() && !LoadedClass ? TEXT("_LOAD FAILED_") : TEXT(""));
}
}
}
GatherEffects is not something you want to do on tick! I would recommend doing this to load some classes and their reference locations into a table or something. I included some of our getters for table information…
A big thank you to KantanDev!