In this tutorial video, I explain how to set up in-world widgets that are attached to actors, using the UWidgetComponent class.Dog.hCopy#pragma once #include "GameFramework/Actor.h" #include "Dog.generated.h" class UWidgetComponent; UCLASS() class ADog : public AActor { GENERATED_BODY() public: ADog(const FObjectInitializer& ObjectInitializer); float GetHealth() const { return Health; } void SetHealth(float Val) { Health = Val; } float GetMaxHealth() const { return MaxHealth; } void SetMaxHealth(float Val) { MaxHealth = Val; } protected: virtual void Tick(float DeltaTime) override; virtual void BeginPlay() override; UPROPERTY(VisibleAnywhere) TObjectPtr<UWidgetComponent> HealthWidgetComp; FVector MovementVelocity = FVector::ZeroVector; float Health = 0; float MaxHealth = 120; float HealthTweenDirection = 0; }; Dog.cppCopy#include "Dog.h" #include "Components/WidgetComponent.h" #include "HealthBar.h" ADog::ADog(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PrimaryActorTick.bCanEverTick = true; if (RootComponent == nullptr) { RootComponent = ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("Root")); } HealthWidgetComp = ObjectInitializer.CreateDefaultSubobject<UWidgetComponent>(this, TEXT("HealthBar")); HealthWidgetComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform); Health = MaxHealth; } void ADog::BeginPlay() { Super::BeginPlay(); UHealthBar* HealthBar = Cast<UHealthBar>(HealthWidgetComp->GetUserWidgetObject()); HealthBar->SetOwnerDog(this); // Make sure every dog starts with different health Health = FMath::RandRange(0.0f, MaxHealth); HealthTweenDirection = FMath::RandBool() ? 1 : -1; // Move in a random direction at a random speed const float DirRads = FMath::RandRange(0.0f, 2 * PI); MovementVelocity = FVector(FMath::Cos(DirRads), FMath::Sin(DirRads), 0) * 10.0f; // Face that way too RootComponent->SetWorldRotation(FRotator::MakeFromEuler(FVector(0, 0, DirRads / PI * 180 - 90))); } void ADog::Tick(float DeltaTime) { Super::Tick(DeltaTime); SetActorLocation(GetActorLocation() + MovementVelocity * DeltaTime); // Bounce health between min and max to show off health bar static const float TweenSpeed = 10.0f; Health = FMath::Clamp<float>(Health + DeltaTime * HealthTweenDirection * TweenSpeed, 0, MaxHealth); if ((Health == MaxHealth && HealthTweenDirection == 1) || (Health == 0 && HealthTweenDirection == -1)) { HealthTweenDirection *= -1; } } HealthBar.hCopy#pragma once #include "Blueprint/UserWidget.h" #include "Dog.h" #include "HealthBar.generated.h" class UProgressBar; class UTextBlock; UCLASS(Abstract) class UHealthBar : public UUserWidget { GENERATED_BODY() public: void SetOwnerDog(ADog* InDog) { OwnerDog = InDog; } protected: virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; TWeakObjectPtr<ADog> OwnerDog; UPROPERTY(meta=(BindWidget)) TObjectPtr<UProgressBar> HealthBar; UPROPERTY(meta=(BindWidget)) TObjectPtr<UTextBlock> CurrentHealthLabel; UPROPERTY(meta=(BindWidget)) TObjectPtr<UTextBlock> MaxHealthLabel; }; HealthBar.cppCopy#include "HealthBar.h" #include "Components/ProgressBar.h" #include "Components/TextBlock.h" void UHealthBar::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) { Super::NativeTick(MyGeometry, InDeltaTime); if (!OwnerDog.IsValid()) return; HealthBar->SetPercent(OwnerDog->GetHealth() / OwnerDog->GetMaxHealth()); FNumberFormattingOptions Opts; Opts.SetMaximumFractionalDigits(0); CurrentHealthLabel->SetText(FText::AsNumber(OwnerDog->GetHealth(), &Opts)); MaxHealthLabel->SetText(FText::AsNumber(OwnerDog->GetMaxHealth(), &Opts)); } actor📘Blueprint🖱️UI🏛️UMG📺Video