In this tutorial video, I explain how to set up in-world widgets that are attached to actors, using the UWidgetComponent class. Dog.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Dog.generated.h" UCLASS() class EXAMPLE_API ADog : public AActor { GENERATED_BODY() public: ADog( const FObjectInitializer& ObjectInitializer ); virtual void Tick(float DeltaTime) override; 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 BeginPlay() override; UPROPERTY( VisibleAnywhere ) class UWidgetComponent* HealthWidgetComp; FVector MovementVelocity; float Health; float MaxHealth = 120; float HealthTweenDirection; }; Dog.cpp #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.h #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "Dog.h" #include "HealthBar.generated.h" UCLASS( Abstract ) class EXAMPLE_API UHealthBar : public UUserWidget { GENERATED_BODY() public: void SetOwnerDog( ADog* InDog ) { OwnerDog = InDog; } protected: void NativeTick( const FGeometry& MyGeometry, float InDeltaTime ) override; TWeakObjectPtr<ADog> OwnerDog; UPROPERTY( meta = ( BindWidget ) ) class UProgressBar* HealthBar; UPROPERTY( meta = ( BindWidget ) ) class UTextBlock* CurrentHealthLabel; UPROPERTY( meta = ( BindWidget ) ) class UTextBlock* MaxHealthLabel; }; HealthBar.cpp #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 ) ); }
Add UWidgets to a UserWidget using C++ in the Editor How to programmatically build up a widget in the editor, while keeping the widget tree in sync.