The code below works but throws CDO errors. Even though none of those values are null, not even when stepping through it when starting the UE4-editor through VS2017 and even though it works, I still get those errors. Also without the function (just calling the FObjectFinder for each Blueprint with it’s own code) it does NOT throw the CDO errors. It must be related to this function somehow but I have no idea why:
APlayerEye_Controller::APlayerEye_Controller()
{
// Note that out-commenting these 2 lines below removes the CDO Errors (but then I have no GUI).
GUI_PlayerEye_Class = GetClassFromBlueprint(TEXT("/Game/UI/BP_PlayerEye_GUI"));
GUI_Debug_Class = GetClassFromBlueprint(TEXT("/Game/UI/BP_Debug_GUI"));
}
UClass* APlayerEye_Controller::GetClassFromBlueprint(FString path)
{
ConstructorHelpers::FObjectFinder<UBlueprint> Temp = nullptr;
Temp = ConstructorHelpers::FObjectFinder<UBlueprint>(*path);
#if WITH_EDITOR
if (Temp.Object == nullptr) { UE_LOG(LogTemp, Error, TEXT("%s"), *path) }
#endif
if (Temp.Succeeded())
{
return (UClass*)Temp.Object->GeneratedClass;
}
else
{
LOG_ERROR_SAFELY("Failed to get UClass* from Blueprint.")
return nullptr;
}
}
When I set the class variables through a blueprint, not only do I need to create a blueprint just for setting a few variables, there is also a bug going around I heard that causes blueprints to reset those values to their defaults when hot-reloading. And the game auto-hot-reloads after adding C++ files and such so mmm… Not sure what to do here.
I also noticed that replacing the paths with invalid ones, doubles the amount of CDO errors (from 2 → 4).
And this function works in another project but not in this one. Odd.
Someone gave me a cleaner version of this function and if I don’t put it in a function but use the code directly it works. I can’t explain it at all but at least I have a workaround (so don’t use a function for this).
FObjectFinder is a tool that works right after you construct it and you not giving it any path to search for here, so it will definitely throw you a error. if anything you could do something like this:
Also CDO means Class Default Object, engine creates those on start up to keep default values of the class variables, so any errors in costructor there will be thrown on game or editor start up and it’s also reason why you should not have any gameplay code in constructor, as constructor is exectured before engine is fully initiated.
That code seems to be extremely long for what it’s doing. Also, the workaround is even more complicated. If you want, I can try to guide you through read all of that, but it’s pretty much putting bandage on top of an old bandage in hopes it fixes the bandage, instead of the problem itself.
So, I suggest you replace your entire code like this:
// Take the paths out of the function, it's easier to replace later on ;)
// Make sure those paths are correct!
#define GUI_PLAYEREYE "/Game/UI/BP_PlayerEye_GUI"
#define GUI_DEBUG "/Game/UI/BP_Debug_GUI"
APlayerEye_Controller::APlayerEye_Controller()
{
static ConstructorHelpers::FClassFinder<UBlueprint> GUI_PlayerEye_BP(TEXT(GUI_PLAYEREYE));
GUI_PlayerEye_Class = GUI_PlayerEye_BP.Class;
static ConstructorHelpers::FClassFinder<UBlueprint> GUI_Debug_BP(TEXT(GUI_DEBUG));
GUI_Debug_Class = GUI_Debug_BP.Class;
}
And get rid of GetClassFromBlueprint(). Much simpler this way.
You can always remove the #define’s and put the text in place if you prefer too, I just think it’s easier to read that way.
Also, I’m pretty sure you should replace UBlueprint with UUserWidget, but since I"m not sure of your setup, I left as is.
About why the error happens… The ConstructorHelpers::FClassFinder() is a static function that is supposed to run only in the constructor.
Also, there’s no need to log that specific action (that’s what the GetClassFromBlueprint() is doing, right?) - Error in those are instantly listed in the constructor, like in your screenshot.
If you still have those errors, with “null” in them, make sure the path is correct and the blueprints are valid.
About why the error happens… The
ConstructorHelpers::FClassFinder() is
a static function that is supposed to
run only in the constructor.
Yes. I assumed that running FClassFinder from a function that is directly called from the constructor would count as the same. After all, I can do this with the FObjectFinderOptional() so it made sense to me that the FClassFinder could do this as well (but apparently not). After all, it works. It’s just that it throws the CDO errors even when there is no ‘real error’. Maybe it would bug out the CDO for the class but in the case of pure C++ classes and not having a blueprint ever inherit from it, should not cause any problems. Maybe if you explicitly try to retrieve the CDO values through C++ but that is rare. Therefor I still believe that the CDO errors for my particular case should not have been thrown. At the most they should have been warnings.
I read: Creating Objects in Unreal Engine | Unreal Engine 5.3 Documentation
What I guess that happens is:
FClassFinder must be inside the constructor, but it will still work when placed outside of it.
The CDO errors from the FClassFinder are queued in some list somewhere and all thrown together as 1 messagebox. Therefor it does not show me the error while stepping through it.
The CDO errors are thrown even if the FClassFinder was successful in retrieving the class from outside of the constructor. Maybe there is some unknown side-effect somewhere but I still suspect a minor UE4 bug here.
Oh I see so I don’t even need all those nullptr checks. Well yes thanks that would simplify it even more :). That no longer requires a function indeed.
offtopic: Oh haha apparently if you have exactly 0 characters left on a comment, the AnswerHub bugs and won’t let me post it. I have to have at least 1 character left.
Yes it’s a UUserWidget.
I’m using this path now: “/Game/UI/BP_PlayerEye_GUI.BP_PlayerEye_GUI_C” because apparently using the generated class (instead of the actualy blueprint) prevents the problem where it cannot find it for a packaged game that does not convert the blueprints to C++. Therefor this path is safer but I do not know if it has any side-effects
Your shorter code basically solved the problem even better. I’m still somewhat trying to understand the whole CDO problem and I think I mostly do now. It’s a lot more complex that it looks. Thanks for the replies you two.