Proper way of loading a BluePrint from GameMode C++

I’m sure I’m not the only one who want to understand this.
I’ve read all questions similar to this on AnswerHUB but either the answers are outdated, bad practices, or just plain confusing.
I really hope someone can shed a light on this:

For example I have this C++ GameMode header (it’s from one of the starter C++ templates):


 #pragma once
 #include "GameFramework/GameMode.h"
 #include "TestProject123GameMode.generated.h"
 
 UCLASS(minimalapi)
 class ATestProjectGameMode : public AGameMode
 {
     GENERATED_BODY()
 
 public:
     ATestProjectGameMode ();
 
     virtual void BeginPlay() override; //I added this to load my C++ class, and it works fine. The problems are with BluePrints.
 };

And its cpp:


 #include "TestProject.h"
 #include "TestProjectGameMode.h"
 #include "TestProjectPlayerController.h"
 #include "TestProjectCharacter.h"
 #include "MapContainer.h"
 #include "EngineUtils.h"
 
 ATestProjectGameMode::ATestProjectGameMode()
 {
     // use our custom PlayerController class
     PlayerControllerClass = ATestProjectPlayerController::StaticClass();
 
     // set default pawn class to our Blueprinted character
     static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/TopDownCPP/Blueprints/TopDownCharacter"));
     if (PlayerPawnBPClass.Class != NULL)
     {
         DefaultPawnClass = PlayerPawnBPClass.Class;
     }
 }
 
 void ATestProjectGameMode::BeginPlay() //I added this to load my C++ class, and it works fine. The problems are with BluePrints.
 {
     UWorld * const World = GetWorld();
     if (World)
     {
         World->SpawnActor<AMapContainer>(AMapContainer::StaticClass());
     }
 }


**I then make this door BluePrint: **

79841-screenshot_59.png

Can someone tell the proper way of making this GameMode C++ spawns this Blueprint when Play is hit?

You must pass the class of your doorBP class to the SpawnActor function.

Here is the code:



void ATestProjectGameMode::BeginPlay()
 {
     UWorld * const World = GetWorld();
     if (World)
     {
         // Your doorBP is an Actor so
         World->SpawnActor<AActor>(doorBPClass);
     }
 }


So you need to find doorBPClass. There are several approaches for this to work.

  1. Hold it as TSubclassOf property in your GameMode and load with the help of ConstructorHelpers::FObjectFinder; just like you did to find the DefaultPawnClass.


class ATestProjectGameMode : public AGameMode
 {
     GENERATED_BODY()
 
 public:

     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "X") TSubclassOf<AActor> doorBPClass;
 };




ATestProjectGameMode::ATestProjectGameMode()
 {
     // use our custom PlayerController class
     PlayerControllerClass = ATestProjectPlayerController::StaticClass();
 
     // set default pawn class to our Blueprinted character
     static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/TopDownCPP/Blueprints/TopDownCharacter"));
     if (PlayerPawnBPClass.Class != NULL)
     {
         DefaultPawnClass = PlayerPawnBPClass.Class;
     }

     static ConstructorHelpers::FClassFinder<AActor> doorBPClassFinder(TEXT("/Game/TopDownCPP/Blueprints/doorBP"));
     if (doorBPClassFinder.Class != nullptr)
     {
         doorBPClass = doorBPClassFinder.Class;
     }
 }


Note: If your doorBp’s parent class was not Actor (for example ACharacter). Then you need this:



// header
TSubclassOf<ACharacter> doorBPClass;
// and
SpawnActor<ACharacter>(doorBPClass);


  1. You can hold your variables, references, classes, assets in Singleton object instead of your GameMode. Look for Rama’s tutorial. This is the most easy way I’ve found.
    A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums
1 Like

Hi cahitburak,
thank you for the prompt answer. It works!

I did try various combinations of this, while experimenting, but it never worked. The reason is I used to put the path like this: /Game/TopDownCPP/Blueprints/doorBP**.doorBP**
Because this is what I get when I click on ‘Copy Reference’. So the culprit was the extra .doorBP…

Thank you very much.

Bonus question: I frequently read that hardcoding the paths is a wrong practice, because when you package (cook?) your build, the BPs are left behind. Do you know if this applies to this case, and if so, how can it be solved?

As far as I know this issue occurs when you reference assets with hard pointer instead of TAssetPtr. In your situtation this is out of the box.

Hardcoding the paths is a wrong practice if for example you are using it in an Actor’s constructor, when you spawn instance of the Actor; it may be getting the asset from hardoceded path for every instance but I am not sure is this correct or not and don’t know it’s performance costs. If this is correct then using in GameMode is not bad because it has only one instance not many.

Perfect then. Thank you again cahitburak!

No problem friend. Again, I am not sure from things I said about hardcoded paths. It obviously doesn’t cause GC problems but the performance; I don’t know. It is better to somebody confirm me about this.

Here is a last quick tip for make things clear for any other users:
We cannot use StaticConstructorHelpers::FObjectFinder outside of a constructor (the name obviously). This is why we hold it as TSubclassOf<AActor> property in the header.

I’ve noticed something when I read your post again. I don’t know extra “.doorBP” in path was the culprit or not but I’m sure “Copy Reference” must work perfectly when it is given to the asset path. I am assuming you did something else wrong. The long path which “Copy Reference” gives us was the requirement of old asset paths which also works fine now (guessing since version 4.4).

You should be really careful with TSubclassOf<T>. It can cause your blueprint to be loaded at unexpected times. TAssetSubclassOf<T> is much safer in that regard, but it will have to be loaded when you actually start using it.

I don’t know what to tell you mate, with the .doorBP it doesn’t work (CDO constructor error), without it instead it does…

Well, it’s working properly, for the moment. I’ll try anyway to switch it to TAssetSubclassOf<T> just to be safe. Thanks for the tip mrooney!