UniversalBars plugin: PEAK-style universal UMG bars
Procedural M_UI_UniversalBar material (SDF rounded-rect, fill/delayed/shield/ penalty-zones/stripes/segments/flash/pulse), 6 preset instances, animated UUniversalBarWidget + UUniversalHUDWidget + global UUniversalBarsSubsystem API, UUniversalBarsTheme DataAsset, and a ready WBP_HUD_InGame with zone-tracking status chips. See README.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
157
Source/UniversalBars/Public/UniversalBarWidget.h
Normal file
157
Source/UniversalBars/Public/UniversalBarWidget.h
Normal file
@ -0,0 +1,157 @@
|
||||
// Copyright IHY. Universal Bars plugin.
|
||||
// One animated UMG bar driven by M_UI_UniversalBar. Handles a single logical
|
||||
// value (HP, stamina, durability, ...) plus optional data-driven penalty zones
|
||||
// for combined PEAK-style capacity bars (hunger / weight / cold eating into max).
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "UniversalBarWidget.generated.h"
|
||||
|
||||
class UImage;
|
||||
class UMaterialInstanceDynamic;
|
||||
|
||||
// A penalty / reserve zone consuming capacity from the right end of the bar.
|
||||
USTRUCT(BlueprintType)
|
||||
struct FBarZone
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Zone")
|
||||
FName Id = NAME_None;
|
||||
|
||||
// Target width as a fraction of the full track (0..1).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Zone", meta=(ClampMin="0", ClampMax="1"))
|
||||
float Width = 0.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Zone")
|
||||
FLinearColor Color = FLinearColor(0.9f, 0.6f, 0.2f, 1.f);
|
||||
|
||||
// Animated width that eases toward Width at runtime (not authored).
|
||||
float DisplayWidth = 0.f;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNIVERSALBARS_API UUniversalBarWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UUniversalBarWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
// Name this "BarImage" in the WBP; assign an MI_UI_Bar_* (or the master) to its Brush.
|
||||
UPROPERTY(meta=(BindWidget))
|
||||
TObjectPtr<UImage> BarImage = nullptr;
|
||||
|
||||
// ---- animation tuning ----
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
float FillRiseSpeed = 7.f; // how fast the bright fill grows on increase/heal
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
float DelayedFallSpeed = 4.f; // how fast the damage-lag tail drains on decrease
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
float DelayedDelay = 0.25f; // pause before the tail starts draining (sec)
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
float FlashSpeed = 6.f; // flash decay rate
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
float ZoneInterpSpeed = 8.f; // penalty-zone width easing
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
float LowThreshold = 0.25f; // below this, low-value pulse turns on
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Anim")
|
||||
bool bAutoFlashOnChange = true; // SetFillPercent auto-plays damage/heal flash
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Colors")
|
||||
FLinearColor DamageFlashColor = FLinearColor(1.f, 0.12f, 0.12f, 1.f);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Colors")
|
||||
FLinearColor HealFlashColor = FLinearColor(0.6f, 1.f, 0.6f, 1.f);
|
||||
|
||||
// ---- data-driven penalty zones (combined / PEAK mode) ----
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bar|Zones")
|
||||
TArray<FBarZone> PenaltyZones;
|
||||
|
||||
// ===== public API =====
|
||||
// Animated set of the bar's main value (0..1). Decrease => instant drop + lag
|
||||
// tail (+ damage flash). Increase => smooth rise (+ heal flash).
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void SetFillPercent(float NewValue, bool bAnimate = true);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void SetShieldPercent(float NewValue);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void PlayDamageFlash();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void PlayHealFlash();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void PlayFlash(FLinearColor Color);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void SetBarColors(FLinearColor Fill, FLinearColor Empty, FLinearColor Border, FLinearColor Delayed);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void SetSegmentCount(int32 Count);
|
||||
|
||||
// ---- penalty zone management (animated) ----
|
||||
UFUNCTION(BlueprintCallable, Category="Bar|Zones")
|
||||
void SetPenaltyZone(FName Id, float Width, FLinearColor Color);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar|Zones")
|
||||
void SetPenaltyZoneWidth(FName Id, float Width);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar|Zones")
|
||||
void RemovePenaltyZone(FName Id);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar|Zones")
|
||||
void ClearPenaltyZones();
|
||||
|
||||
// Generic escape hatches.
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void SetScalar(FName Param, float Value);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Bar")
|
||||
void SetVector(FName Param, FLinearColor Value);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="Bar")
|
||||
float GetFillPercent() const { return TargetFill; }
|
||||
|
||||
// ---- zone layout in bar-U space (0..1), using current animated widths ----
|
||||
// Right edge of the usable region (1 - sum of penalty display widths).
|
||||
UFUNCTION(BlueprintPure, Category="Bar|Zones")
|
||||
float GetUsableEnd() const;
|
||||
|
||||
// U-coordinate of a penalty zone's centre; -1 if the zone is absent.
|
||||
UFUNCTION(BlueprintPure, Category="Bar|Zones")
|
||||
float GetZoneCenter(FName Id) const;
|
||||
|
||||
protected:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
UMaterialInstanceDynamic* EnsureMID();
|
||||
void PushZones(bool bImmediate);
|
||||
|
||||
private:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UMaterialInstanceDynamic> BarMID = nullptr;
|
||||
|
||||
float TargetFill = 1.f;
|
||||
float DisplayFill = 1.f;
|
||||
float DelayedFill = 1.f;
|
||||
float DelayTimer = 0.f;
|
||||
|
||||
float FlashAmount = 0.f;
|
||||
bool bFlashing = false;
|
||||
FLinearColor PendingFlashColor = FLinearColor::Red;
|
||||
|
||||
float PulseAmount = 0.f;
|
||||
|
||||
static constexpr int32 MaxMaterialZones = 4;
|
||||
};
|
||||
50
Source/UniversalBars/Public/UniversalBarsSubsystem.h
Normal file
50
Source/UniversalBars/Public/UniversalBarsSubsystem.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright IHY. Universal Bars plugin.
|
||||
// Global access point for the active HUD. From any Blueprint/C++:
|
||||
// Get World Subsystem -> UniversalBarsSubsystem -> Set Health (0.5)
|
||||
// No widget reference needed. The HUD widget registers itself on construct.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "UniversalBarsSubsystem.generated.h"
|
||||
|
||||
class UUniversalHUDWidget;
|
||||
class APlayerController;
|
||||
|
||||
UCLASS()
|
||||
class UNIVERSALBARS_API UUniversalBarsSubsystem : public UWorldSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// One-call accessor from any object with a world.
|
||||
UFUNCTION(BlueprintPure, Category="UniversalBars", meta=(WorldContext="WorldContextObject"))
|
||||
static UUniversalBarsSubsystem* Get(const UObject* WorldContextObject);
|
||||
|
||||
// Spawn a HUD widget, add it to the viewport, and make it the active HUD.
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars")
|
||||
UUniversalHUDWidget* CreateHUD(APlayerController* Owner, TSubclassOf<UUniversalHUDWidget> HUDClass, int32 ZOrder = 0);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars")
|
||||
void RegisterHUD(UUniversalHUDWidget* InHUD);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars")
|
||||
void UnregisterHUD(UUniversalHUDWidget* InHUD);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="UniversalBars")
|
||||
UUniversalHUDWidget* GetHUD() const { return HUD.Get(); }
|
||||
|
||||
// ===== global forwarders (animated) =====
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void SetHealth(float Percent);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void SetStamina(float Percent);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void SetShield(float Percent);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void SetHunger(float Amount);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void SetWeight(float Amount);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void SetCold(float Amount);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void DamageHealth(float Delta);
|
||||
UFUNCTION(BlueprintCallable, Category="UniversalBars") void HealHealth(float Delta);
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<UUniversalHUDWidget> HUD;
|
||||
};
|
||||
36
Source/UniversalBars/Public/UniversalBarsTheme.h
Normal file
36
Source/UniversalBars/Public/UniversalBarsTheme.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright IHY. Universal Bars plugin.
|
||||
// Central palette / geometry tokens for the HUD. One DataAsset to retint every
|
||||
// bar and chip without touching widgets. Applied by UUniversalHUDWidget on construct.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "UniversalBarsTheme.generated.h"
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class UNIVERSALBARS_API UUniversalBarsTheme : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// ---- palette ----
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor HealthFill = FLinearColor(0.30f, 0.85f, 0.20f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor StaminaFill = FLinearColor(0.85f, 0.95f, 0.30f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Hunger = FLinearColor(0.90f, 0.60f, 0.20f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Weight = FLinearColor(0.82f, 0.23f, 0.23f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Cold = FLinearColor(0.40f, 0.70f, 1.00f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Shield = FLinearColor(0.65f, 0.85f, 1.00f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Border = FLinearColor(1.00f, 1.00f, 1.00f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Empty = FLinearColor(0.05f, 0.07f, 0.05f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor Delayed = FLinearColor(1.00f, 0.95f, 0.85f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor DamageFlash = FLinearColor(1.00f, 0.12f, 0.12f, 1.f);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Palette") FLinearColor HealFlash = FLinearColor(0.60f, 1.00f, 0.60f, 1.f);
|
||||
|
||||
// ---- geometry (PEAK = thick white border, soft rounding) ----
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Geometry", meta=(ClampMin="0", ClampMax="1")) float CornerRadius = 0.60f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Geometry", meta=(ClampMin="0", ClampMax="0.25")) float BorderSize = 0.10f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Geometry", meta=(ClampMin="0", ClampMax="0.2")) float InnerPadding = 0.06f;
|
||||
|
||||
// ---- status chips ----
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Chips") TArray<FLinearColor> ChipColors;
|
||||
};
|
||||
115
Source/UniversalBars/Public/UniversalHUDWidget.h
Normal file
115
Source/UniversalBars/Public/UniversalHUDWidget.h
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright IHY. Universal Bars plugin.
|
||||
// HUD container that stacks a combined health bar (HP + hunger/weight/cold
|
||||
// penalty zones) and a stamina bar. Exposes high-level, animated stat setters
|
||||
// and registers itself with UUniversalBarsSubsystem for global access.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "UniversalHUDWidget.generated.h"
|
||||
|
||||
class UUniversalBarWidget;
|
||||
class UPanelWidget;
|
||||
class UCanvasPanel;
|
||||
class UImage;
|
||||
class UUniversalBarsTheme;
|
||||
|
||||
UCLASS()
|
||||
class UNIVERSALBARS_API UUniversalHUDWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Place a WBP_UniversalBar named "HealthBar" / "StaminaBar" in the HUD WBP.
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UUniversalBarWidget> HealthBar = nullptr;
|
||||
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UUniversalBarWidget> StaminaBar = nullptr;
|
||||
|
||||
// Optional container for status chips (filled in the designer with placeholders).
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UPanelWidget> ChipsPanel = nullptr;
|
||||
|
||||
// PEAK-style markers that sit ABOVE the health bar, over their zone. Place
|
||||
// these inside a CanvasPanel ("ChipCanvas") overlaid on HealthBar; they are
|
||||
// moved every tick to track the (animated) zone boundaries.
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UCanvasPanel> ChipCanvas = nullptr;
|
||||
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UImage> ChipHeart = nullptr; // tracks usable-HP end
|
||||
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UImage> ChipHunger = nullptr; // tracks "Hunger" zone
|
||||
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UImage> ChipWeight = nullptr; // tracks "Weight" zone
|
||||
|
||||
UPROPERTY(meta=(BindWidgetOptional))
|
||||
TObjectPtr<UImage> ChipCold = nullptr; // tracks "Cold" zone (hidden if absent)
|
||||
|
||||
// Optional central palette/geometry; applied on construct when set.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD")
|
||||
TObjectPtr<UUniversalBarsTheme> Theme = nullptr;
|
||||
|
||||
// Penalty-zone colours used by the hunger/weight/cold helpers (overridden by Theme if set).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD|Colors")
|
||||
FLinearColor HungerColor = FLinearColor(0.90f, 0.60f, 0.20f, 1.f);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD|Colors")
|
||||
FLinearColor WeightColor = FLinearColor(0.82f, 0.23f, 0.23f, 1.f);
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD|Colors")
|
||||
FLinearColor ColdColor = FLinearColor(0.40f, 0.70f, 1.00f, 1.f);
|
||||
|
||||
// ===== high-level animated API =====
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void SetHealth(float Percent); // 0..1
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void SetStamina(float Percent); // 0..1
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void SetShield(float Percent); // 0..1 (overlay above HP)
|
||||
|
||||
// Penalty fractions of the full track (eat into max HP). 0 hides the zone.
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void SetHunger(float Amount);
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void SetWeight(float Amount);
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void SetCold(float Amount);
|
||||
|
||||
// Deltas (positive = heal/up, negative = damage/down) with the right flash.
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void DamageHealth(float Delta);
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void HealHealth(float Delta);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category="HUD") float GetHealth() const;
|
||||
|
||||
// Re-tint and re-shape all bars + chips from the assigned Theme.
|
||||
UFUNCTION(BlueprintCallable, Category="HUD") void ApplyTheme();
|
||||
|
||||
// ===== self-test / study driver =====
|
||||
// When on, NativeTick cycles through every stat change so you can watch the
|
||||
// animations live in PIE. Defaults ON for this study HUD — uncheck "Demo Mode"
|
||||
// in the WBP Class Defaults (or call SetDemoMode(false)) for shipping.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD|Demo")
|
||||
bool bDemoMode = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD|Demo", meta=(ClampMin="0.2"))
|
||||
float DemoStepInterval = 1.6f;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="HUD|Demo")
|
||||
void SetDemoMode(bool bEnable);
|
||||
|
||||
// Runs one demo step (damage / heal / hunger / weight / stamina). Called by
|
||||
// the demo tick, but also handy to fire manually from BP.
|
||||
UFUNCTION(BlueprintCallable, Category="HUD|Demo")
|
||||
void DemoStepNow();
|
||||
|
||||
protected:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
// Slides each chip to sit above the centre of its zone on the health bar.
|
||||
void PositionChips();
|
||||
|
||||
private:
|
||||
float DemoTimer = 0.f;
|
||||
int32 DemoStep = 0;
|
||||
bool bDemoInit = false;
|
||||
};
|
||||
Reference in New Issue
Block a user