I have a class derived from UUserWidget that attempts to get a specific widget blueprint in the constructor using the following code:
static ConstructorHelpers::FClassFinder<UUserWidget> WidgetBlueprint(TEXT("/Game/Etc/TestBP"));
TestBP
is parented to this class. At this point I would think that there’s some sort of circular dependency problem going on here, but I have another class doing the exact same thing that works 100%. To test, I made two blank/simple classes that followed the same pattern, and neither of them worked. I then made a copy of the working class and its corresponding BP, which also worked–though stripping out everything aside from the offending statement made the load fail again.
After putting things back in and stripping them back out again, I have reached a conclusion for the bare minimum needed to make this work:
.h
public:
MyWidget(const FObjectInitializer& o);
private:
static UTexture2D* someOtherResourceRequiringLoad; // Must be a resource, other datatypes don't work
static TSubclassOf<UUserWidget> bpWidget;
.cpp
UTexture2D* MyWidget::someOtherResourceRequiringLoad; // HAS to be put above bpWidget
TSubclassOf<UUserWidget> MyWidget::bpWidget;
MyWidget::MyWidget(const FObjectInitializer& o) : UUserWidget (o)
{
if (someOtherResourceRequiringLoad == nullptr) { // MUST check for this instead of bpWidget being null
// This entire block must exist, and before the nextone
static const ConstructorHelpers::FObjectFinder<UTexture2D> Texture(TEXT("Texture'/Game/Etc/Tex.Tex'"));
someOtherResourceRequiringLoad = Texture.Object;
static ConstructorHelpers::FClassFinder<UUserWidget> WidgetBlueprint(TEXT("/Game/Etc/TestBP"));
bpWidget = WidgetBlueprint.Class;
}
}
I was originally going to submit this as a normal question, but the solution doesn’t make sense to me. Is there a circular dependency after all? Why does putting something else–specifically a resource, and specifically above the usually load-stopping code–make things work? What is going on in the backend of all this?
I know this may sound stupid, but are you sure it’s not some ShaderCompileWorker.exe instances running in the background ?
Hello sgp,
The workflow of having a class depend on there being a certain child that is inheriting from it seems wrong. I would always expect this to end up as a circular dependency. Have you tried commenting that first line out and seeing if you still get stuck trying to load this?
This is likely something to do with how ConstructorHelpers works and I’ll need to look more into it, but either way it is not a suggested workflow and will only lead the problems in the future.
Hi,
I have indeed tried commenting out the first line and any other lines in sequence, but having the specified lines at their specified spots is the only way to get things un-stuck.
It does seem like a weird hack to get around things, but I would still like to spawn a widget blueprint that only the widget’s class cares about (so the best place to store a reference to it would theoretically be in said class). Do you have any suggestions for how I should better organize my code? Thanks!
Yup I’m sure, I could leave it loading for hours and it’d still be at 71%–and when it works, it goes right past that number without a hitch. The profiler shows that memory is at 0% all the way afterwards too.
I’m trying to spawn several instances of it with CreateWidget(world, bpWidget)
. Without having the actual BP class, there will be no GUI. While I can technically put bpWidget
anywhere, it just seems cleaner to put it inside the class (since it’s not used by anything else). My idea was to have something like a static Make()
function for all relevant widget classes that spawns the widget with its BP class (eg. graphical front) and adds it to the viewport with any other initialization specific to that widget.
If you ever plan to use this widget blueprint, the player is going to automatically have a reference to it when it is added to the viewport either way, as it always has an Owning Player, so there isn’t much reason to even bother having its parent reference it as far as I’m aware.
Can you explain what you’re trying to accomplish with this reference that couldn’t otherwise be done in either the GameInstance, PlayerController, or otherwise?
The main issue I see with this is that, if you do create all of these widgets in code inside of their parent prior to adding them to the viewport, even ignoring that this would already cause issues as the children would also be spawning more children since it’s spawning something that inherits from it, there won’t be anything else that has a reference to it.
Being able to edit these widgets elsewhere is imperative their functionality as widgets, otherwise you may as well just draw a texture on the screen instead as it would be more performant. If you create these widgets with the GameInstance instead, which is a object that is made to be persistent through the game session, you’ll always have a reference to them so if any blueprint needs to change a value inside of these widgets like setting a health value, changing a color, adding to a score, etc., it’ll be able to.
What I meant before when I asked what you’re trying to accomplish is: What are these widgets supposed to be? Do they need to be edited while they exist? Are they static? Is there even a reason to have the C++ class that they inherit from instead of just having them be a base Widget Blueprint?
The widgets are ‘bubbles’ that are generated off an item. So ex. when you go near an item to pick it up in game, the bubble will appear and will have the item’s name and etc. on it. There are some visual changes that can occur dynamically based on the item, so it makes sense to put it in a C++ class.
Basically these widgets are already kept referenced after spawn and can be dynamically edited and etc. They have worked and already accomplished what I wanted for a long time–right now I’m just trying to restructure them a bit so that I can encapsulate BP classes away from where I’m keeping track of them and just leave the spawning and misc visual things to the widgets themselves for a better separation of GUI and game logic. Though I do understand that circular dependency is a problem it would be really nice if I could keep the variable that only they care about hidden away from everything else.
As you mentioned, they are already referenced elsewhere so that they can be edited at runtime, so I’m not sure what you mean to accomplish by making them only referenced in the C++, which isn’t possible since the player must have a reference for them to exist, even if you don’t set an owning player.
As far as them having visual changes that occur dynamically based off the item, is there a reason to have a BP for it if you plan to change their visuals dynamically in C++? Otherwise, anything that changes dynamically could be done in the blueprint itself, just pulling from values already in the item that they’re popping up for. Simple references and displays such as that wouldn’t be much different in performance from a C++ vs Blueprint standpoint. You could always have a Blueprint based off of UUserWidget be the base class for your Bubble widgets instead of a C++ class. All you would need to do is create Child Blueprints from it.
While C++ and Blueprint working together is the best way to work in UE4 most of the time, it’s only one way. Blueprints should reference C++ but not the other way around in most cases. It’s like inheritance with C++ itself. Having a child rely on the parent is a good idea, but having a parent rely on its child for something is an issue. I don’t mean to shut down your entire idea it just doesn’t seem like a safe and useful workflow.
We haven’t heard from you in a while, sgp. Are you still experiencing this issue? If so, are you still able to upload your project? In the meantime, I’ll be marking this as resolved in the meantime.
1 Like
To reply to your question in the accepted answer without unaccepting it: Based on the above discussion I have decided to organize my code in another way such that a circular dependency will not be involved, so while the issue I posted about is still technically there, the underlying issue (eg. circular dependency) has been resolved. Thanks for your detailed help!