Announcement

Collapse
No announcement yet.

When retrieving all subclasses of a C++ base class, how can I exclude the 'SKEL_' default classes?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    When retrieving all subclasses of a C++ base class, how can I exclude the 'SKEL_' default classes?

    I have BPs that inherit from an AShipClassBase class, and I'm trying to get all subclasses (the BPs) that inherit from this. The following code works for this purpose (and even excludes the AShipClassBase itself, since I don't want it):

    Code:
    ABaseGamemode::ABaseGamemode(const FObjectInitializer& ObjectInitializer)
    	: Super(ObjectInitializer)
    {
    	if (!IsDefaultSubobject())
    	{
    		TArray<UClass*> shipClasses;
    		for (TObjectIterator<UClass> It; It; ++It)
    		{
    			if (It->IsChildOf(AShipClassBase::StaticClass()) && !It->HasAnyClassFlags(CLASS_Abstract) 
    				&& *It->GetName() != AShipClassBase::StaticClass()->GetName())
    			{
    				shipClasses.Add(*It);
    			}
    		}
    	}
    }
    The problem is that I'm still getting an additional UClass for each asset, beginning with "SKEL_", but the EClassFlags in each are the same so I'm not sure how to exclude those (short of excluding those that start with "SKEL_", but was curious about nicer ways... what if I ended up starting an asset with "SKEL_" for some reason? Ya never know).

    Click image for larger version

Name:	8VeTtyi.jpg
Views:	1
Size:	59.6 KB
ID:	1185425

    Any ideas are appreciated!

    #2
    I think you are overcomplicating your iterator No need to iterate over UClass, and your TArray can be a TArray<AShipClassBase*> instead of TArray<UClass*>.

    Try this:

    Code:
    TArray<AShipClassBase*> shipClasses;
    for (TObjectIterator<AShipClassBase> It; It; ++It)
    {
    	if (!It->GetClass()->HasAnyClassFlags(EClassFlags::CLASS_Abstract))
    	{
    		shipClasses.Add(*It);
    	}
    }
    The default constructor for TObjectIterator is defined as TObjectIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true, /* more stuff */). So there should be no need to check IsDefaultSubobject() either, since it will already be excluded.

    I have no idea if this will actually fix your problem, but I think it's likely. If it doesn't, try constructing the iterator with exclusion flags RF_ClassDefaultObject | RF_Transient.

    Edit: forgot to say this, but for AActor derived classes it's actually usually more common to use TActorIterator. It works the same way, but its constructor needs a World pointer, like so:
    Code:
    for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
    {
    	// stuff
    }
    Last edited by Mattiwatti; 06-04-2016, 02:46 PM.

    Comment


      #3
      Originally posted by Mattiwatti View Post
      I think you are overcomplicating your iterator No need to iterate over UClass, and your TArray can be a TArray<AShipClassBase*> instead of TArray<UClass*>.

      Try this:

      Code:
      TArray<AShipClassBase*> shipClasses;
      for (TObjectIterator<AShipClassBase> It; It; ++It)
      {
      	if (!It->GetClass()->HasAnyClassFlags(EClassFlags::CLASS_Abstract))
      	{
      		shipClasses.Add(*It);
      	}
      }
      The default constructor for TObjectIterator is defined as TObjectIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true, /* more stuff */). So there should be no need to check IsDefaultSubobject() either, since it will already be excluded.

      I have no idea if this will actually fix your problem, but I think it's likely. If it doesn't, try constructing the iterator with exclusion flags RF_ClassDefaultObject | RF_Transient.

      Edit: forgot to say this, but for AActor derived classes it's actually usually more common to use TActorIterator. It works the same way, but its constructor needs a World pointer, like so:
      Code:
      for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
      {
      	// stuff
      }
      Hey Mattiwatti, at this point in the game, I'm actually trying to grab all BP assets that derive from AShipClassBase, rather than actors that exist in the world, hence using an object iterator

      I'll try the exclusion flags!

      To put it into context, this is me loading those BP assets then iterating objects and finding those that inherit from AShipClassBase (and excluding that class itself):

      Code:
      void ABaseGamemode::LoadShipClasses()
      {
      #ifdef WITH_EDITOR
      	// clear up first in-case we're PIE.
      	FShipClassManager::Get()->Reset();
      #endif 
      
      	// Force-load everything at location of ship class assets so their UClasses show up when iterating
      	TArray<UObject*> loadedShipClassAssets;
      	EngineUtils::FindOrLoadAssetsByPath(ShipClassAssetPath, loadedShipClassAssets, EngineUtils::ATL_Regular);
      
      	TArray<UClass*> shipAssetClasses;
      	for (TObjectIterator<UClass> It; It; ++It)
      	{
      		FString name = It->GetName();
      
      		if (It->IsChildOf(AShipClassBase::StaticClass())
      			&& !It->HasAnyClassFlags(CLASS_Abstract)
      			&& name != AShipClassBase::StaticClass()->GetName()
      			&& !name.StartsWith("SKEL_")
      			&& !name.StartsWith("REINST"))
      		{
      			shipAssetClasses.Add(*It);
      		}
      	}
      
      	FShipClassManager* classManager = FShipClassManager::Get();
      	for (int32 i = 0; i < shipAssetClasses.Num(); ++i)
      	{
      		AShipClassBase* shipClassBase = shipAssetClasses[i]->GetDefaultObject<AShipClassBase>();
      		classManager->RegisterShipClass(shipClassBase, shipAssetClasses[i]);
      	}
      }
      I could probably just load the assets from that folder directly then iterate over those at this point, so I might try that later (although this works fine for now).

      Thanks for the reply!

      Comment


        #4
        TObjectIterator is rather low level and in practice there's very rarely a need to use it and generally a better way. In this case it won't work properly anyway since it only iterates objects in memory - blueprints (or rather their generated classes) that haven't been loaded at the time won't be found.

        I think you should probably be using the asset registry for something like this. See FAssetRegistryModule::Get and the IAssetRegistry interface. Keep in mind though that the difference between editor and packaged builds here is significant. Blueprint assets only exist in the editor. In a packaged build, the blueprints are gone, but the associated UBlueprintGeneratedClass, which is what you're after, will be there.

        Comment

        Working...
        X