There are a few ways that you can get a reference to a blueprint class to instantiate it.
First is through a function call parameter like you’re doing here. The second is configuring a member variable on some data asset or the native part of a blueprint class. Third is marking up a UPROPERTY as Config so that it can be initialized with a path from an ini. Fourth is hard coding the path and using various tools like LoadObject or FConstructorHelpers to get the object you need.
The first two are the best options in equal measure (which you use depends on use case). The ini/config option can also be okay, and is great for configuring default values for option two. The last option is exactly that, an option of last resort because it’s very difficult to iterate on and doesn’t participate in all the reference tools so is very brittle.
Can you explain why you don’t want to be setting this from blueprint? UI tends to be more blueprint heavy than other systems because UMG and the layout designer are best used from there.
ProTip 1: if you make your input TSubclassOf<UKickPlayerButton> the dropdown you get in the editor will only show you classes that are derived from that type.
ProTip 2: you can make your CreateWidget call look like CreateWidget<UKickPlayerButton>(controller, class) and avoid the need to cast and do two IsValid checks.
Yes of course. I thought it would be very convenient to be able to get the derived class from the base class itself.
Precisely to eliminate dependencies and have the code more encapsulated.
It would be great to have something like this: UKickPlayerButton::StaticBlueprintClass();
And get directly what I’m looking for…
So if the class itself is capable of creating instances of its own objects, everything stays within the house. A much cleaner and more orderly code. That’s why I use a static function to create the instances.
Yes, but actually my code has error handling. I mean I get messages when something goes wrong. I’m tired of crashes and the editor closing and not knowing what happened. Then I check all the pointers even though it’s apparently not necessary and send an error message.
Sometimes I have null pointers where I least expect it. (It’s better to prevent than to cure).
This is my real code (in the question simplifies it a bit).
UKickPlayerButton *UKickPlayerButton::Create(APlayerController *PlayerController, TSubclassOf<UUserWidget> BLuePrintClass)
{
if (!IsValid(PlayerController))
{
message::Error("UKickPlayerButton::Create
--> PlayerController is NULL");
return nullptr;
}
UUserWidget* Widget = CreateWidget<UUserWidget>(PlayerController, BLuePrintClass);
if (!IsValid(Widget))
{
message::Error("UKickPlayerButton::Create
--> Widget is NULL");
return nullptr;
}
UKickPlayerButton *PlayerButton =
Cast<UKickPlayerButton>(Widget);
if (!IsValid(PlayerButton))
{
message::Error("UKickPlayerButton::Create
-> PlayerButton is NULL" );
return nullptr;
}
return PlayerButton;
}
Thank you very much for showing me all the possible options. And for the tips. I will try it to see what best suits my needs.
You have made a function that only works if it receives a UKickPlayerButton so you should only allow UserWidgets of that type then you can catch any incompatible type at compile time which is another advantage of TSubclassOf namely type safety.
UKickPlayerButton *UKickPlayerButton::Create(APlayerController *PlayerController, TSubclassOf<UKickPlayerButton> KickPlayerButtonClass)
{
if (!PlayerController)
{
message::Error("UKickPlayerButton::Create
--> PlayerController is NULL");
}
if (!KickPlayerButtonClass)
{
message::Error("UKickPlayerButton::Create
--> KickPlayerButtonClass is NULL");
}
UKickPlayerButton* KickPlayerButton = CreateWidget<UKickPlayerButton>(PlayerController, KickPlayerButtonClass);
return KickPlayerButton;
}