In here will be the code I used for both my .h and .cpp files.
// In my .h file
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Components/TextBlock.h"
#include "MyHUDDisplay.generated.h"
/**
*
*/
UCLASS()
class TOAS_NE_CPP_API UMyHUDDisplay : public UUserWidget
{
GENERATED_BODY()
public:
// Crearemos esta funciĂłn para actualizar el texto mostrado
// en el contador de puntaje.
UFUNCTION(BlueprintCallable)
void UpdateScoreText(int32 ScoreToUpdate);
protected:
// Etiqueta "BindWidget" en los metadatos de nuestra variable
// permite a Unreal conectar esta variable con algĂşn
// componente de widget del tipo al que pertenece esta clase
// mientras ese componente tenga el mismo nombre que nuestra
// variable.
UPROPERTY(meta = (BindWidget))
class UTextBlock* UTB_ScoreDisplay;
// FunciĂłn heredada de nuestra Super Clase UserWidget.h que
// es llamado al momento de constuir el HUD en pantalla,
// serĂa como nuestro constructor especializado en Widgets.
void NativeConstruct() override;
};
// And over here my .cpp file
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyHUDDisplay.h"
/// <summary>
/// From this function the score is updated on screen.
/// </summary>
/// <param name="ScoreToUpdate">Score to update. By design impossible to lower.</param>
void UMyHUDDisplay::UpdateScoreText(int32 ScoreToUpdate)
{
// From this function the score will be updated.
UTB_ScoreDisplay->SetText(FText::FromString("SCORE : " + FString::FromInt(ScoreToUpdate)));
}
void UMyHUDDisplay::NativeConstruct()
{
Super::NativeConstruct();
// Constructing the display on viewport would update the score text automatically
UpdateScoreText(0);
}
And with this setup I create a Blueprint Widget with this class as it’s Parent.
I already set it up with a TextBlock with the same name as it’s variable in the Parent Class, so all I should do is add it to the viewport.
However, to do that I need to access the static class of my Widget.
I intend to Add to the Viewport from the Game Mode like this:
UMyHUD = CreateWidget<UMyHUDDisplay>(GetWorld()->GetGameInstance(), UMyHUDDisplay::StaticClass());
// Of course, with the exception that UMyHUDDisplay should actually be something like UMyHUDFromCPP
Normally what I thought I would do is to #include the reference to use the class, but I haven’t really found a proper way to do this at all, as much as I search even using the Reference Viewer for the Widget.
And then set its value in your blueprint that you’ve created based on this c++ class.
Or if there’s no blueprint, you can skip the UPROPERTY() line and set the value using ConstructorHelpers.
However, in either case, you won’t be able to access any functions in the widget through c++ code. If you need to do that, you’ll need a c++ widget class.
I went and checked out the ConstructorHelpers, tried it out but an error was printed on screen:
Default Property warnings and errors:
Error: CDO Constructor (ToaS_NE_CPPGameModeBase): Failed to find /Game/Widgets/MyHUDFromCPP.MyHUDFromCPP
I did use the correct Widget I wanted to construct from its class, but apparently the ConstructorHelpers weren’t able to find it- unless? I did something wrong?
This is what I did to my header:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyPaperCharacter.h"
#include "MyHUDDisplay.h"
// Went and #included the uses of UserWidget and ConstructorHelpers
#include "Blueprint/UserWidget.h"
#include "UObject/ConstructorHelpers.h"
#include "ToaS_NE_CPPGameModeBase.generated.h"
/**
*
*/
UCLASS()
class TOAS_NE_CPP_API AToaS_NE_CPPGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
AToaS_NE_CPPGameModeBase();
void AddPoints(uint32 uint_NewPoints);
// Added and changed the classes to load the UUserWidget.
TSubclassOf<class UUserWidget> UMyHUD_Class;
UPROPERTY(EditAnywhere)
class UUserWidget* UMyHUD;
private:
uint32 uint_LevelPoints;
protected:
virtual void BeginPlay() override;
};
And to my CPP file:
#include "ToaS_NE_CPPGameModeBase.h"
// To my Game Mode constructor I put the ConstructorHelpers as I was told from another source.
AToaS_NE_CPPGameModeBase::AToaS_NE_CPPGameModeBase()
{
DefaultPawnClass = AMyPaperCharacter::StaticClass();
ConstructorHelpers::FClassFinder<UUserWidget> WidgetClassFinder(TEXT("WidgetBlueprint'/Game/Widgets/MyHUDFromCPP.MyHUDFromCPP'"));
if (WidgetClassFinder.Succeeded())
{
UMyHUD_Class = WidgetClassFinder.Class;
}
}
void AToaS_NE_CPPGameModeBase::AddPoints(uint32 uint_NewPoints)
{
if (uint_LevelPoints <= 255)
{
uint_LevelPoints += uint_NewPoints;
}
if (GEngine)
{
}
}
// And on BeginPlay I'd know if the Class of my Widget (with a C++ class as its parent) was found.
void AToaS_NE_CPPGameModeBase::BeginPlay()
{
if (UMyHUD_Class != nullptr)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("A CLASS WAS FOUND."));
}
}
}
This is it! This syntax is the correct way to use, thanks @Tuerer !
ConstructorHelpers::FClassFinder<T>
So, according to this case, the solution to finding my Widget Blueprint was to use the ConstructorHelpers::FClassFinder.
In our .h file we would “include” the following:
// The class of our C++ Widget, in my case:
#include "MyHUDDisplay.h"
// Went and #included the uses of UserWidget and ConstructorHelpers
#include "Blueprint/UserWidget.h"
#include "UObject/ConstructorHelpers.h"
UCLASS()
class TOAS_NE_CPP_API AToaS_NE_CPPGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
// Our public constructor.
AToaS_NE_CPPGameModeBase();
// In my case I have a function that is called by the coins in my game when they're collected by the playable character.
void AddPoints(uint32 uint_NewPoints);
// We add the following to our public variables, we want to find it as a UUserWidget class.
TSubclassOf<class UUserWidget> UMyHUD_Class;
// In this variable we will Create and Reference our widget to add to viewport.
UPROPERTY()
class UUserWidget* UMyHUD;
private:
// I keep my points, using the Super Mario 64 rules of coin values (yellow = 1, red = 2, blue = 5) as they're added when they're collected with the previous function.
uint32 uint_LevelPoints;
protected:
// And, as usual, our BeginPlay in our protected declaration.
virtual void BeginPlay() override;
};
And in our CPP file of our GameMode we execute the following code:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "ToaS_NE_CPPGameModeBase.h"
AToaS_NE_CPPGameModeBase::AToaS_NE_CPPGameModeBase()
{
// This is where I tell my GameMode to load the static class of our playable character, in my case it's a 2D Paper Character.
DefaultPawnClass = AMyPaperCharacter::StaticClass();
// Inside our constructor we will also use the ConstructorHelpers, using the FClassFinder.
// T type should be UUserWidget so that we can track our Widget correctly.
// Usually when we Copy the Reference to our assets in our Content source it would normally come out as something like this: WidgetBlueprint'/Game/Widgets/MyHUDFromCPP.MyHUDFromCPP'
// To make our ConstructorHelpers work this out, just as user Tuerer has pointed out, we need to remove from our reference the WidgetBlueprint part, and also our ' ' from the string. That way the Helpers won't have trouble finding our Widget or any asset you need to find.
ConstructorHelpers::FClassFinder<UUserWidget> WidgetClassFinder(TEXT("/Game/Widgets/MyHUDFromCPP"));
if (WidgetClassFinder.Succeeded())
{
// Just to make sure that this is done when the Helpers succeed with our class I placed an if. However, once it does then our .Class will be referenced in our _Class variable.
UMyHUD_Class = WidgetClassFinder.Class;
}
}
// The function that is called when a coin is collected in the world.
void AToaS_NE_CPPGameModeBase::AddPoints(uint32 uint_NewPoints)
{
if (uint_LevelPoints <= 255)
{
uint_LevelPoints += uint_NewPoints;
}
// We verify that our Widget was created and the reference is not empty
if (UMyHUD != nullptr)
{
// Since I'm using a C++ Widget, which means that it's a widget that has a C++ class as a parent (which in turn has UserWidget as a parent) I cast it to the C++ class so I can access my function to update the score text.
UMyHUDDisplay* UMyHUD_OnScreen = Cast<UMyHUDDisplay>(UMyHUD);
if (UMyHUD_OnScreen != nullptr)
{
UMyHUD_OnScreen->UpdateScoreText(uint_LevelPoints);
}
}
}
void AToaS_NE_CPPGameModeBase::BeginPlay()
{
// We verify that our Class reference is not empty
if (UMyHUD_Class != nullptr)
{
// We will create our widget using the instance of our game as the owner of the widget, and also the _Class variable we aquired.
UMyHUD = CreateWidget<UUserWidget>(GetWorld()->GetGameInstance(), UMyHUD_Class);
// We then proceed to Add our Created Widget to our Viewport in our designated layer.
UMyHUD->AddToViewport(0);
}
}