Download

How do I access a widget from my game instance?

Just getting started with C++ and doing some tutorials so bear with me…

I’m trying to create a scoring system for a simple game. I created a GameInstance to keep a score value and to hold the functions GetScore and IncrementScore.

I also have a UserWidget class called ScoreWidget that has a TextBlock that displays the score. Then in my BP_GameModeBase I’m creating a BP_ScoreWidget that is a child of ScoreWidget.

So in order to update the score on screen, I want the GameInstance.IncrementScore to tell the ScoreWidget to update its text field (has a function call that accesses the GameInstance).

My problem/question is how do I access the ScoreWidget from the GameInstance?

And as a bonus question - is this a sensible way of doing things?

Thanks!

Somewhere, somehow, you have to keep a reference to the widget you created.



// .h
UUserWidget* MyScoreWidget;

// .cpp
MyScoreWidget = CreateWidget<UUserWidget>(...);


I always suggest people to use the HUD class for this. That’s exactly what it was made for. Instead of creating the widget in the GameMode, you can create it in the HUD class. You can even create a more generalized function that creates widgets, and stores them in an array, or a dictionary. Anyways, so the HUD class has a reference to the score widget, now if you want to access it, you can do it like this:



// Get the first player controller's HUD. In a networking environment, only available client side.
AMyHUD* MyHUD = GetWorld()->GetFirstPlayerController()->GetHUD();

// Check if the score widget exists. If so, update the score text.
if (MyHUD->MyScoreWidget)
{
     MyHUD->MyScoreWidget->UpdateScore(...);
}


Great - thanks for the reply! That makes sense to to me on the C++ side of it, but how do I get the blueprint version of my ScoreWidget (BP_ScoreWidget) to also update whenever the Score is updated? I figured it doesn’t make sense to have an onTick constantly running to check the score, so I’m not sure how to call the BP to tell it to update its TextBlock on command.

Had a think about this more… would something like this work (conceptually?)

When one of the enemy pawn dies (BP_EnemyPawn), it has an override function to handle its death (HandleDestruction). In this function I’ll put an event dispatcher to notify the BP_ScoreWidget to update its score. The BP_ScoreWidget binds to the dispatcher and will get the notification and will call its own function to update its score textBlock.

So the Parent classes (C++) aren’t really talking to the Blueprints directly… the blueprints tell each other what’s going on and they ask the parent classes for specific data when they need it.

I think you are misunderstanding how C++ and Blueprints work. The code I showed you earlier will update the blueprint version of you widget, given that the class you put in CreateWidget is the blueprint version. There is no such thing as c++ and blueprint version though. You should think about them the same way. A blueprint is the same as a C++ class, but instead of writing, you are connecting nodes to make the code.



// .h - this will allow you to set a widget in the editor
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TSubclassOf<UUserWidget> ScoreWidgetClass;

// .cpp - create the widget from the given class
CreateWidget<UUserWidget>(GetWorld()->GetFirstPlayerController(), ScoreWidgetClass);


You’ll have to create a C++ class from UUserWidget as well, so that you can call the UpdateScore function. You don’t necessarily have to code the functionality though. If you want to handle that in blueprints (which I would), you can mark the function as BlueprintImplementableEvent. This way, you can call the function from C++, but have the actual code implemented in blueprints.



UFUNCTION(BlueprintImplementableEvent, Category = "Events")
void UpdateScore(float NewScore);


Hey STRIFE - Thanks so much for taking your time to help me out. Really appreciate it. I owe you a drink!

It took a lot of trial and error but I was finally able to get the scoring system working. Here’s what I did and some questions I still have:

  1. In my HUD I created the ScoreWidget as you suggested. I had to dig around from some more assistance though because I couldn’t figure out how to get ScoreWidgetClass. My question is - why do I have to do all this FClassFinder stuff… why can’t I just type in something like “BP_ScoreWidget.class?” In other words, I created the class and I know what it’s called, so why do I have to ‘find’ it? If you look a the HUD code below- is that the right way to get the class?

  2. I created a C++ class for the ScoreWidget (parent class of BP_ScoreWidget) but it has no added code in it. You said I needed it to call the UpdateScore function but it I’m not totally clear on if that’s the case? What I did was in the BP_ScoreWidget I have a textBlock that I did a bind on (see attached image) and that field gets the score from my GameInstance. So in reality is the ScoreWidget CPP doing anything aside from acting as a shell parent class for BP_ScoreWidget?

GameInfoHUD.cpp

AGameInfoHUD::AGameInfoHUD()
{
static ConstructorHelpers::FClassFinder<UUserWidget> ScoreWidgetObj(TEXT("/Game/Blueprints/Widgets/WBP_ScoreWidget"));
ScoreWidgetClass = ScoreWidgetObj.Class;
}

void AGameInfoHUD::BeginPlay()
{
Super::BeginPlay();
if (ScoreWidgetClass != nullptr)
{
// .cpp - create the widget from the given class
CurrentWidget = CreateWidget<UUserWidget>(GetWorld()->GetFirstPlayerController(), ScoreWidgetClass);
if (CurrentWidget)
{
CurrentWidget->AddToViewport();
}
}
}

After managing HUD on several projects that’s pretty much how we do it as well.

  • HUDWidgets: contain logic pertaining to the widget itself, register with the HUD class on BeginPlay()
  • HUD class: the interface from all other code to any HUD element, manages all the HUDWidgets and funnels data to them when needed
  • Done this way for all GameModes

That way, nothing outside of the HUD is aware of any widgets. Outside code just needs to know HUD::UpdateScoreBoard().

@clintchangrules

  1. You don’t have to find the ScoreWidgetClass. In fact, I always discourage people from doing that, as it basically means you hard code the class into the system (fine in some cases obviously). Since it’s marked as editable (via the UPROPERTY), you can set it in the details panel of the HUD class. If you want to initialize it with a class (so that it isn’t null by default), use the StaticClass() of your custom C++ widget class.


// MyHUD.h - this will allow you to set a widget in the editor
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TSubclassOf<UUserWidget> ScoreWidgetClass;

// MyHUD.cpp - in the constructor.
ScoreWidgetClass = UMyCustomWidgetClass::StaticClass();


  1. If you haven’t created any additional variable or function in the C++ widget class then sure it’s just a shell. But you can extend it at any time. I suggested a UpdateScore function because this way, you can call this function from C++. The issue with binding is that it will call the binded function every frame. This obviously isn’t the most performant/effective way of doing things, but I also have to say that the performance impact is negligible. As I’ve said before, you can have the best of both worlds if you mark the function as a BlueprintImplementableEvent. That way you can code the function in blueprints, but still have the ability to call it from C++.

Hope this helps, cheers!

Thanks STRIFE - that all makes sense now.

  1. My bad - I didn’t notice/realize the UPROPERTY in the .h was telling me I needed to specify the class in a blueprint HUD.
  2. Yeah that makes sense to do it via function call… I don’t need the score checking every frame for sure. I’ll give that a shot.

Cheers!