Examples of instantiating custom actors in code

So I have an actor that has a couple of additional properties that I want to initialize at time of creation. Were this vanilla C++, it’s just a matter of having a constructor that accepts in what values I want an instantiation to start with. Except the Unreal Engine has a couple of wrinkles to how it uses C++ and I have yet to see any actual instances of just having a constructor with a bunch of custom parameters. For that matter, the example projects I’ve looked at seem to go out of their way to avoid showing how to dynamically instantiate actors. I’ve seen in examples of using the Slate classes the constructor has an FArguments argument that can be filled in with entries based on stuff marked with SLATE_ATTRIBUTE or etc in the class declaration. Do Actor classes have something similar?

As an aside, the entire object model that Unreal uses isn’t covered very well in existing documentation. I come from a C++ background but not one that’s heavily into smart pointers and only light usage of templates and whenever I look at the code, I always feel like I’m missing something. Rama’s page on garbage collection in Unreal has helped a bit, but I feel like I need a crash course on “Unreal C++” to know how to properly interface with the engine.

My plebian opinion on Unreal constructors is that you should only use them to initialise values to avoid null references.

What I mean by this is that suppose you have a class that you know you will be extending other classes from, and this base class has some non-basic properties (basic here refers to float/int/Vector etc. and non-basic refers to objects and actors), you should do the instantiation of these “non-basic” properties somewhere in the Spawn callback process (functions like BeginPlay/OnConstruction/OnComponentInitialize).

This is due to the fact that you cannot call virtual methods from a constructor, or more accurately, it will only call the base class’ version of the virtual function from the constructor.

I was also a bit irritated with this state of things at first, as it also prevents you from using ConstructorHelpers. So the way I currently do it is as follows:

Please note that the variable declarations in my header are as follows:



UPROPERTY()
FString ConstRefArmMeshLeftName;
	
// Arm Component
UPROPERTY()
TSubobjectPtr<USkeletalMeshComponent> Arm;

// Skeletal Mesh
UPROPERTY()
USkeletalMesh* ArmMesh;



Cpp files:

My Weapon Constructor:



APWNWeapon::APWNWeapon(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{	
	// Arm Comp
	Arm = PCIP.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("Arm"));
}


Beginplay:



void APWNWeapon::BeginPlay()
{
        Super::BeginPlay();
        SetAssetReferences();
        SpawnAssetReferences();
}


Set Asset references:



void APWNWeapon::SetAssetReferences_Base()
{
	// Arm	
	ConstRefArmMeshLeftName = TEXT("SkeletalMesh'/Game/PWNGame/FPSArms/Mesh/FPSArm_Left.FPSArm_Left'");
}


Spawn Assets:



void APWNWeapon::SpawnAssets_Base()
{
	/***********/
	/*** Arm ***/
	/***********/
	// Arm Mesh
	if (ConstRefArmMeshLeftName.IsEmpty() || ConstRefArmMeshRightName.IsEmpty())
		UPWNGameGlobals::PrintOnscreenString("No ConstRefArmMeshLeftName mesh specified.");
	else
	{
		// Load arm mesh asset from path
		ArmMesh = LoadSkeletalMeshFromPath(IsInLeftHand ? ConstRefArmMeshLeftName : ConstRefArmMeshRightName);
		
		if(ArmMesh)
			Arm->SetSkeletalMesh(ArmMesh);
	}
}




USkeletalMesh* APWNWeapon::LoadSkeletalMeshFromPath(FString path)
{	
	return Cast<USkeletalMesh>(StaticLoadObject(USkeletalMesh::StaticClass(), NULL, *path));
}


Also note that SetAssetReferences_Base() is declared as virtual, which allows child classes to entirely override the reference values, without the need to override the spawn process.
The base class flow will then be executed as expected, but child classes have an “entry point” to set their own references before spawning the actual objects.

Please note that I have no idea whether this is a good approach or not, but it works for me.

Except how do you pass in custom values to say the OnComponentInitialize method? If I need each instantiated actor to have a different value for one of their data members.

As far as I know, you cannot. There are two approaches I’ve seen used by Epic:

  1. Create a version of the class that contains its parameter values as default values, e.g.

Meta Code:



class MyWeapon
{
    float Damage;

    public Weapon()
    {
        Damage = 5.0f;
    }
}

class MyHighDamageWeapon extends MyWeapon
{
    public MyHighDamageWeapon()
    {
        Damage = 20.0f;
    }
}

class MyLowDamageWeapon extends MyWeapon
{
    public MyLowDamageWeapon()
    {
        Damage = 1.0f;
    }
}


  1. Use a custom Initialiser function


class MyWeapon
{
    float Damage;

    public Weapon()
    {
        Damage = 5.0f;
    }

    public void InitCustom(float damageAmount)
    {
        Damage = damageAmount;
    }
}

In some other class:

...
MyHighDamageWeapon = new MyWeapon();
MyHighDamageWeapon.InitCustom(20.0f);
...
MyLowDamageWeapon = new MyWeapon();
MyLowDamageWeapon.InitCustom(1.0f);



The problem with approach 2 is that you have no hooks into the construction process - the workaround is as specified in my first post.