How do I Get My Pickup To Increase A Counter In My Widget?

I’m working on a project that requires me to code a user HUD with a scoring system based on pickups, but the tutorial I’ve followed shows me how to create the pickup, and not translate the additional score into my widget, so I figured I’d come here and seek out help. Basically, I’ve created a coin that is supposed to award the user a score award, and then display that score on the main viewport via a HUD. I’m pretty sure I have to do something with UUserWidget and UPROPERTYs, but I’m unsure how to approach the task. Here is my code:

Coin.cpp:



#include "Coin.h"

// Sets default values
ACoin::ACoin()
{
//Declaring variables for the pickup.
fg_bCollected = false;
fg_Pitch = 0.f;

// 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;

//Root for the pickup item.
fg_Root = CreateDefaultSubobject<USceneComponent>(TEXT("Scene Root"));

//Static Mesh compontent.
fg_StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
fg_StaticMesh->SetupAttachment(fg_Root);

//Particle system.
static ConstructorHelpers::FObjectFinder<UParticleSystem>pickupFX(TEXT("/Game/InfinityBladeEffects/Effects/FX_Forging/P_Buff_Loop"));
fg_ParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("PickupEffect"));
fg_ParticleSystem->SetTemplate(pickupFX.Object);
fg_ParticleSystem->SetWorldScale3D(FVector(3.0f, 3.0f, 3.0f));

//Sphere trigger.
fg_SphereCollider = CreateDefaultSubobject<USphereComponent>(TEXT("SphereTrigger"));
fg_SphereCollider->SetGenerateOverlapEvents(true);
fg_SphereCollider->SetWorldScale3D(FVector(1.0f, 1.0f, 1.0f));
fg_SphereCollider->OnComponentBeginOverlap.AddDynamic(this, &ACoin::fg_OnOverlapBegin);
fg_SphereCollider->AttachToComponent(fg_Root, FAttachmentTransformRules::SnapToTargetNotIncludingScale);

}

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

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

//Adding rotation to the pickup.
FRotator rotation = FRotator(fg_Pitch, 0.f, 0.f);
FQuat quaternion = FQuat(rotation);

fg_Pitch += 0.1f * DeltaTime;

//Applying rotation.
AddActorLocalRotation(quaternion, false, 0, ETeleportType::None);

//Check if the pickup has been collected.
if (fg_bCollected == true) {
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), fg_ParticleSystem->Template,
GetActorLocation());
Destroy();
}
}
//Logic to handle pickup and set bCollected to true.
void ACoin::fg_OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) {
if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr)) {
fg_bCollected = true;
Score += 100;
}
}

Coin.h**:**


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

#pragma once

#include "Engine.h"
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "Components/SceneComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Particles/ParticleSystemComponent.h"
#include "CoreUObject/Public/UObject/ConstructorHelpers.h"
#include "Coin.generated.h"

UCLASS()
class GAM312_PROJECT_API ACoin : public AActor
{
GENERATED_BODY()

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

private:
//Bool value to store whether coin has been collected or not.
bool fg_bCollected;
//Float value to control rotation on pitch.
float fg_Pitch;
//Int value to control Score.
int Score = 0;

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

//Declaring necessary UPROPERTY values for static mesh and particle components.
UPROPERTY(EditAnywhere, Category = "Components")
USceneComponent* fg_Root;

UPROPERTY(EditAnywhere, Category = "Components")
UStaticMeshComponent* fg_StaticMesh;

UPROPERTY(EditAnywhere, Category = "Components")
USphereComponent* fg_SphereCollider;

UPROPERTY(EditAnywhere, Category = "Components")
UParticleSystemComponent* fg_ParticleSystem;

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

//Creating overlap UFUNCTION.
UFUNCTION()
void fg_OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};


I appreciate any help and insight into helping me solve this issue for my game.

You’ll want to store the score in your player’s pawn/character or the GameMode or GameInstance… depends on your needs.

Then you’ll want to setup a binding in your HUD widget to read and display that value from the player/game mode/instance.

If you can, I’d definitely recommend doing the HUD widget side of things in UMG and Blueprints in the Editor. I’ve learned the semi-hard way that this type of stuff is much easier and quicker to setup once you learn BPs.

You can approach this from a few different angles but here’s what I suggest (I’m assuming this is a singleplayer game!). Have the “Score” variable in your GameMode class. Make a UMG Widget for the score display. Widgets can have functions as well, so you can make a function (UpdateScoreText) that will get the score from the GameMode and update the score text. (I recommend creating a UUserWidget class in C++, then in the editor, just create a UserWidget and reparent it to the C++ class)

In you HUD class, you can create the widget on BeginPlay(), and store a reference to it. When the player picks up the coin, get the GameMode and increase the score, then get the score widget (through the first player controller’s HUD) and call the UpdateScoreText function.

Thank you for your response! Normally, I’d do this in BPs, as I’m a bit more comfortable with them, however for this specific project I am required to code it myself in C++, without the use of BPs.

Regarding your insight, I completely forgot I should assign that in my character or GameMode. I feel like GameMode would work better as eventually, I would like my character to have other values (multipliers and the like) that are based on timers, so I feel like separating the score from the class that will house the multiplier may prevent some issues. It’s just figuring out how to apply this code via C++ and the binding that I’m primarily having issue with.

I appreciate your insight and help!

My apologies, I completely forgot to mention that this is indeed a single-player game (as well as my being extremely green to coding in UE4). It’s going to serve as a proof of concept for me that will hopefully lead to a larger project on the more personal side of things.

I think I understand what you’re saying, something along these lines, correct?

ScoreWidget.cpp



#include "GAM312_ProjectGameMode.h"
#include "ScoreWidget.h"

int32 UScoreWidget::GetScore() {
if (AGAM312_ProjectGameMode* GameMode = Cast<AGAM312_Project>(GetWorld()->GetAuthGameMode())) {
return GameMode->GetScore();
}
}

ScoreWidget.h


#pragma once

#include "Engine.h"
#include "GAM312_Project.h"
#include "Coin.h"
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "ScoreWidget.generated.h"

/**
*
*/
UCLASS()
class GAM312_PROJECT_API UScoreWidget : public UUserWidget
{
GENERATED_BODY()

private:
UFUNCTION(BlueprintCallable, Category = "ScoreWidget")
int32 GetScore;
};

GameMode.cpp


#include "GAM312_ProjectGameMode.h"
#include "GAM312_ProjectHUD.h"
#include "GAM312_ProjectCharacter.h"
#include "UObject/ConstructorHelpers.h"

AGAM312_ProjectGameMode::AGAM312_ProjectGameMode()
: Super()
{
//Setting variable for score.
int score = 0;

// set default pawn class to our Blueprinted character
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnClassFinder(TEXT("/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter"));
DefaultPawnClass = PlayerPawnClassFinder.Class;

// use our custom HUD class
HUDClass = AGAM312_ProjectHUD::StaticClass();

void AGAM312_ProjectGameMode::GetScore() {
if (fg_bCollected == true) {
score += 100;
}
}
}

HUD.h


// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "GAM312_ProjectHUD.generated.h"

UCLASS()
class AGAM312_ProjectHUD : public AHUD
{
GENERATED_BODY()

public:
AGAM312_ProjectHUD();

/** Primary draw call for the HUD */
virtual void DrawHUD() override;

private:
/** Crosshair asset pointer */
class UTexture2D* CrosshairTex;

void UUserWidget();

UFUNCTION(BlueprintCallable, Category = "ScoreWidget")
class ScoreText;
};

HUD.cpp


// Copyright Epic Games, Inc. All Rights Reserved.

#include "GAM312_ProjectHUD.h"
#include "Engine/Canvas.h"
#include "Engine/Texture2D.h"
#include "TextureResource.h"
#include "CanvasItem.h"
#include "UObject/ConstructorHelpers.h"

AGAM312_ProjectHUD::AGAM312_ProjectHUD()
{
// Set the crosshair texture
static ConstructorHelpers::FObjectFinder<UTexture2D> CrosshairTexObj(TEXT("/Game/FirstPerson/Textures/FirstPersonCrosshair"));
CrosshairTex = CrosshairTexObj.Object;
}


void AGAM312_ProjectHUD:: DrawHUD()
{
Super:: DrawHUD();

// Draw very simple crosshair

// find center of the Canvas
const FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);

// offset by half the texture's dimensions so that the center of the texture aligns with the center of the Canvas
const FVector2D CrosshairDrawPosition((Center.X),
(Center.Y + 20.0f));

// draw the crosshair
FCanvasTileItem TileItem(CrosshairDrawPosition, CrosshairTex->Resource, FLinearColor::White);
TileItem.BlendMode = SE_BLEND_Translucent;
Canvas->DrawItem(TileItem);
}

void AGAM312_ProjectHUD::ScoreText() {
FText score = FString::FromInt(score);
}


I’m certain I haven’t done this correctly, as the compile failed, but I feel like I’m at least in the city or the ballpark? Again, I appreciate your insight and help!

I just glanced over the code, there are lots of “errors”, to the point where I’m not sure if you copied everything correctly from your actual scripts or not. For example in the HUD.h, “ScoreText” is a class, marked as UFUNCTION? It’s not even a function lol. It’s fine if you don’t understand stuff, but this straight doesn’t make any sense. Another example, GameMode.h, the score variable is named “GetScore”, also marked as UFUNCTION.

Anyways, don’t check if the coin is collected or not. Separate things. In the GameMode class, have the score as a variable, and two functions: GetScore(), AddScore(int32 Amount). Now the coin. When you pick it up (basically when you overlap with it), you can get the GameMode and call AddScore(100); to increase the score.

Yeah, I’m quickly realizing I may not be understanding or interpreting what I’m learning correctly. Then again, this project involves working off of older code with no explanation or reasoning; hence why I was hoping I would learn something here, which I have, as I haven’t really learned anything from what I’m supposed to be learning from, lol. I removed the UFUNCTIONS in HUD.h and GameMode.h. My logic behind that was using a base from another tutorial I was given for the project, but didn’t end up working, which subsequently lead me to try out a scoring system as I remember being fairly simple in blueprints. I have declared score as an int variable and added the two functions, but still can’t get it to work. So I think I may just scrap this and find something else more up to my speed.