Best practice for spawning a blueprint in a pure C++ object?

I’m kind of curious what the best practice here is. I’ve got an AInfo object called ADiceManager, that receives input from the player controller, and when the player presses the Space Bar action, it should spawn a handful of dice and toss them out onto the table.

I’ve got a handful of blueprints for various types of dice (D4/D6/D8, etc).

Currently, I’m doing something like this:



ConstructorHelpers::FObjectFinder<UBlueprint> D6BP(TEXT("Blueprint'/Game/Blueprints/Dice/Dice_D6.Dice_D6'"));

To get a reference to the blueprint that I can then call SpawnActor on the world. I’m wondering though, is there a better practice here? Obviously having hard coded paths in C++ code is not ideal. I could move the paths to some sort of global configuration class “string D6Path = <blahblah>” and then access that from objects that would use it… I could even push that out further to a configuration file to save having to recompile code on change…

…but all of these just feel dirty and incorrect. In BluePrint I could just call SpawnActorFromClass and just pick the object from the class list… in Unity3D I can do similar and just drag and drop the prefab into the Inspector for this script.

Is this something I’m going to just have to suck up and deal with?

UPROPERTY(EditDefaultsOnly, Category = “Spawning”) // or whatever category, doesn’t matter
TSubclassOf<YourBlueprint’sBaseClassHere> DiceClass;

Then go to whichever blueprint inherits from the class with the above property (i.e. the gamemode), and you’ll find a drop-down of all classes (both C++ and BP) that inherit from the class you specify between the < > symbols. Throw that property into the SpawnActor function in place of the UClass* (The TSubclassOf will implicitly convert for you).

In ADiceManager, you would have something like:


UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<ABaseDiceClass> DiceClass;

Then in a Blueprint-derived class, say ADiceManagerBP, you would be able to select the class in question:

Capture.PNG

Here’s how you might organize it to support multiple types of dice:


// Somewhere in the header

UENUM(BlueprintType)
enum class EDiceType : uint8
{
    DT_D6 /* = 6 */ UMETA(DisplayName = "D6"),
    DT_D20 /* = 20 */ UMETA(DisplayName = "D20"),
    DT_D100 /* = 100 */ UMETA(DisplayName = "D100"),
};

// Somewhere in ADiceManager

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<ABaseDiceClass> DiceClassD6;

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<ABaseDiceClass> DiceClassD20;

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<ABaseDiceClass> DiceClassD100;

// Somewhere in cpp file

bool RollDie(EDiceType type)
{
    TSubclassOf<ABaseDiceClass> classType = nullptr;
    
    switch (type)
    {
    case DT_D6:
        classType = DiceClassD6;
        break;
    case DT_D20:
        classType = DiceClassD20;
        break;
    case DT_D100:
        classType = DiceClassD100;
        break;
    }
    
    if (classType != nullptr)
    {
        GetWorld()->SpawnActor<ABaseDiceClass>(classType);
        return true;
    }
    
    return false;
}