I had been selecting a blueprint using:
.h
UPROPERTY(EditAnywhere, Category = "Actors To Be Spawned")
TSubclassOf<AActor> Wall_X;
and then spawning it with:
.cpp
world->SpawnActor<AActor>(Wall_X, spawnLocation, spawnRotation);
Which was working well.
But I’ve been having a lot of trouble with UE5 losing track of the property settings and needing a recompile to get them back, so I thought I would hard code the blueprint references in whilst I experimented… and entered a rabbit hole. I ended up with this:
static ConstructorHelpers::FObjectFinder<UBlueprint> Wall_X(TEXT("Blueprint'/Game/LevelPrototyping/Prototypes/BP_Wall_A.BP_Wall_A'"));
UWorld* world = GetWorld();
FVector spawnLocation = FVector::ZeroVector;
FRotator spawnRotation = FRotator::ZeroRotator;
world->SpawnActor<UBlueprint>(Wall_X, spawnLocation, spawnRotation);
Which causes an error because SpawnActor does not match the argument list of (ConstructorHelpers::FObjectFinder, FVector, FRotator)
I don’t understand what is going on with the type returned from the finder. I don’t understand how I can move between TSubclassOf and an object that I can find with FObjectFinder.
You can get the default actor for the blueprint with:
bpActor=(AActor*)bp->GeneratedClass->GetDefaultObject();
I Haven’t tried spawning with that, but it should be ok - it can be used for pretty much everything else.
Or if you have the BP Editor open you can access the one generated by that:
bpActor=(AActor*)bp->SimpleConstructionScript->GetComponentEditorActorInstance();
SimpleConstructionScript is very useful - worth reading up on…
Sorry, I’m probably being a bit slow… I have a simple blueprint that just contains a static mesh (it may contain more sophisticated things in the future) and I have a non-blueprinted spawner cpp class in my level that controls the level spawning. I’m trying to find the blueprint and spawn it directly from the spawner class (no blueprint).
I can’t see how I can use:
bpActor=(AActor*)bp->GeneratedClass->GetDefaultObject();
to achieve this within the context of
static ConstructorHelpers::FObjectFinder<UBlueprint> Wall_X(TEXT("Blueprint'/Game/LevelPrototyping/Prototypes/BP_Wall_A.BP_Wall_A'"));
UWorld* world = GetWorld();
FVector spawnLocation = FVector::ZeroVector;
FRotator spawnRotation = FRotator::ZeroRotator;
world->SpawnActor<UBlueprint>(Wall_X, spawnLocation, spawnRotation);
try:
AActor* bpActor=(AActor*)Wall_X->GeneratedClass->GetDefaultObject();
world->SpawnActor<AActor>(bpActor,spawnLocation,spawnRotation)
If that doesn’t work you could try:
world->SpawnActor<AActor>(Wall_X->GeneratedClass,spawnLocation,spawnRotation);
Unfortunately neither:
static ConstructorHelpers::FObjectFinder<UBlueprint> Wall_X(TEXT("Blueprint'/Game/LevelPrototyping/Prototypes/BP_Wall_A.BP_Wall_A'"));
UWorld* world = GetWorld();
FVector spawnLocation = FVector::ZeroVector;
FRotator spawnRotation = FRotator::ZeroRotator;
AActor* bpActor = (AActor*)Wall_X->GeneratedClass->GetDefaultObject();
world->SpawnActor<AActor>(bpActor, spawnLocation, spawnRotation);
Wall_X->GeneratedClass->GetDefaultObject()
and world->SpawnActor
and
static ConstructorHelpers::FObjectFinder<UBlueprint> Wall_X(TEXT("Blueprint'/Game/LevelPrototyping/Prototypes/BP_Wall_A.BP_Wall_A'"));
UWorld* world = GetWorld();
FVector spawnLocation = FVector::ZeroVector;
FRotator spawnRotation = FRotator::ZeroRotator;
world->SpawnActor<AActor>(Wall_X->GeneratedClass, spawnLocation, spawnRotation);
Wall_X->GeneratedClass
Here is some code that works for me:
UPackage* package=FindPackage(NULL,*(asset.PackageName.ToString()));
if(package) {
bp=FindObject<UBlueprint>(package,*(asset.AssetName.ToString()),true);
if(bp) {
FWorldContext* world=GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport);
UWorld* World=world->World();
FActorSpawnParameters SpawnParams;
SpawnParams.Owner=nullptr;
SpawnParams.SpawnCollisionHandlingOverride=ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnParams.ObjectFlags=RF_Transient;
SpawnParams.bTemporaryEditorActor=true;
SpawnParams.bHideFromSceneOutliner=true;
AActor* newActor=World->SpawnActor<AActor>(bp->GeneratedClass,FVector(0,0,0),FRotator(0,0,0),SpawnParams);
You’ll need the Spawn Params by the looks (and change to not being a temporary actor) - also the Package code to load the package and then do the FindObject in there…
Thanks for that, I really appreciate your time. I’ll check it out tomorrow and get back.
1 Like
I tried to get that working in my standalone cpp class, but I could not figure out how to set asset.PackageName and asset.AssetName correctly. After a great deal of google “coding” I now have a solution:
FString bpResource = "/Game/Blueprints/BP_Wall_A.BP_Wall_A";
UBlueprint* GeneratedBP = Cast<UBlueprint>(StaticLoadObject(UObject::StaticClass(), NULL, *bpResource));
UWorld* World = GetWorld();
World->SpawnActor<AActor>(GeneratedBP->GeneratedClass, FVector(0, 0, 0), FRotator(0, 0, 0));
For anyone else finding this post and needing clarification:
“/Game” is the position of your “Content” folder and the name of the blueprint is the name as it appears in your blueprint (or whatever you called it) folder, with a . and then the name again.
I need to think a bit about what is actually happening in the StaticLoadObject line.
Perhaps it is a UE5 thing, but I found I did not need to specify the SpawnParams.
Hey this works for me in the editor but in packaged versions, it can’t find the Blueprint so the generated class is null
Oh. I have not tested it in a packaged build. Well that’s disappointing. I’ll try some more experiments and post here with any successes.
Well that was all rather painful. I could not see a way to keep the resources addressable by name in the pak files, but I did eventually get a spawning mechanism working:
Create a new cpp class that inherits from actor, I called mine Spawner (don’t forget to replace SPAWNBP_2_API with your project).
header:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Spawner.generated.h"
UCLASS()
class SPAWNBP_2_API ASpawner : public AActor
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = "Spawn Object")
TSubclassOf<AActor> SpawnBP;
public:
ASpawner();
protected:
virtual void BeginPlay() override;
private:
void SpawnNow();
};
cpp
#include "Spawner.h"
ASpawner::ASpawner()
{
PrimaryActorTick.bCanEverTick = false;
}
void ASpawner::BeginPlay()
{
Super::BeginPlay();
SpawnNow();
}
void ASpawner::SpawnNow()
{
if (SpawnBP)
{
UWorld* world = GetWorld();
world->SpawnActor<AActor>(SpawnBP, FVector(0, 0, 150), FRotator::ZeroRotator);
}
}
You can then add the class to your level and in the details set the blueprint to spawn. This is really ugly for me as I have a large number of blueprints to add and I found UE often forgot that I had added them in a previous save and left them blank, hopefully this is just me.
I needed to delete my Binaries and Intermediate directory, and right click on my uproject to “Generate visual studio project files”. Under platforms I cooked content and then packaged. I find UE5 quite unstable and I often have to do this annoying loop, I’m not sure if this is just UE5 or my installation, but the blueprint was not appearing in the package build until I did this.
I should add that I’m a complete UE noob, so I may well not be doing this the correct way - I would be happy to hear a better way to use some procedural cpp to spawn blueprint “tiles” into a level.