Can't access to widget second time

I have a AScoreboardWidget

ScoreboardWidget.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ScoreboardWidget.generated.h"

UCLASS()
class FLY_BIRD_FLY_API AScoreboardWidget : public AActor {
	GENERATED_BODY()

	public:
		// Sets default values for this actor's properties
		AScoreboardWidget();

	protected:
		// Called when the game starts or when spawned
		virtual void BeginPlay() override;

	public:
		// Called every frame
		virtual void Tick(float DeltaTime) override;

		void SetScore(int Score);

	private:
		UPROPERTY(EditDefaultsOnly, Category = "Category")
		class UUserWidget* Scoreboard;

};

ScoreboardWidget.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "ScoreboardWidget.h"
#include "Blueprint/UserWidget.h"
#include "Components/TextBlock.h"
#include "Internationalization/Text.h"

// Sets default values
AScoreboardWidget::AScoreboardWidget() {
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AScoreboardWidget::BeginPlay() {
	Super::BeginPlay();

	Scoreboard->AddToViewport();

	Cast<UTextBlock>(Scoreboard->GetWidgetFromName(TEXT("ScoreText")))->SetText(
		FText::FromString("Score is: 0")
	);
}

// Called every frame
void AScoreboardWidget::Tick(float DeltaTime) {
	Super::Tick(DeltaTime);
}

void AScoreboardWidget::SetScore(int Score) {
	UE_LOG(LogTemp, Warning, TEXT("Score: %d"), Score);
	
	if (auto x = Scoreboard) {
		UE_LOG(LogTemp, Warning, TEXT("Scoreboard found"));
	} else {
		UE_LOG(LogTemp, Warning, TEXT("Scoreboard not found"));

		return;
	}

	if (auto x = Scoreboard->GetWidgetFromName(TEXT("ScoreText"))) {
		UE_LOG(LogTemp, Warning, TEXT("Widget found"));
	} else {
		UE_LOG(LogTemp, Warning, TEXT("Widget not found"));

		return;
	}
	
	Cast<UTextBlock>(Scoreboard->GetWidgetFromName(TEXT("ScoreText")))->SetText(
		FText::FromString("Score is: 1")
	);

	// Cast<UTextBlock>(Scoreboard->GetWidgetFromName(TEXT("ScoreText")))->SetText(
	// 	FText::FromString(
	// 		FString::Printf(TEXT("Score is: %d"), Score)
	// 	)
	// );
}

Also I have a ABird class which uses this widget

Bird.h

...
public:
		...
		void SetScore(int NewScore);

private:
		...
		UPROPERTY(EditDefaultsOnly, Category = "Components")
		TSubclassOf<AScoreboardWidget> ScoreboardWidget;
...

Bird.cpp

...
void ABird::SetScore(int NewScore) {
	Score = NewScore;
	UE_LOG(LogTemp, Warning, TEXT("Score: %d"), Score);
	ScoreboardWidget->GetDefaultObject<AScoreboardWidget>()->SetScore(Score);
}
...

Here is more info on screenshots:




So as you can see I can access to widget inside of BeginPlay but not inside of SetScore. What is the problem?

In AScoreboardWidget BeginPlay you need to create the widget and then add it to the viewport.

AScoreboardWidget:

UPROPERTY(EditAnywhere)
TSubclassOf<UUserWidget> ScoreboardClass;
UUserWidget* ScoreboardWidget;
//BeginPlay
ScoreboardWidget = UWidgetBlueprintLibrary::Create(this, ScoreboardClass, GetWorld()->GetFirstPlayerController());
ScoreboardWidget->AddToViewport();

Set ScoreboardClass in you blueprint child in AScoreboardWidget.

Then you need a reference to the AScoreboardWidget Actor in your ABird.
ABird:

UPROPERTY(EditAnywhere)
AScoreboardWidget* WidgetActor;
// SetScore function
WidgetActor->SetScore(Score)

You definitely need to set a specific AScoreboardWidget actor (via the detail panel on the level or UGameplayStatics::GetActorOfClass).

But already have a widget. You can see in screenshots that I’m able to interact with it inside BeginPlay method. The text “Score is: 0” is from this method

In ABird you take GetDefaultObject - this is an engine object that stores the default values ​​of class variables. You should not use it as an object that can be used at runtime.

At runtime you need to create a new widget instance as described above.

To be honest, I’m surprised you managed to use it the first time. It’s not supposed to work like that.

I’m really sorry to bothering you. But if you don’t mind can we please continue discussing? I don’t understand a several things:

  1. I used GetDefaultObject because I saw a lot of time that using TSubclassOf is more safety and help in editor to choose correct object. BUT when I use this I can’t access to the methods and variables of desired class. Because of this I started using GetDefaultObject everywhere to access members which I need. What is an alternative way to do it?
  2. Also you says that it is engine object and it just stores the default values BUT I used it a lot of time and didn’t get any errors till yesterday. Also when I used it in other places I correctly got the need objects which I define from Editor so I don’t understand how does it work?
  3. I still can’t understand why I can access the widget inside Scoreboard in BeginPlay but can’t get access to widget inside Scoreboard in SetScore? In which moment I loose the widgets which is defined inside Scoreboard?

Sorry if they are stupid questions but I can’t understand the core concepts

Class - is the plan according to which the objectwill be built.
You can create one class, and then create countless objects (instances of the class).

After creating an object - you can access their variables and functions.

TSubclassOf as the name suggests, it allows you to select a CLASS that is a child of the specified one. In your case, it is a child class of UUserWidget.
Then you need to use this to create objects of selected class.

In C++, once the source code is compiled - you cannot change the class. You can only change the objects from which they were created.
But you can create a blueprint based on a C++ class.
That’s why Epic Games came up with CDO (Class Default Object) - an object in which you change the values ​​when you edit a class in Blueprint.
You can get a CDO and simulate its change similar to how you do it in blueprint, but this should only be used during development, not runtime.

When you change a CDO you need to regenerate the class (compile the blueprint). After the class is generated, a new CDO is also created.
When you run PIE all blueprints are compiled automatically. Probably when PIE is already running - this happens too.
Objects created based on an old class break because their class is no longer valid.
Apparently this is exactly what happens after you call SetText.

So If I understood you correctly, when I create a widget in editor like this:

I create a CLASS and not the object which I can interact directly?

It is a little bit strange because I thought I already created a widget I don’t wanna to create it again from C++ I just need a reference to this

I found this video which I think talking exactly what did you mean

Now I can understand you more :slight_smile:

I just thought that I can create widget just in editor and then interact with it but now I see that I have to create a CLASS by myself and create a widget based on this CLASS

I’m still a little confused because in my mind it must be legal to work with widget created by editor directly without recreating it in C++

BTW Thank you so much for your time

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.