Files
ultimate-bar-system-plugin/Source/UniversalBars/Public/UniversalBarWidget.h
Bonchellon 41a0cdef36 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>
2026-06-23 19:04:50 +03:00

158 lines
5.3 KiB
C++

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