Download

How to let subclasses specify asset paths to use in base constructor?

So, I’ve created a hierarchy where the base class constructor pretty much does all of the heavy lifting for creating objects using ConstructorHelpers.

Now, for subclasses, I want them to be able to set the paths to use for their specific assets before calling the whole heavy lifting parts in the parent constructor.
I tried creating my own virtual Init function, which is called at the start of the Base constructor to set some text references, and then I override this Init function in subclasses so they can set their own asset path references.
This does not work however, as it seems that only the base class Init function is ever called.

An example:

PWNWeapon.cpp - The Base class



// Constructor
APWNWeapon::APWNWeapon(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
        // Set asset path for this specific class
        SetConstructorRefs();

        // Find and create arm mesh
	static ConstructorHelpers::FObjectFinder<USkeletalMesh> armMeshObLeft(*ConstRefArmMeshLeftName);
	if (armMeshObLeft.Object)
		ArmMeshLeft = armMeshObLeft.Object;
}

void APWNWeapon::SetConstructorRefs()
{
	ConstRefArmMeshLeftName = TEXT("SkeletalMesh'/Game/PWNGame/FPSArmsYYY/FPSArm_YYY.FPSArm_YYY'");
}


PWNWeapon_Melee - The child class:



// Constructor
APWNWeapon_Melee::APWNWeapon_Melee(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
        // Set asset path for this specific class
        // I assume that when the base constructor is called, it will call my version of SetConstructorRefs() to use my specific assets
}

void APWNWeapon_Melee::SetConstructorRefs()
{
	ConstRefArmMeshLeftName = TEXT("SkeletalMesh'/Game/PWNGame/FPSArmsXXX/FPSArm_XXX.FPSArm_XXX'");
}


So what I’m hoping to achieve is to just have each subclass set their specific text references, and the base class constructor then uses these to construct the actual objects.
This approach does not work, so could anyone please give me a hint on what kind of pattern I can use to get this type of behaviour working?

Currently the only solution I see is to copy the whole base class constructor to the child class, which seems like a massive hack.
Another solution I thought of was to defer the construction of the objects, but then I can’t use ConstructorHelpers, as they can only be used in a constructor.

I’m really stuck on this one so any help would be appreciated.

Ok, soliloquy time again.

I’ve read up a bit more on C++ inheritance and constructors, and it seems like the situation is as follows in UE4:

The constructor seems like it can only really be used to create specific objects.
If you want to have a generic approach to how subclasses spawn their own specific assets, you cannot use the constructor.
This is due to the fact that when you spawn an instance of a child class, the code will start out by calling the base class constructor,
then the first child class constructor, then the next one and so forth until it reaches the actual child class you’re spawning.
E.g. suppose you have a hierarchy as follows

Weapon
----MeleeWeapon
--------SwordWeapon
--------AxeWeapon
----HandgunWeapon
--------Glock19Weapon
--------BrowningHPWeapon
--------ThirtyEightSpecialWeapon
----RifleWeapon
--------SniperRifleWeapon
--------Shotgun

When creating a new pointer to an instance of BrowningHPWeapon, the constructors are called in the following order:

  • Weapon
  • HandgunWeapon
  • BrowningHPWeapon

So, setting variables directly in the constructors will effect BrowningHPWeapon’s values being used, BUT if you are setting variables that are used in other subroutines of e.g. Weapon,
these other subroutines will execute BEFORE AxeWeapon’s constructor is called!
This means e.g. if you have a text reference to an asset, you cannot simply have the code to spawn that asset sit in Weapon’s constructor if you want to override it in subclasses.
No, you have to use another mechanism. This in effect makes the constructor useful only for setting default base class variables (i.e. to prevent NULL values).

The way I’m going about this is to have a function called “Init”, which is not part of the construction or subroutines, and is called independently after spawning the class. (Some further reading has also shown that you can use functions like OnConstruction, PostInitializeComponents etc. which are not technically part of the construction routine - they get called automatically after construction).
Then from this “Init” function I call another virtual function called “SetAssetReferences”, which is marked as virtual so that subclasses can set their references there.
SetAssetReferences() is then called at the start of Init in Weapon. In this way, the code will use the last overridden version of SetAssetReferences() leading to each class
being able to set their explicit references independently, but still retain the ease of having a single Initialising function.

Please note that this approach prevents you from using ConstructionHelpers to find assets, as they can only be used in Constructors.

You will have to use other methods of finding assets at runtime:

Rama’s Tutorial

AnswerHub Discussion