Download

Our solution to spawning blueprints from c++

Hello,

First of i would like to warn people i started using unreal since 2 months and never programmed c++ before so if i do something stupid i would appreciate some feedback :smiley:

So in our team we had some trouble figuring out a way to actually spawn a blueprint actor (or anything else for that matter) from c++ and then to do it in a efficient way.

An example of getting a blueprint class in code:


ConstructorHelpers::FClassFinder<AGun> BPClass(TEXT("/Game/Path/To/My/Gun/Bluepr"));

And then spawning it


GunInstance = world->SpawnActor<AGun>(BPClass.Class, spawnParams);

But the only way we found to get the blueprint classes in code was with the ContructorHelpers::FClassFinder which only work in the Constructor (surprise :p).
So i found a few examples which add a TSubclassOf BPClass; variable to their class so they can fill it in the constructor and use the class variable when something needs to be spawned outside of the constructor (bullets for example).

But in our case (to name 1 example where this gets ugly) is that we needed to have spawnpoints that randomly choice between all of our monsters. Which would have mend adding a variable of every monster blueprint in our game.

So because we didn’t feel like adding variables for every blueprint to all the classes that might need it (which would be quit some) we decided to use a single class (In our case a singleton) to handle all blueprint loading.

Below the .h file


 
#pragma once
/**
 * 
 */
class MOONSHINEWORKS_API BlueprintLoader
{

private:

	//static BlueprintLoader* Instance;
	BlueprintLoader();
	~BlueprintLoader();
	BlueprintLoader(BlueprintLoader const&); // Don't Implement
	void operator=(BlueprintLoader const&); // Don't implement

	TMap<FName, TSubclassOf<class UObject>> Classes;

public:

	//void Initialize(const class FPostConstructInitializeProperties& PCIP);
	static BlueprintLoader& Get(){
		static BlueprintLoader	Instance;
		return Instance;
	};

	void AddBP(FName Name, TCHAR* Path);
	TSubclassOf<class UObject> GetBP(FName Name);

};


And here the .cpp file



#include "MOOnshineWorks.h"
#include "BlueprintLoader.h"

BlueprintLoader::BlueprintLoader()
{
	AddBP(FName("BP_1"), TEXT("/Game/Path/To/My/Gun/Bluepr"));
	AddBP(FName("BP_2"), TEXT("/Game/Path/To/My/Gun/Bluepr2"));
 	AddBP(FName("AndMyLastBluePrint"), TEXT("/Game/Path/To/My/Last/BlBlueprintuepr2"));   
}

BlueprintLoader::~BlueprintLoader()
{

}

void BlueprintLoader::AddBP(FName Name, TCHAR* Path)
{
	ConstructorHelpers::FClassFinder<UObject> BP(Path);
	Classes.Add(Name, BP.Class);
}

TSubclassOf<class UObject> BlueprintLoader::GetBP(FName Name)
{
	TSubclassOf<class UObject> Result = (*Classes.Find(Name));

	if (Result){
		return Result;
	}
	else{
		return nullptr;
	}
}


Then when we need to spawn a Blueprint


  GunInstance = GetWorld()->SpawnActor<AGun>(TSubclassOf<AGun>(*(BlueprintLoader::Get().GetBP(FName("BP_2")))), spawnParams); 

We currently add all classes needed in the constructor but somewhere down the line we will do something like parsing the content tree to include all blueprints (or something similar).
Or we could add a way to add Blueprint classes to the Classes array directly and add the blueprints in the classes we need them in.

Any thoughts on this way of spawning blueprints? Feedback in general is also greatly appreciated as always :D.
(I also quietly hope i saved someone some trouble by posting this :3)

Actually, I find that spawning blueprint based actors using paths is something very rare.

Usually that’s only needed to set a sensible default for TSubclassOf variables. Basically, what you’ve done exposes one quirk of your use of UE4: you use a lot of path based blueprint spawning instead of using the reflection system.

For example, you have a map, and there’s a crate on the level. You want the crate to contain a gun(that’s a bluprint), and when the character opens the crate, the gun should spawn in his hands/inventory. You could do that by spawning the bluprint from a path in code, but that solution is pretty rigid. The other solution is to make an array of TSubclassOf<> variables that’s a UProperty of the ACrate actor and make it editable in the editor. Now the level designer can set the variable to any type that he wants and in code you can iterate over the array, and just spawn the actors from there.

tl;dr
Paths for blueprints to be spawned should commonly flow from things set in editor.

Hard-coded asset paths make me cringe, what happens when one of your artists or level designers moves an asset to a different directory? In addition to what @Thraden has mentioned you can use FStringAssetReference and TAssetPtr to delay-load assets, the linked page also covers the asset registry and object libraries which provide a way to discover assets at runtime rather than referencing them at design time.

I have some bad news for you – this will likely break once you create a cooked build.

Cooked builds gather only the assets you “need”. The cooker determines that you need an asset by checking if there is a reference to it somewhere. References can only be gathered from UProperties on UObjects – the cooker has no way to introspect native variables.

Currently, your blueprint spawner is entirely native and will therefore not count as a reference to your blueprints. If they are not loaded anywhere else in your project, this means they will not be included in the cooked build and you will end up failing to spawn them.

Automatically gathering references (i.e.: “parsing the content tree to include all blueprints”) is something I’ve been experimenting with, but I’m finding out it’s not for the faint of heart. Epic tends to advise maintaining a data asset (like UDataAsset) and manually adding the blueprints you want on it. A quick and dirty example:


UCLASS()
class UGunList : public UDataAsset
{
	GENERATED_UCLASS_BODY()

	UPROPERTY( Category=Guns, EditAnywhere )
	TArray<TSubclassOf<AGun> > GunTypes;
};

Then you can create a new asset of this type (in the asset browser, Miscellaneous, then Data Asset, all data asset subtypes will be listed) and add your blueprints in. One upside of this is you can also use the data asset as a list, for instance on a random spawner. I created a random spawner component for our project that takes such an ItemList data asset, and I can control what kind of items will spawn there by maintaining different pickup lists. And as a freebie, it also creates a reference to the item types. :slight_smile:

Whether using such a spawner component/actor or not, you still need to have a reference to the UGunList somewhere or you’ll have the same problem. If you just want a single central location to store it in, you could add a UGunList* UProperty in your game mode or in a game singleton (set a GameSingletonClassName in DefaultEngine.ini, or through the project settings).

Many thanks too everyone for the quick replies and feedback :smiley:
I guess it’s back to the drawing board for me.

@Tharden
We tried to come up with a solution like this (adding the reference in code) but the spawn actor needed to be able to pick between sets of enemies (5Big or 2Big or 1big + 2small, 10*small) and i couldn’t find a way to make this possible (currently we parse a string like “5:Big;2:Big;1:Big+2:small;10:Small” (it’s not pretty to look at or work with thou). Reading this thread i guess it would be possible to define enemy spawn combo’s in a list and use a array of those lists in the spawn actor.

Take a look at this template function. Just add it in a header, and if you use pre-compiled headers add it in to the PCH.




//

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = TemplateExample)
     UClass* ExampleBP;
     FVector GenSpawnLoc, GenSpawnRot;

template <typename SpawnWorldBP>
FORCEINLINE SpawnWorldBP* SpawnBP(
    UWorld* TheWorld,
    UClass* TheBP,
    const FVector& Loc,
    const FRotator& Rot,
    const bool bNoCollisionFail = true,
    AActor* Owner = NULL,
    APawn* Instigator = NULL
    ){
    if (!TheWorld) return NULL;
    if (!TheBP) return NULL;
    //~~~~~~~~~~~

    FActorSpawnParameters SpawnInfo;
    SpawnInfo.bNoCollisionFail = bNoCollisionFail;
    SpawnInfo.Owner = Owner;
    SpawnInfo.Instigator = Instigator;
    SpawnInfo.bDeferConstruction = false;

    return TheWorld->SpawnActor<SpawnWorldBP>(TheBP, Loc, Rot, SpawnInfo);
}

Useage


AActor* SpawnedActor = SpawnBP<AActor>(GetWorld(), ExampleBP, GenSpawnLoc, GenSpawnRot);

If you use it thank Rama!

Thanks for posting this - I was having this issue and I assumed this was a bug but you’ve set me in the right direction

Hey see you got some good replys allready.
I tough i drop a method for you maybe you find it helpfull. :slight_smile:


/**
@name: SpawnBlueprintFromPath<T>(UWorld* MyWorld, const FString PathToBlueprint, const FVector SpawnLocation, FRotator SpawnRotation, FActorSpawnParameters SpawnInfo)
@param: Pointer to loaded world.
@param: Path to the Blueprint we want to spawn.
@param: Spawn Loaction for the new Blueprint actor.
@param: Spawn Rotation for the new Blueprint actor.
@param: FActorSpawnParameters for the new Blueprint actor.
@Description: Spawn a Blueprint from a refrence path in PathToBlueprint.
*/
template <typename T>
static FORCEINLINE T* SpawnBlueprintFromPath(UWorld* MyWorld, const FString PathToBlueprint, const FVector SpawnLocation,
											FRotator SpawnRotation, FActorSpawnParameters SpawnInfo)
{
	FStringAssetReference ItemToReference(PathToBlueprint);
	UObject* ItemObject = ItemToReference.ResolveObject();
	if ((ItemObject) && (MyWorld))
	{
		UBlueprint* GeneratedBP = Cast<UBlueprint>(ItemObject);
		return MyWorld->SpawnActor<T>(GeneratedBP->GeneratedClass, SpawnLocation, SpawnRotation, SpawnInfo);
	}
	else {
		return NULL;
	}
}

This will spawn a actor for you using a path.
It resolves it to a String Asset Refrence, and spawns the actor for you.

WCode

Yeah. I wish this could be stickied somewhere in clear sight – I cringe every time I see someone using asset paths outside of FObjectFinder/FClassFinders. Using StaticLoadObject as a magical loading solution is bandied all over the place but that’s a big trap to anyone unaware of the cooking process.

My two cents…

We want to have spawn points in our level, that randomly pick from a category. In order to keep the definition in the level light-weight we use the string of the loot
like…


Blueprint'/Game/Blueprints/DevelopmentObjects/TestActor.TestActor'

So we have a LootSpawinPointBP, a LootChoiceBP blueprints.

The LootChoiceBP is a string reference to a blueprint, and a weight. The weight is a relative random selection weight.
There is also a second bleuprint refence for ammo so a spawned gun can have some ammo next to it.

We then make blueprints from the base blueprints for a specific spawn point category.
For example we might have a UpstairsClosetLootSpawnBP that is a LootSpawinPointBP with a bunch of LootChoiceBP like Rifle308LootBP, BackpackRedLargeBP and such.
On startup the loot spawn point randomly chooses on of the LootChoiceBP with the higher weight items being more likely to get chosen.
(You can also have a choice NoChoiceBP with both blueprint ref strings empty too.)

So this requires that we can spawn from a string. Thus the code that counts…



AActor * UtdlBlueprintHelpers::SpawnActorFromReferenceString(AActor * sourceActor, FTransform trans, FString blueprintRefString, FName nameToAssign)
{
	//UE_LOG(TDLLog, Log, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString %s"), *blueprintRefString);

	if (blueprintRefString.Len() == 0)
	{
		return NULL;
	}

	AActor * a = NULL;

	FStringAssetReference itemRef = blueprintRefString;

	if (itemRef.TryLoad() != NULL)
	{
		UObject* itemObj = itemRef.ResolveObject();
		if (itemObj == NULL)
		{
			UE_LOG(TDLLog, Log, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString invalid blueprint reference (resolve falied): %s"), *itemRef.AssetLongPathname);
			return NULL;
		}
		UBlueprint* gen = Cast<UBlueprint>(itemObj);
		if (gen == NULL)
		{
			UE_LOG(TDLLog, Log, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString invalid blueprint reference (cast failed): %s"), *itemRef.AssetLongPathname);
			return NULL;
		}

		FActorSpawnParameters SpawnInfo;
		SpawnInfo.bNoFail = true;
		SpawnInfo.bNoCollisionFail = true;
		SpawnInfo.bRemoteOwned = false;
		if (nameToAssign.IsValid() && nameToAssign.ToString().Len() > 0)
		{
			SpawnInfo.Name = nameToAssign;
		}

		a = sourceActor->GetWorld()->SpawnActor<AActor>(gen->GeneratedClass, SpawnInfo);
		if (a == NULL)
		{
			UE_LOG(TDLLog, Log, TEXT("UtdlBlueprintHelpers::SpawnActorFromReferenceString invalid blueprint reference (spawn failed): %s"), *itemRef.AssetLongPathname);
			return NULL;
		}

		a->SetActorTransform(trans);
	}

	return a;
}


Question on this, what exactly do we use for the UClass* TheBP argument? I can’t exactly use the name of the BP.

For my project, i have a base C++ class, and a blueprint parented to it. I want to spawn the Blueprint from C++

Nice solution, but you must call ItemToReference.TryLoad(); before UObject* ItemObject = ItemToReference.ResolveObject();

Hey guys,

I know this thread is quite old, but is there an updated solution to this problem for UE 4.24? I tried all of the solutions above but always get a null pointer from SpawnActor().

Hi,

Here’s how I did it in a game I’m writing lately:

  • Declare a UPROPERTY() in the class of the blueprint actor you want to spawn, I’m spawning an ACharacter derived actor so I put ACharacter in the template argument, put the name of the class you’re deriving from in there.

1.png

  • Now you can see the property in your blueprint and you can choose the blueprint you want to spawn

2.png

  • Now you can use the property you declared to call SpawnActor() wherever you want in your code (my dumb comments are a bonus ;d)

Finally it’s working and even with only 3 lines of code! Thanks a lot!

4.24 it Work. Good Luck

UObject* SpawnActor = Cast<UObject>(StaticLoadObject(UObject::StaticClass(), NULL, TEXT("/Game/DEXIED/Foliage/Tree/BP_TreeDestroyed_Style_1.BP_TreeDestroyed_Style_1")));

UBlueprint* GeneratedBP = Cast<UBlueprint>(SpawnActor);
if (!SpawnActor)
{
GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("CANT FIND OBJECT TO SPAWN")));
return;
}

UClass* SpawnClass = SpawnActor->StaticClass();
if (SpawnClass == NULL)
{
GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("CLASS == NULL")));
return;
}

UWorld* World = GetWorld();
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
World->SpawnActor<AActor>(GeneratedBP->GeneratedClass, GetActorLocation(), GetActorRotation(), SpawnParams);

It is incredibly poor design to hardcode references to blueprint assets.

The correct way to spawn actors and get a reference to a Blueprint was answered in Post #14