From 41a0cdef369e2d7a6e856a0403dce893e96bc6d3 Mon Sep 17 00:00:00 2001 From: Bonchellon <64547270+bonchellon@users.noreply.github.com> Date: Tue, 23 Jun 2026 19:04:50 +0300 Subject: [PATCH] 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 --- .gitignore | 9 + Content/Components/WBP_BarHealth.uasset | Bin 0 -> 27643 bytes Content/Components/WBP_BarStamina.uasset | Bin 0 -> 27572 bytes Content/Components/WBP_HUD_InGame.uasset | Bin 0 -> 45574 bytes Content/Components/WBP_UniversalBar.uasset | Bin 0 -> 28246 bytes Content/Materials/MI_UI_Bar_Boss.uasset | Bin 0 -> 11321 bytes Content/Materials/MI_UI_Bar_Health.uasset | Bin 0 -> 5346 bytes Content/Materials/MI_UI_Bar_Injury.uasset | Bin 0 -> 12147 bytes .../Materials/MI_UI_Bar_PeakCombined.uasset | Bin 0 -> 13627 bytes Content/Materials/MI_UI_Bar_Shield.uasset | Bin 0 -> 10907 bytes Content/Materials/MI_UI_Bar_Stamina.uasset | Bin 0 -> 9674 bytes Content/Materials/M_UI_UniversalBar.uasset | Bin 0 -> 42651 bytes Content/Theme/DA_UniversalBars_Default.uasset | Bin 0 -> 1357 bytes README.md | 231 ++++++++++++ .../Private/UniversalBarWidget.cpp | 345 ++++++++++++++++++ .../Private/UniversalBarsModule.cpp | 4 + .../Private/UniversalBarsSubsystem.cpp | 59 +++ .../Private/UniversalHUDWidget.cpp | 217 +++++++++++ .../UniversalBars/Public/UniversalBarWidget.h | 157 ++++++++ .../Public/UniversalBarsSubsystem.h | 50 +++ .../UniversalBars/Public/UniversalBarsTheme.h | 36 ++ .../UniversalBars/Public/UniversalHUDWidget.h | 115 ++++++ Source/UniversalBars/UniversalBars.Build.cs | 20 + UniversalBars.uplugin | 19 + 24 files changed, 1262 insertions(+) create mode 100644 .gitignore create mode 100644 Content/Components/WBP_BarHealth.uasset create mode 100644 Content/Components/WBP_BarStamina.uasset create mode 100644 Content/Components/WBP_HUD_InGame.uasset create mode 100644 Content/Components/WBP_UniversalBar.uasset create mode 100644 Content/Materials/MI_UI_Bar_Boss.uasset create mode 100644 Content/Materials/MI_UI_Bar_Health.uasset create mode 100644 Content/Materials/MI_UI_Bar_Injury.uasset create mode 100644 Content/Materials/MI_UI_Bar_PeakCombined.uasset create mode 100644 Content/Materials/MI_UI_Bar_Shield.uasset create mode 100644 Content/Materials/MI_UI_Bar_Stamina.uasset create mode 100644 Content/Materials/M_UI_UniversalBar.uasset create mode 100644 Content/Theme/DA_UniversalBars_Default.uasset create mode 100644 README.md create mode 100644 Source/UniversalBars/Private/UniversalBarWidget.cpp create mode 100644 Source/UniversalBars/Private/UniversalBarsModule.cpp create mode 100644 Source/UniversalBars/Private/UniversalBarsSubsystem.cpp create mode 100644 Source/UniversalBars/Private/UniversalHUDWidget.cpp create mode 100644 Source/UniversalBars/Public/UniversalBarWidget.h create mode 100644 Source/UniversalBars/Public/UniversalBarsSubsystem.h create mode 100644 Source/UniversalBars/Public/UniversalBarsTheme.h create mode 100644 Source/UniversalBars/Public/UniversalHUDWidget.h create mode 100644 Source/UniversalBars/UniversalBars.Build.cs create mode 100644 UniversalBars.uplugin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4562bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# UE build artifacts +Intermediate/ +Binaries/ +DerivedDataCache/ +Saved/ +*.sln +*.suo +*.VC.db +.vs/ diff --git a/Content/Components/WBP_BarHealth.uasset b/Content/Components/WBP_BarHealth.uasset new file mode 100644 index 0000000000000000000000000000000000000000..16cd40f3e24beb879b825e78c0bb295137e32d9d GIT binary patch literal 27643 zcmeHQ31AdOx~?GsLLiW^9D*R72ojFmAwU2n$K)X7fFz(?VKUQ`j7(;dIS4@zL_|ba z6gk#&5n*NVMi5;OL|IYvt-9!{>#m5;Ma2s*P(<_ouexfcGns@zl()~f6=tgYufOU) z>#wTrnqtqD!|yoW+}u1jjIqcLjGdqyDebk#4n8^at)$48+;2VKJ-zz{BSy?vJB6_b zDjUCN&gkz8-tTmDIoo-8!|)5j3AW;#fpeFaTJC-4K%Z@Y+jk`D5W%i^eX;GHtkT1u zOqsMT{*hfzH4!ZC^@`h`D!pK9Ugy}guPobP8cDGCQ{Q|rs`Q#cyDoqE-c6H!$cQA^ zMV^x{FU@^0=U}fJZ*nUG`wk&k_MBVa%qxC!@YZ{KE^q2jSvOrtWpO^snEj=q$0nH3V)Hm%?A|$r|2iG}y|gv=NLOk-%Wj`;v(%^_ zH+yzS?+qN}FxNU=xh9W^eOH^jpgXW!E>lyP%UQ3wJWZ_d!T+{FlxnK%Y|JIadTdSy zYyP79$6W|aPEuHBs!`b!8(+(Ud8>2mCbxU4+TgXhRCkHVLmpOPGtXoD2iO{Va|CBy z9XVOHx4PP{mf0K*)xsJQhfMC%s&JakZ6h2uW$^g%P<6F&LA}XgQ7wgzT-9x>ai}h~ zZeLaDxhN|zxiw1^*45ipP*5#pCY!^<1<8b_=BrDbD+oL ztYgi)7H)=jRF_f@cG)ayp4VaKringw!%XNhyG+VL%Hlo>@=JPO(CwTSrNyep1P3rW zJgz48*Y)*FJE7d=b=R`QZ~9m`CruvJ$I0Q6gr{Nq9P-{8C)GKA`Px56>j33Wm&fL) zVZCC``%47in#I}veoy=!aVw{^B)6)BLJiw-Jarg{I2~?}%S&CS(Rs&hS{*VV8Sl>G zjQ4O44vW+#7PZ?u6E$!@%|wc4#t#?$9z5o%R+HE6(SXsTAhWxv35| z-e5%6L1!#<*wL}ayfyCG-vBh(W~Kg7N1fV1rO@{i;g;vYozVfWvXpzk%$^}7|AZ|? zT-asEJq%kKOPm(91>%Z{Z+|r)SV?9kamrg{58d7tL*Mg zhwj0UVAKK(h7y~dI({AR{ADiHV$-~H^OsWxjtBuUGjqDlZs)zz``qJ?qHm}RR5M$2 zd04Ne+h0Ow%EO&c`0ww2=Lcvc&t+2`7Q1H6us`qb3dZwQ3L9hu3lCIm zMa0DwFr5!9JB^!7DSKugT#XCPyY{AKFU|)i{3fUDL@kQBUdwF@Rd#;Dpq)Ml*K|y` zIZr@?2v~NH3Ho5U%Tg*dn4jY<2Qnss<3hI<*f`?BLwzT~hso_5oi2-;bqfU6aoTr>u;uYauUg(iyCx@y(M z-Tb;i4?hCh8mP=f2KBJZKUzEvr3pUl-^9?Am*lF?FRT_PH z*e^5D+@U`1S`#POl&?DLRD_uS>iGZzK~VScx$Mh`N#Gq_K*o!?gR8!R0s!MGfA!w* zdnBOD>Ar-y@;+7J4Zv`-$9}xk4rTDr@^#psqae6b8+Lyz~D0IR5F!kH`Tb=+s^a0>!DD9oBgS= z+Lp*X~c~`@Hls0eI4pkVTsdYvo>LXV4ZJ$Z4Km`s=A$C zmsu4Pmdj@K+X+2Rwbj&mSl462?6BEXr_+;Ta?Cfm*`dr&&X*vqi?eR((_u&;m+_+ikKW4lO z&6RsxQYUQO9@k({6FOTPyZ&OxK`1n8_^^`MZ6~4M3Uz@WxHr?Y3zR&yDh_Ai3RRM6mDVXJ35k92+tlluy1Jv&jg zVJF(Fq`pnauwgqU7;Bm+g~u=h29#d_TJqsw?XZi#VTB?%H|FG?Fo!18b}5^(5rBQ z3yK}=o`)7~#aJolss8y5WMUt^y(kwG3|Z{s1*-_wy~y=kGD=&3lZq+i(#*>4PAV>} zsw~9nqN+?a&7;0vO+%=K9er;A50#q3m6;r>okfIC=&wyI;Dj8;-Q~fY-u>V%TBq_f zF1QgQi3vbem7cP2){i1*$6Wz`~t7X27OG^+Z&@^h`(l9pO6Ru7E^Eku!9~=1?ru3FfO7#f(*w z>K>_7dp(ND-e_uaD>SCqsj8~f^D$Vl$%Tvwkv$RZa~cz=+*GG1#l9g`si0bX5Tdpy zgwpIru{vFPK3de&O-B@k;u?{qnF-M)o-B&nMsB7MgB}8#=b>g2D-ps%A7$QZ8gG(( zHcaw6MPe=U2+S8K|0UHC`P1|uYn_%3xJ$q_LU4yZyK{QInN?hMU$)%U74Z ziB?C78>@feB?|eASxyL43pov~Q)uoAJf}mcbvE+aS`}?ZgDPG!7dI?-IF(P+>r72b zwaT4|DA&=7$VTg7ZeX`!@zRx3Fh4M)PBrxweUWoKk32 zXtt?1X#K006dG=2+X+r`lY~YZR1?fJ9f8kr4n~44rPNXApcN3NNhFWvLq_2lh$Go5 zO-Ge7T5J6T*D{cb&xUG$A+`Q2DrNLAF4PFMlW+rEvYNca>8a%wuc6UjfiocZ&`OP# z#N>hefe-gF7ezFu;_^Buf^nNFG*|Z4ix8vQT*8YvTD3~y$EDCJd%jI=6y8bEiPs>L zg9c63`O_OtR0}sXd|76w?ZBAnf&JyRkt^ne#ctdWY%GgJ$lo6>6&1 zf&zN5+ccj#KWQIP-a{rM>sQMsS0puQne$*aQU4_gMB5a8)cN$FMH_?^5n=q$kjz?I9o0b z3D25)`&>Ix@6#1$-Jj2jFGrp=C;s^znBgT6jZ+LONhC}kFnsc$9kgHu$2-k;E6YWt zFXv&ZBHr2%@iv5rw>Sj6 zPf6bOA>e&NcsB*XgNGnpuVw6G!doq_gW2~+QHBifsgO>Er(vBMPx5lA_An(uIu#ze z2GXhUumD3k72Z;gb!xmT+QC~R%04GOUmFB(senma!ERRt!IL~o+rtm84uU6jAbsgW z!jso}dA}#PsLrt3R>ch#^LBBKcaxzcYRcDEAii8}>*-<98;l;C8dy8{eJJ!!=y-yk zq3^}p&VWKTPJ@{)ddN-F-K?tcm`6RuQI{ENWVwGqY@;xT5pQ{iTr^wo) zw^P*pPDc;^f)^4$ZbI+N1Z3@Rwlp;5X2xTXf=$GlR9>8rB}YgYD1*8PHn|fhql_O3P36 z9SS`>1k|S$;wFXii^TONiyoux>-Ol46V<*UBz+6(#0?$_JS*&XNJkH95vuW3llSe> z+b!C{#+|QqH08rSJUy%v4Q;mhnhtsq3canO<}n>D^miH4yU@H(x=GBu)?_3NeMx)r zNf+1CgDoXJ_=}`x4?<5*M_z|Q4}1!@=Z_$8k4M=^(odmV!~yh^uc%SLoWI%(>Z?hcMH6kK1qI1{C zE|F2)x<m8c#blKh0tyz*CqZj&+DJgvA(|_p(96XGa51 znmlyhAuPOOr_K>wBD)g8&S=)b5Ej-UJgj5KaMGG#F_p9Mn2tSTQ?fe!W{RnETtlza zD{j0q;{5D~_Vk|m>e0bz)vje-BK!0`r(gde7YvObHavY)#^^C)Gjno{dHDr}lgcY9 zr%k{3k{M=;YOSfYUF!CD=Ql2BT6pF1t5#gS@|sm^Zd$u;{f6J&e9NZ0HgEaE-CO^- zZTrJJc0RJ}(Z~Mu%(Hv{{M^3hU)cZG1FyaQw>RE=>+gr&`|tPvb@+o1KRWWomtTGT z&9UR(es=?cX)6U;By-&waX0~s$YhJxau+D)ri z{Qu?8J%1`$*KqH_g!>+Oy}E1KqvOiDf56_Y3A=mhh>gwcs-~?=UdbtKW>0NtX73Ff zJLK`W^6NZ~eM5}i-A5l7)#F5jt(hHvZTCszpq96Z3y$~QdH2Z|KOZ}M#C?J7#J2)` zu)6-(j%IfA;t#0hz87}-)5E)(J{y%VAuemZeSgDu`Io%)wZ?6&QP?Z=wg_E}3_?y}_bEA!t8`!o8gH$6=BqchxOYsuVRh`2!-wBCt9-(Q^vAa+k4&tLoV$A1 zu823jT@ZHIeA`zQjvcp8x&P+B++P3rI!A2Xmgiab&i|Zp#}D&f-g^J@@6Grydeg?w z-S>SF?s~H}!8U)*vx7HGd7^NnT6EPVVNaEQUov9Nrd7{v{_bBdId(+GZ`#;lRs4TG z|Ixv(+6Q_xza__LxO8r47CT~z89teFTHtN`W@G_Vq#FUUpe%Cg74@=NpF1# zJ-YX8A#Gi#+cb$nY$5Qq>E9oPB8cHD;v2+T3)TXM?|k8qvWiQ}jI?dkRYzN&uv*|d z2DHsmm_(+ek4+zwHY%0REc0yHe1BYDdW=bP4F0!`b>n%4xYwWtnt{rzbHr6XyOcTU z$;Tx6&S7=j zfb>^QdbBYk$88`BRI`Olwmz5Tu|}%pBrW@K{hAJ5+EKLb=jf+dJ4&=;Bn~{pKfI!W z+q<4PGSJROO&4vIOI`W-=qhTo_A~>98L8QNovfAQXZ55Ncv%BsL

5q$6-)Wy1;5 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 literal 0 HcmV?d00001 diff --git a/Content/Components/WBP_BarStamina.uasset b/Content/Components/WBP_BarStamina.uasset new file mode 100644 index 0000000000000000000000000000000000000000..294e49ff00733c9a2df62c765521cf1488faab02 GIT binary patch literal 27572 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 Q%_@)F@!aTS0RaC009@=k5&!@I literal 0 HcmV?d00001 diff --git a/Content/Components/WBP_HUD_InGame.uasset b/Content/Components/WBP_HUD_InGame.uasset new file mode 100644 index 0000000000000000000000000000000000000000..b5c1dd9993949d3d7ed16f039da390d51b89053a GIT binary patch literal 45574 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 literal 0 HcmV?d00001 diff --git a/Content/Components/WBP_UniversalBar.uasset b/Content/Components/WBP_UniversalBar.uasset new file mode 100644 index 0000000000000000000000000000000000000000..3e675f3fc87a3b11bd317066e3d10c590034e98b GIT binary patch literal 28246 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 literal 0 HcmV?d00001 diff --git a/Content/Materials/MI_UI_Bar_Boss.uasset b/Content/Materials/MI_UI_Bar_Boss.uasset new file mode 100644 index 0000000000000000000000000000000000000000..94396225a7576e842c242485c21332ce056efa2b GIT binary patch literal 11321 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 literal 0 HcmV?d00001 diff --git a/Content/Materials/MI_UI_Bar_Shield.uasset b/Content/Materials/MI_UI_Bar_Shield.uasset new file mode 100644 index 0000000000000000000000000000000000000000..db3286590b81b03fa2441aba22102a40c6f4c603 GIT binary patch literal 10907 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 G`TqfB&HWSr literal 0 HcmV?d00001 diff --git a/Content/Theme/DA_UniversalBars_Default.uasset b/Content/Theme/DA_UniversalBars_Default.uasset new file mode 100644 index 0000000000000000000000000000000000000000..6077b724bc3bc9461a7f87e3842d23d3c4e5fd64 GIT binary patch literal 1357 zcmX@utTpfZ|Ns9Jm>C$jm>3v-0cjAhC@EZB%M|f9Mxm%a?6Su`#udvi@vt&5FaqV? zW^8!!(rL;U?pub79Cy3vX#mCafLK2?FS9JQs5mjlDY2+nKO`eHH&x%oF&;%Y-X%3H zu{5UysGfm=6No2+j0A#ypzVr4+T7LI$->ye$l1-z+0?|<%+tx#L~pl z0(pgx8pAOZ+lpw0);AOm6i4j>)*P%-`$^W;u?^7 zAPjO8D+44#f1!&r4iDPAydPs&P7E@AlLscR>OB!?0a41C^2 z$4HO1VzUxeHlUf#If=!^3?=0i6D5H>oUwfS{|Pq^paQ5L7>eCHM1_HT6sLew3d7nJ zx1RuIz;OVI2M|yMVuA?*8u!3Rfq;L&fCbr!4q)ooktC4P16&joF>nr$0rD;ga04+f zn8m;Via1;VmXJX5=w>170VOnC0AvP^aKanjJwOlrVS$(mG6Sz-O`rl;x0I_3nacYTEXUT@edScU^oaY7^i<*+Ms8B New C++ classes (a new module) cannot be hot-loaded by Live Coding — do a full +> editor build the first time. + +--- + +## 2. What's inside + +``` +/UniversalBars/ + Materials/ + M_UI_UniversalBar master material (Domain=UI, Translucent, procedural SDF) + MI_UI_Bar_Health solo green bar + MI_UI_Bar_Stamina solo yellow-green bar + MI_UI_Bar_Injury striped "broken HP" bar + MI_UI_Bar_Shield blue bar + shield overlay + MI_UI_Bar_Boss segmented boss bar + MI_UI_Bar_PeakCombined HP + hunger + weight in ONE bar (PEAK capacity) + Components/ + WBP_UniversalBar one reusable bar (parent: UUniversalBarWidget) + WBP_BarHealth combined-capacity variant + WBP_BarStamina stamina variant + WBP_HUD_InGame ready HUD: health + stamina + status chips + Theme/ put DA_UniversalBars_* theme assets here + +C++ (module UniversalBars): + UUniversalBarWidget one animated bar + UUniversalHUDWidget HUD container with high-level stat setters + UUniversalBarsSubsystem global access point (UWorldSubsystem) + UUniversalBarsTheme palette / geometry / chip-colour DataAsset +``` + +--- + +## 3. Quick start + +### Blueprint — easiest path (global API, no widget reference) + +1. On `BeginPlay` (player controller / HUD), call + `Universal Bars Subsystem → Create HUD` with `HUD Class = WBP_HUD_InGame`. +2. From **anywhere** in BP: + +``` +Get World Subsystem (UniversalBarsSubsystem) + → Set Health (0.62) // 0..1, animates + flashes automatically + → Set Stamina (0.8) + → Set Hunger (0.16) // penalty zone eats into max HP (combined bar) + → Set Weight (0.12) + → Damage Health (0.2) // red flash + instant drop + lag tail + → Heal Health (0.25) // green flash + smooth rise +``` + +Direction is detected for you: a decrease plays the damage animation, an +increase plays the heal animation. + +### C++ + +```cpp +#include "UniversalBarsSubsystem.h" + +if (UUniversalBarsSubsystem* Bars = UUniversalBarsSubsystem::Get(this)) +{ + Bars->SetHealth(0.62f); + Bars->SetHunger(0.16f); + Bars->DamageHealth(0.2f); +} +``` + +### Driving a single bar directly + +Place a `WBP_UniversalBar` (or a preset variant) anywhere and call: + +```cpp +UUniversalBarWidget* Bar = ...; +Bar->SetFillPercent(0.5f); // animated +Bar->SetShieldPercent(0.25f); +Bar->SetSegmentCount(10); // boss segments +Bar->SetPenaltyZone("Hunger", 0.16f, FLinearColor(0.9f,0.6f,0.2f,1)); // animated zone +Bar->PlayDamageFlash(); // manual flash +``` + +--- + +## 4. Animation model (same as the HTML prototype) + +| Change | Bright fill | Lag tail | Flash | +|---|---|---|---| +| **Decrease / damage** | snaps down instantly | holds `DelayedDelay`s, then drains at `DelayedFallSpeed` | red (`DamageFlashColor`) | +| **Increase / heal** | eases up at `FillRiseSpeed` | jumps to target (lighter "incoming" band) | green (`HealFlashColor`) | +| **value < `LowThreshold`** | — | — | border pulses (low-value warning) | +| **penalty zone change** | — | zone width eases at `ZoneInterpSpeed` | — | + +All tuning fields live on `UUniversalBarWidget` (Bar|Anim category). Set +`bAutoFlashOnChange=false` to drive flashes manually. + +Under the hood the material exposes `FillPercent`, `DelayedFillPercent`, +`FlashAmount`, `LowHPPulseAmount` — the widget tweens these every tick via a +Dynamic Material Instance. + +--- + +## 5. Combined "capacity" bar (PEAK) + +One bar = current value (bright) + depleted-but-recoverable zone (dark) + +striped penalty zones eating max from the right: + +- `MaxFillPercent` — right edge of the usable region (`1 - sum(penalties)`). +- `PenaltyCount` + `PenaltyEnd1..4` + `PenaltyColor1..4` — up to 4 striped zones. + +You never set these by hand — `SetHunger / SetWeight / SetCold` (HUD) or +`SetPenaltyZone(Id, Width, Color)` (bar) manage them and animate the widths. +Set a penalty to `0` to retract its zone. + +`PenaltyCount = 0` → the same material is a plain solo bar. + +--- + +## 6. Material parameter reference + +**Scalars:** `FillPercent`, `DelayedFillPercent`, `ShieldPercent`, +`MaxFillPercent`, `PenaltyCount`, `PenaltyEnd1..4`, `BarOpacity`, +`BackgroundOpacity`, `CornerRadius`, `BorderSize`, `InnerPadding`, +`EdgeSoftness`, `BarAspect`, `FlashAmount`, `LowHPPulseAmount`, `PulseSpeed`, +`StripeAmount`, `StripeScale`, `StripeSpeed`, `SegmentCount`, +`SegmentDividerSize`, `SegmentDividerOpacity`, `UseVerticalFill`, `InvertFill`, +`NoiseAmount`, `TimeOffset`. + +**Vectors:** `FillColor`, `DelayedFillColor`, `ShieldColor`, `BackgroundColor`, +`BorderColor`, `EmptyColor`, `FlashColor`, `StripeColorA`, `StripeColorB`, +`PenaltyColor1..4`. + +`BarAspect` must roughly match the on-screen width/height ratio so the rounded +corners stay circular (default 8 ≈ a 600×75 bar). + +--- + +## 7. Theme + +`UUniversalBarsTheme` (DataAsset) centralises palette + geometry + chip colours. +Create one under `/UniversalBars/Theme/` (right-click → Miscellaneous → Data +Asset → Universal Bars Theme), then assign it to `WBP_HUD_InGame`'s `Theme` +property. `UUniversalHUDWidget::ApplyTheme()` runs on construct and retints both +bars and all chips. Per-preset look still lives in the `MI_UI_Bar_*` instances; +the theme is the HUD-level override. + +--- + +## 8. Presets (recommended values) + +| Preset | Fill | Notes | +|---|---|---| +| Health | `(0.30,0.85,0.20)` | white border, low-HP pulse, damage lag | +| Stamina | `(0.85,0.95,0.30)` | thinner, no pulse | +| Injury | `(0.75,0.20,0.20)` + `StripeAmount=1` | striped "broken" HP | +| Shield | `(0.30,0.70,1.0)` + `ShieldPercent` | blue + overlay | +| Boss | `(0.85,0.15,0.15)` + `SegmentCount=10` | wide, segmented, less rounded | +| PeakCombined | green + Hunger/Weight penalties | one-bar HP+hunger+weight | + +Keep `BorderColor` pure white and `BorderSize` 0.06–0.10 for the toy/cartoon +look; `CornerRadius` 0.5–0.7 for horizontal bars. + +--- + +## 9. Demo / study mode (WBP_HUD_InGame) + +`WBP_HUD_InGame` ships with **Demo Mode ON** (`UUniversalHUDWidget::bDemoMode`). +While on, the HUD's `NativeTick` runs a 10-step cycle every `DemoStepInterval` +(1.6 s) so you can watch every animation live in PIE: + +``` +0 DamageHealth(0.25) red flash + instant drop + lag tail +1 SetStamina(0.30) stamina drop +2 HealHealth(0.20) green flash + smooth rise +3 SetHunger(0.18) amber zone eases in from the right +4 SetWeight(0.14) red zone eases in +5 SetStamina(0.95) stamina refill +6 HealHealth(0.30) +7 SetHunger(0.0) hunger zone retracts +8 SetWeight(0.0) weight zone retracts +9 DamageHealth(0.45) big hit -> low-HP border pulse +``` + +To watch: add the HUD (`Subsystem → Create HUD`, or just drop it in any widget / +PIE) and press Play. The cycle restarts each time the widget constructs. + +**Turn it off for shipping:** open `WBP_HUD_InGame` → Class Defaults → uncheck +**Demo Mode**, or call `SetDemoMode(false)`. Read `DemoStepNow()` in +`UniversalHUDWidget.cpp` to see exactly which API call drives each animation. + +## 9b. Status chips (PEAK markers) + +In `WBP_HUD_InGame` the chips live in a `CanvasPanel` (`ChipCanvas`) **overlaid on +the health bar**, not in a row below it. Each tick `UUniversalHUDWidget::PositionChips()` +slides them to sit just above the centre of their zone, using +`UUniversalBarWidget::GetUsableEnd()` / `GetZoneCenter("Hunger"|"Weight"|"Cold")`: + +- `ChipHeart` → the usable-HP / penalty boundary +- `ChipHunger` → centre of the hunger zone (hidden until `SetHunger > 0`) +- `ChipWeight` → centre of the weight zone +- `ChipCold` → centre of the cold zone + +So when hunger/weight grow or shrink, the chips ride along with the zone edges. +Add your own marker by placing an `Image` in `ChipCanvas` and binding it (or extend +`PositionChips`). Swap the placeholder tints for real icon textures. + +## 10. Notes / limits + +- The bar is a UI-domain material — it only renders inside UMG/Slate (not on + meshes). Verify visuals in the UMG designer or content-browser thumbnail. +- Up to 4 penalty zones are drawn by the material in a single draw call; the + widget warns if you push more. +- `SetFillPercent` clamps to 0..1. Feed it `CurrentHP / MaxHP`, etc. diff --git a/Source/UniversalBars/Private/UniversalBarWidget.cpp b/Source/UniversalBars/Private/UniversalBarWidget.cpp new file mode 100644 index 0000000..012b6be --- /dev/null +++ b/Source/UniversalBars/Private/UniversalBarWidget.cpp @@ -0,0 +1,345 @@ +// Copyright IHY. Universal Bars plugin. +#include "UniversalBarWidget.h" +#include "Components/Image.h" +#include "Materials/MaterialInstanceDynamic.h" + +UUniversalBarWidget::UUniversalBarWidget(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + +void UUniversalBarWidget::NativeConstruct() +{ + Super::NativeConstruct(); + EnsureMID(); + if (BarMID) + { + BarMID->SetScalarParameterValue(TEXT("FillPercent"), DisplayFill); + BarMID->SetScalarParameterValue(TEXT("DelayedFillPercent"), DelayedFill); + } + PushZones(true); +} + +UMaterialInstanceDynamic* UUniversalBarWidget::EnsureMID() +{ + if (BarMID || !BarImage) + { + return BarMID; + } + BarMID = BarImage->GetDynamicMaterial(); + return BarMID; +} + +void UUniversalBarWidget::SetFillPercent(float NewValue, bool bAnimate) +{ + EnsureMID(); + NewValue = FMath::Clamp(NewValue, 0.f, 1.f); + + if (!bAnimate) + { + TargetFill = DisplayFill = DelayedFill = NewValue; + if (BarMID) + { + BarMID->SetScalarParameterValue(TEXT("FillPercent"), DisplayFill); + BarMID->SetScalarParameterValue(TEXT("DelayedFillPercent"), DelayedFill); + } + return; + } + + const bool bDown = NewValue < TargetFill - KINDA_SMALL_NUMBER; + const bool bUp = NewValue > TargetFill + KINDA_SMALL_NUMBER; + TargetFill = NewValue; + + if (bDown) + { + // instant bright drop; the lighter tail holds, then drains (damage lag) + DisplayFill = NewValue; + DelayTimer = DelayedDelay; + if (BarMID) + { + BarMID->SetScalarParameterValue(TEXT("FillPercent"), DisplayFill); + } + if (bAutoFlashOnChange) + { + PlayDamageFlash(); + } + } + else if (bUp) + { + // show the incoming amount immediately as the lighter band; bright fill eases up into it + DelayedFill = FMath::Max(DelayedFill, NewValue); + if (BarMID) + { + BarMID->SetScalarParameterValue(TEXT("DelayedFillPercent"), DelayedFill); + } + if (bAutoFlashOnChange) + { + PlayHealFlash(); + } + } +} + +void UUniversalBarWidget::SetShieldPercent(float NewValue) +{ + EnsureMID(); + if (BarMID) + { + BarMID->SetScalarParameterValue(TEXT("ShieldPercent"), FMath::Clamp(NewValue, 0.f, 1.f)); + } +} + +void UUniversalBarWidget::PlayDamageFlash() +{ + PlayFlash(DamageFlashColor); +} + +void UUniversalBarWidget::PlayHealFlash() +{ + PlayFlash(HealFlashColor); +} + +void UUniversalBarWidget::PlayFlash(FLinearColor Color) +{ + EnsureMID(); + PendingFlashColor = Color; + if (BarMID) + { + BarMID->SetVectorParameterValue(TEXT("FlashColor"), Color); + } + FlashAmount = 1.f; + bFlashing = true; +} + +void UUniversalBarWidget::SetBarColors(FLinearColor Fill, FLinearColor Empty, FLinearColor Border, FLinearColor Delayed) +{ + EnsureMID(); + if (!BarMID) + { + return; + } + BarMID->SetVectorParameterValue(TEXT("FillColor"), Fill); + BarMID->SetVectorParameterValue(TEXT("EmptyColor"), Empty); + BarMID->SetVectorParameterValue(TEXT("BorderColor"), Border); + BarMID->SetVectorParameterValue(TEXT("DelayedFillColor"), Delayed); +} + +void UUniversalBarWidget::SetSegmentCount(int32 Count) +{ + EnsureMID(); + if (!BarMID) + { + return; + } + BarMID->SetScalarParameterValue(TEXT("SegmentCount"), static_cast(FMath::Max(0, Count))); + BarMID->SetScalarParameterValue(TEXT("SegmentDividerOpacity"), Count > 0 ? 0.85f : 0.f); +} + +void UUniversalBarWidget::SetPenaltyZone(FName Id, float Width, FLinearColor Color) +{ + Width = FMath::Clamp(Width, 0.f, 1.f); + for (FBarZone& Zone : PenaltyZones) + { + if (Zone.Id == Id) + { + Zone.Width = Width; + Zone.Color = Color; + return; // tick eases DisplayWidth toward the new Width + } + } + FBarZone& NewZone = PenaltyZones[PenaltyZones.AddDefaulted()]; + NewZone.Id = Id; + NewZone.Width = Width; + NewZone.Color = Color; +} + +void UUniversalBarWidget::SetPenaltyZoneWidth(FName Id, float Width) +{ + Width = FMath::Clamp(Width, 0.f, 1.f); + for (FBarZone& Zone : PenaltyZones) + { + if (Zone.Id == Id) + { + Zone.Width = Width; + return; + } + } +} + +void UUniversalBarWidget::RemovePenaltyZone(FName Id) +{ + // Ease to zero, then drop on next tick when fully collapsed. + for (FBarZone& Zone : PenaltyZones) + { + if (Zone.Id == Id) + { + Zone.Width = 0.f; + return; + } + } +} + +void UUniversalBarWidget::ClearPenaltyZones() +{ + for (FBarZone& Zone : PenaltyZones) + { + Zone.Width = 0.f; + } +} + +void UUniversalBarWidget::PushZones(bool bImmediate) +{ + EnsureMID(); + if (!BarMID) + { + return; + } + + const int32 Count = FMath::Min(PenaltyZones.Num(), MaxMaterialZones); + float Total = 0.f; + for (int32 i = 0; i < Count; ++i) + { + if (bImmediate) + { + PenaltyZones[i].DisplayWidth = PenaltyZones[i].Width; + } + Total += FMath::Clamp(PenaltyZones[i].DisplayWidth, 0.f, 1.f); + } + Total = FMath::Clamp(Total, 0.f, 1.f); + + const float MaxFill = 1.f - Total; + BarMID->SetScalarParameterValue(TEXT("MaxFillPercent"), MaxFill); + BarMID->SetScalarParameterValue(TEXT("PenaltyCount"), static_cast(Count)); + + float Bound = MaxFill; + static const TCHAR* EndNames[MaxMaterialZones] = { TEXT("PenaltyEnd1"), TEXT("PenaltyEnd2"), TEXT("PenaltyEnd3"), TEXT("PenaltyEnd4") }; + static const TCHAR* ColorNames[MaxMaterialZones] = { TEXT("PenaltyColor1"), TEXT("PenaltyColor2"), TEXT("PenaltyColor3"), TEXT("PenaltyColor4") }; + for (int32 i = 0; i < MaxMaterialZones; ++i) + { + if (i < Count) + { + Bound = FMath::Min(1.f, Bound + FMath::Clamp(PenaltyZones[i].DisplayWidth, 0.f, 1.f)); + BarMID->SetScalarParameterValue(EndNames[i], Bound); + BarMID->SetVectorParameterValue(ColorNames[i], PenaltyZones[i].Color); + } + else + { + BarMID->SetScalarParameterValue(EndNames[i], 1.f); + } + } +} + +float UUniversalBarWidget::GetUsableEnd() const +{ + float Total = 0.f; + const int32 N = FMath::Min(PenaltyZones.Num(), MaxMaterialZones); + for (int32 i = 0; i < N; ++i) + { + Total += FMath::Clamp(PenaltyZones[i].DisplayWidth, 0.f, 1.f); + } + return FMath::Clamp(1.f - Total, 0.f, 1.f); +} + +float UUniversalBarWidget::GetZoneCenter(FName Id) const +{ + float Edge = GetUsableEnd(); + const int32 N = FMath::Min(PenaltyZones.Num(), MaxMaterialZones); + for (int32 i = 0; i < N; ++i) + { + const float W = FMath::Clamp(PenaltyZones[i].DisplayWidth, 0.f, 1.f); + if (PenaltyZones[i].Id == Id) + { + return Edge + W * 0.5f; + } + Edge += W; + } + return -1.f; +} + +void UUniversalBarWidget::SetScalar(FName Param, float Value) +{ + EnsureMID(); + if (BarMID) { BarMID->SetScalarParameterValue(Param, Value); } +} + +void UUniversalBarWidget::SetVector(FName Param, FLinearColor Value) +{ + EnsureMID(); + if (BarMID) { BarMID->SetVectorParameterValue(Param, Value); } +} + +void UUniversalBarWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + Super::NativeTick(MyGeometry, InDeltaTime); + if (!BarMID) + { + return; + } + + // bright fill eases toward target (smooth rise on heal; already snapped on damage) + if (!FMath::IsNearlyEqual(DisplayFill, TargetFill, 1e-3f)) + { + DisplayFill = FMath::FInterpTo(DisplayFill, TargetFill, InDeltaTime, FillRiseSpeed); + BarMID->SetScalarParameterValue(TEXT("FillPercent"), DisplayFill); + } + + // lighter "lag" tail: holds, then drains toward target (damage). Never below the bright fill. + const float DelayedTarget = FMath::Max(TargetFill, DisplayFill); + if (!FMath::IsNearlyEqual(DelayedFill, DelayedTarget, 1e-3f)) + { + if (DelayTimer > 0.f) + { + DelayTimer -= InDeltaTime; + } + else + { + DelayedFill = FMath::FInterpTo(DelayedFill, DelayedTarget, InDeltaTime, DelayedFallSpeed); + BarMID->SetScalarParameterValue(TEXT("DelayedFillPercent"), DelayedFill); + } + } + + // flash decay + if (bFlashing) + { + FlashAmount = FMath::FInterpTo(FlashAmount, 0.f, InDeltaTime, FlashSpeed); + BarMID->SetScalarParameterValue(TEXT("FlashAmount"), FlashAmount); + if (FlashAmount < 0.01f) + { + FlashAmount = 0.f; + bFlashing = false; + BarMID->SetScalarParameterValue(TEXT("FlashAmount"), 0.f); + } + } + + // low-value pulse + const float PulseTarget = (TargetFill < LowThreshold) ? 1.f : 0.f; + if (!FMath::IsNearlyEqual(PulseAmount, PulseTarget, 1e-3f)) + { + PulseAmount = FMath::FInterpTo(PulseAmount, PulseTarget, InDeltaTime, 6.f); + BarMID->SetScalarParameterValue(TEXT("LowHPPulseAmount"), PulseAmount); + } + + // penalty zone widths ease toward target; drop fully-collapsed removed zones + bool bZonesDirty = false; + bool bNeedCompact = false; + for (FBarZone& Zone : PenaltyZones) + { + if (!FMath::IsNearlyEqual(Zone.DisplayWidth, Zone.Width, 1e-4f)) + { + Zone.DisplayWidth = FMath::FInterpTo(Zone.DisplayWidth, Zone.Width, InDeltaTime, ZoneInterpSpeed); + bZonesDirty = true; + } + if (Zone.Width <= 0.f && Zone.DisplayWidth <= 1e-3f) + { + bNeedCompact = true; + } + } + if (bZonesDirty) + { + PushZones(false); + } + if (bNeedCompact) + { + PenaltyZones.RemoveAll([](const FBarZone& Z) { return Z.Width <= 0.f && Z.DisplayWidth <= 1e-3f; }); + PushZones(false); + } +} diff --git a/Source/UniversalBars/Private/UniversalBarsModule.cpp b/Source/UniversalBars/Private/UniversalBarsModule.cpp new file mode 100644 index 0000000..a8d70a0 --- /dev/null +++ b/Source/UniversalBars/Private/UniversalBarsModule.cpp @@ -0,0 +1,4 @@ +// Copyright IHY. Universal Bars plugin. +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FDefaultModuleImpl, UniversalBars); diff --git a/Source/UniversalBars/Private/UniversalBarsSubsystem.cpp b/Source/UniversalBars/Private/UniversalBarsSubsystem.cpp new file mode 100644 index 0000000..71aee2d --- /dev/null +++ b/Source/UniversalBars/Private/UniversalBarsSubsystem.cpp @@ -0,0 +1,59 @@ +// Copyright IHY. Universal Bars plugin. +#include "UniversalBarsSubsystem.h" +#include "UniversalHUDWidget.h" +#include "Blueprint/UserWidget.h" +#include "Engine/World.h" +#include "GameFramework/PlayerController.h" + +UUniversalBarsSubsystem* UUniversalBarsSubsystem::Get(const UObject* WorldContextObject) +{ + if (!WorldContextObject) + { + return nullptr; + } + if (const UWorld* World = WorldContextObject->GetWorld()) + { + return World->GetSubsystem(); + } + return nullptr; +} + +UUniversalHUDWidget* UUniversalBarsSubsystem::CreateHUD(APlayerController* Owner, TSubclassOf HUDClass, int32 ZOrder) +{ + if (!HUDClass) + { + return nullptr; + } + UUniversalHUDWidget* Widget = CreateWidget(Owner ? Cast(Owner) : GetWorld()->GetFirstPlayerController(), HUDClass); + if (Widget) + { + Widget->AddToViewport(ZOrder); + RegisterHUD(Widget); // also registered in NativeConstruct; idempotent + } + return Widget; +} + +void UUniversalBarsSubsystem::RegisterHUD(UUniversalHUDWidget* InHUD) +{ + if (InHUD) + { + HUD = InHUD; + } +} + +void UUniversalBarsSubsystem::UnregisterHUD(UUniversalHUDWidget* InHUD) +{ + if (HUD.Get() == InHUD) + { + HUD.Reset(); + } +} + +void UUniversalBarsSubsystem::SetHealth(float Percent) { if (UUniversalHUDWidget* H = HUD.Get()) { H->SetHealth(Percent); } } +void UUniversalBarsSubsystem::SetStamina(float Percent) { if (UUniversalHUDWidget* H = HUD.Get()) { H->SetStamina(Percent); } } +void UUniversalBarsSubsystem::SetShield(float Percent) { if (UUniversalHUDWidget* H = HUD.Get()) { H->SetShield(Percent); } } +void UUniversalBarsSubsystem::SetHunger(float Amount) { if (UUniversalHUDWidget* H = HUD.Get()) { H->SetHunger(Amount); } } +void UUniversalBarsSubsystem::SetWeight(float Amount) { if (UUniversalHUDWidget* H = HUD.Get()) { H->SetWeight(Amount); } } +void UUniversalBarsSubsystem::SetCold(float Amount) { if (UUniversalHUDWidget* H = HUD.Get()) { H->SetCold(Amount); } } +void UUniversalBarsSubsystem::DamageHealth(float Delta) { if (UUniversalHUDWidget* H = HUD.Get()) { H->DamageHealth(Delta); } } +void UUniversalBarsSubsystem::HealHealth(float Delta) { if (UUniversalHUDWidget* H = HUD.Get()) { H->HealHealth(Delta); } } diff --git a/Source/UniversalBars/Private/UniversalHUDWidget.cpp b/Source/UniversalBars/Private/UniversalHUDWidget.cpp new file mode 100644 index 0000000..0131e8c --- /dev/null +++ b/Source/UniversalBars/Private/UniversalHUDWidget.cpp @@ -0,0 +1,217 @@ +// Copyright IHY. Universal Bars plugin. +#include "UniversalHUDWidget.h" +#include "UniversalBarWidget.h" +#include "UniversalBarsSubsystem.h" +#include "UniversalBarsTheme.h" +#include "Components/Image.h" +#include "Components/PanelWidget.h" +#include "Components/CanvasPanel.h" +#include "Components/CanvasPanelSlot.h" + +void UUniversalHUDWidget::NativeConstruct() +{ + Super::NativeConstruct(); + ApplyTheme(); + if (UUniversalBarsSubsystem* Sub = UUniversalBarsSubsystem::Get(this)) + { + Sub->RegisterHUD(this); + } +} + +void UUniversalHUDWidget::ApplyTheme() +{ + if (!Theme) + { + return; + } + + HungerColor = Theme->Hunger; + WeightColor = Theme->Weight; + ColdColor = Theme->Cold; + + auto StyleBar = [this](UUniversalBarWidget* Bar, FLinearColor Fill) + { + if (!Bar) + { + return; + } + Bar->SetBarColors(Fill, Theme->Empty, Theme->Border, Theme->Delayed); + Bar->SetVector(TEXT("ShieldColor"), Theme->Shield); + Bar->DamageFlashColor = Theme->DamageFlash; + Bar->HealFlashColor = Theme->HealFlash; + Bar->SetScalar(TEXT("CornerRadius"), Theme->CornerRadius); + Bar->SetScalar(TEXT("BorderSize"), Theme->BorderSize); + Bar->SetScalar(TEXT("InnerPadding"), Theme->InnerPadding); + }; + StyleBar(HealthBar, Theme->HealthFill); + StyleBar(StaminaBar, Theme->StaminaFill); + + if (ChipsPanel && Theme->ChipColors.Num() > 0) + { + const int32 N = ChipsPanel->GetChildrenCount(); + for (int32 i = 0; i < N; ++i) + { + if (UImage* Img = Cast(ChipsPanel->GetChildAt(i))) + { + Img->SetBrushTintColor(FSlateColor(Theme->ChipColors[i % Theme->ChipColors.Num()])); + } + } + } + + // Tracking chips: tint by what they represent. + if (ChipHeart) { ChipHeart->SetBrushTintColor(FSlateColor(Theme->HealthFill)); } + if (ChipHunger) { ChipHunger->SetBrushTintColor(FSlateColor(Theme->Hunger)); } + if (ChipWeight) { ChipWeight->SetBrushTintColor(FSlateColor(Theme->Weight)); } + if (ChipCold) { ChipCold->SetBrushTintColor(FSlateColor(Theme->Cold)); } +} + +void UUniversalHUDWidget::NativeDestruct() +{ + if (UUniversalBarsSubsystem* Sub = UUniversalBarsSubsystem::Get(this)) + { + Sub->UnregisterHUD(this); + } + Super::NativeDestruct(); +} + +void UUniversalHUDWidget::SetHealth(float Percent) +{ + if (HealthBar) { HealthBar->SetFillPercent(Percent, true); } +} + +void UUniversalHUDWidget::SetStamina(float Percent) +{ + if (StaminaBar) { StaminaBar->SetFillPercent(Percent, true); } +} + +void UUniversalHUDWidget::SetShield(float Percent) +{ + if (HealthBar) { HealthBar->SetShieldPercent(Percent); } +} + +void UUniversalHUDWidget::SetHunger(float Amount) +{ + if (HealthBar) { HealthBar->SetPenaltyZone(TEXT("Hunger"), Amount, HungerColor); } +} + +void UUniversalHUDWidget::SetWeight(float Amount) +{ + if (HealthBar) { HealthBar->SetPenaltyZone(TEXT("Weight"), Amount, WeightColor); } +} + +void UUniversalHUDWidget::SetCold(float Amount) +{ + if (HealthBar) { HealthBar->SetPenaltyZone(TEXT("Cold"), Amount, ColdColor); } +} + +void UUniversalHUDWidget::DamageHealth(float Delta) +{ + if (HealthBar) { HealthBar->SetFillPercent(HealthBar->GetFillPercent() - FMath::Abs(Delta), true); } +} + +void UUniversalHUDWidget::HealHealth(float Delta) +{ + if (HealthBar) { HealthBar->SetFillPercent(HealthBar->GetFillPercent() + FMath::Abs(Delta), true); } +} + +float UUniversalHUDWidget::GetHealth() const +{ + return HealthBar ? HealthBar->GetFillPercent() : 0.f; +} + +void UUniversalHUDWidget::SetDemoMode(bool bEnable) +{ + bDemoMode = bEnable; + bDemoInit = false; + DemoTimer = 0.f; + DemoStep = 0; +} + +void UUniversalHUDWidget::DemoStepNow() +{ + // Each step shows a different animation; between steps the bars ease on their own. + switch (DemoStep % 10) + { + case 0: DamageHealth(0.25f); break; // red flash + lag tail + case 1: SetStamina(0.30f); break; // stamina drop + case 2: HealHealth(0.20f); break; // green flash + smooth rise + case 3: SetHunger(0.18f); break; // hunger zone grows in + case 4: SetWeight(0.14f); break; // weight zone grows in + case 5: SetStamina(0.95f); break; // stamina refill + case 6: HealHealth(0.30f); break; + case 7: SetHunger(0.0f); break; // hunger zone retracts + case 8: SetWeight(0.0f); break; // weight zone retracts + case 9: DamageHealth(0.45f); break; // big hit -> low-HP pulse + } + ++DemoStep; +} + +void UUniversalHUDWidget::PositionChips() +{ + if (!HealthBar) + { + return; + } + // Width of the bar in local pixels — ChipCanvas is overlaid on HealthBar so + // the same width maps zone-U (0..1) straight to chip X. + const float W = HealthBar->GetCachedGeometry().GetLocalSize().X; + if (W <= 1.f) + { + return; // not laid out yet this frame + } + + auto Place = [W](UImage* Chip, float U) + { + if (!Chip) + { + return; + } + if (U < 0.f) + { + Chip->SetVisibility(ESlateVisibility::Collapsed); // zone absent + return; + } + Chip->SetVisibility(ESlateVisibility::HitTestInvisible); + if (UCanvasPanelSlot* CSlot = Cast(Chip->Slot)) + { + const float ChipW = CSlot->GetSize().X; + // centre over the zone, sitting just above the bar (negative Y) + CSlot->SetPosition(FVector2D(U * W - ChipW * 0.5f, -ChipW - 6.f)); + } + }; + + Place(ChipHeart, HealthBar->GetUsableEnd()); + Place(ChipHunger, HealthBar->GetZoneCenter(TEXT("Hunger"))); + Place(ChipWeight, HealthBar->GetZoneCenter(TEXT("Weight"))); + Place(ChipCold, HealthBar->GetZoneCenter(TEXT("Cold"))); +} + +void UUniversalHUDWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + Super::NativeTick(MyGeometry, InDeltaTime); + + PositionChips(); // always track the bar, demo or not + + if (!bDemoMode) + { + return; + } + + if (!bDemoInit) + { + bDemoInit = true; + SetHealth(1.f); + SetStamina(1.f); + SetHunger(0.f); + SetWeight(0.f); + DemoTimer = 0.f; + DemoStep = 0; + } + + DemoTimer += InDeltaTime; + if (DemoTimer >= DemoStepInterval) + { + DemoTimer = 0.f; + DemoStepNow(); + } +} diff --git a/Source/UniversalBars/Public/UniversalBarWidget.h b/Source/UniversalBars/Public/UniversalBarWidget.h new file mode 100644 index 0000000..242c1b5 --- /dev/null +++ b/Source/UniversalBars/Public/UniversalBarWidget.h @@ -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 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 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 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; +}; diff --git a/Source/UniversalBars/Public/UniversalBarsSubsystem.h b/Source/UniversalBars/Public/UniversalBarsSubsystem.h new file mode 100644 index 0000000..032f21d --- /dev/null +++ b/Source/UniversalBars/Public/UniversalBarsSubsystem.h @@ -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 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 HUD; +}; diff --git a/Source/UniversalBars/Public/UniversalBarsTheme.h b/Source/UniversalBars/Public/UniversalBarsTheme.h new file mode 100644 index 0000000..c384db1 --- /dev/null +++ b/Source/UniversalBars/Public/UniversalBarsTheme.h @@ -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 ChipColors; +}; diff --git a/Source/UniversalBars/Public/UniversalHUDWidget.h b/Source/UniversalBars/Public/UniversalHUDWidget.h new file mode 100644 index 0000000..354cd5e --- /dev/null +++ b/Source/UniversalBars/Public/UniversalHUDWidget.h @@ -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 HealthBar = nullptr; + + UPROPERTY(meta=(BindWidgetOptional)) + TObjectPtr StaminaBar = nullptr; + + // Optional container for status chips (filled in the designer with placeholders). + UPROPERTY(meta=(BindWidgetOptional)) + TObjectPtr 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 ChipCanvas = nullptr; + + UPROPERTY(meta=(BindWidgetOptional)) + TObjectPtr ChipHeart = nullptr; // tracks usable-HP end + + UPROPERTY(meta=(BindWidgetOptional)) + TObjectPtr ChipHunger = nullptr; // tracks "Hunger" zone + + UPROPERTY(meta=(BindWidgetOptional)) + TObjectPtr ChipWeight = nullptr; // tracks "Weight" zone + + UPROPERTY(meta=(BindWidgetOptional)) + TObjectPtr ChipCold = nullptr; // tracks "Cold" zone (hidden if absent) + + // Optional central palette/geometry; applied on construct when set. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="HUD") + TObjectPtr 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; +}; diff --git a/Source/UniversalBars/UniversalBars.Build.cs b/Source/UniversalBars/UniversalBars.Build.cs new file mode 100644 index 0000000..ad9369c --- /dev/null +++ b/Source/UniversalBars/UniversalBars.Build.cs @@ -0,0 +1,20 @@ +// Copyright IHY. Universal Bars plugin. +using UnrealBuildTool; + +public class UniversalBars : ModuleRules +{ + public UniversalBars(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] + { + "Core", + "CoreUObject", + "Engine", + "UMG", + "SlateCore", + "Slate" + }); + } +} diff --git a/UniversalBars.uplugin b/UniversalBars.uplugin new file mode 100644 index 0000000..1196620 --- /dev/null +++ b/UniversalBars.uplugin @@ -0,0 +1,19 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "Universal Bars", + "Description": "PEAK-style universal UMG bars (health / stamina / hunger / weight / shield / boss / durability) driven by one procedural material, with a global subsystem API and animated damage / heal / low-HP feedback.", + "Category": "UI", + "CreatedBy": "IHY", + "CanContainContent": true, + "IsBetaVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "UniversalBars", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +}