Files
UE5-Modular-Game-Framework/docs/blueprints/02-player/08_BPC_HealthSystem.md

18 KiB

08 — Health System (BPC_HealthSystem)

C++ Status: StubSource/PG_Framework/Public/Player/BPC_HealthSystem.h provides the UCLASS shell, MaxHealth/CurrentHealth variables, OnHealthChanged and OnDeath event dispatchers. The C++ stub has NO gameplay logic — it exists so other C++ classes (BPC_StateManager, BPC_DamageReceptionSystem) can forward-reference it. Create a BP child and build ALL logic from this spec: TakeDamage(), Heal(), OnDeath() transition, damage resistance modifiers, health regen tick, I_Damageable interface. See docs/developer/cpp-integration-guide.md for setup steps.


Purpose

Manages the player's health pool, damage application with type-based resistance, health regeneration, death state, and critical-health events. Serves as the central survivability component on the player character.

Dependencies

  • Requires: FL_GameUtilities (for safe interface casts), I_Damageable (interface on the owner), GI_GameFramework (for GamePhase queries)
  • Required By: BPC_StressSystem (health below threshold triggers fear events), BPC_PlayerMetricsTracker (death tracking), WBP_HUD (health bar), AI_EnemyController (player health as awareness trigger), BPC_InteractionDetector (low-health checks)
  • Engine/Plugin Requirements: GameplayTags, DeveloperSettings

Class Info

Property Value
Parent Class ActorComponent
Class Type Blueprint Component
Asset Path Content/Framework/Player/BPC_HealthSystem
Implements Interfaces I_Damageable

1. Enums

E_DamageType

Value Description
Physical = 0 Bullets, blunt force, falls
Arcane = 1 Magic, curse, psychic damage
Fire = 2 Burning, environmental heat
Poison = 3 Toxins, venom, chemical
Fear = 4 Psychological damage (stress overflow)
Environmental = 5 Traps, crushing, drowning
True = 6 Bypasses all resistances, no reduction

E_DeathState

Value Description
Alive = 0 Normal gameplay
Dying = 1 Downed state, waiting for recovery or final death
Dead = 2 Character is dead, no further actions possible
PermaDeath = 3 Save-scrubbing locked, forced reload

2. Structs

S_DamageEvent

Field Type Description
BaseAmount Float Raw damage before resistance
DamageType E_DamageType Category of damage
Instigator Actor Who caused the damage
HitLocation Vector World location of impact
DamageTags GameplayTagContainer Additional contextual tags
bIsCriticalHit Boolean True if this is a headshot / weak-point hit
SourceDescription Text Human-readable source (for log / UI)

S_DamageResistance

Field Type Description
DamageType E_DamageType Type this resistance applies to
Multiplier Float 1.0 = normal, 0.5 = half damage, 2.0 = double
bIsStackable Boolean Can this resistance stack with others?

3. Variables

Configuration (Instance Editable, Expose On Spawn)

Variable Type Default Category Description
MaxHealth Float 100.0 Health Config Maximum hit points
bRegenEnabled Boolean true Health Config Allow passive health regeneration
RegenDelay Float 3.0 Health Config Seconds after last damage before regen starts
RegenRate Float 2.0 Health Config HP recovered per second during regen
RegenDelayUntil Float 0.0 Health Config World time when regen can resume
CriticalHealthPercent Float 0.25 Health Config Below this fraction of MaxHealth, fire critical event
DefaultResistances Array<S_DamageResistance> [] Health Config Base damage resistances for this character
bEnableFriendlyFire Boolean false Health Config Can the player damage themselves?

Internal (Private / Protected, No Expose)

Variable Type Default Category Description
CurrentHealth Float 100.0 Health State Current HP, clamped [0..MaxHealth]
DeathState E_DeathState Alive Health State Current death state
ActiveDamageOverTime Map<FName, FTimerHandle> {} Health State Active DoT timers keyed by source name
InvincibilityTimer FTimerHandle - Health State Timer handle for temporary invincibility
RegenTimerHandle FTimerHandle - Health State Timer handle for regen tick
bIsInvincible Boolean false Health State True during invincibility window
LastDamageInstigator Actor None Health State Most recent damage source (for kill credit)
LastDamageTime Float 0.0 Health State World time of last damage taken

Replicated (if multiplayer)

Variable Type Condition Description
CurrentHealth Float RepNotify Replicated with OnRep handler for UI updates
DeathState E_DeathState RepNotify Replicated death state

4. Functions

Public Functions

ApplyDamageS_DamageResult (custom struct)

  • Description: Applies damage to the health pool after resistance calculation. Returns effective damage and any side effects.
  • Parameters:
    Param Type Description
    DamageEvent S_DamageEvent The damage event to process
  • Blueprint Authority: Server (if MP), Any (single-player)
  • Flow:
    1. Early-out if DeathState is Dead or bIsInvincible
    2. Calculate effective damage from BaseAmount * type resistances
    3. If damage > 0: reset RegenDelay timer, update CurrentHealth
    4. Fire OnDamageTaken dispatcher
    5. If CurrentHealth <= 0: call HandleDeath
    6. If CurrentHealth <= CriticalHealthPercent: fire OnHealthCritical
    7. Return damage result struct

ApplyHealingFloat (effective healing)

  • Description: Adds health up to MaxHealth. Returns the amount actually healed.
  • Parameters:
    Param Type Description
    HealAmount Float Raw healing value
    HealTags GameplayTagContainer Contextual tags
  • Blueprint Authority: Server (if MP), Any (single-player)
  • Flow:
    1. Early-out if DeathState is Dead
    2. Clamp: CurrentHealth = FMath::Min(MaxHealth, CurrentHealth + HealAmount)
    3. Fire OnHealthChanged
    4. Return actual heal amount

GetHealthNormalisedFloat [0.0 - 1.0]

  • Description: Returns NormalisedCurrentHealth for UI widgets.
  • Flow: Return CurrentHealth / MaxHealth

SetMaxHealthvoid

  • Description: Changes MaxHealth and optionally heals to match new ratio or clamps current.
  • Parameters:
    Param Type Description
    NewMax Float New maximum health value
    bMaintainRatio Boolean If true, scale CurrentHealth to same ratio
  • Flow:
    1. Clamp NewMax to >= 1.0
    2. If bMaintainRatio: CurrentHealth = (CurrentHealth / MaxHealth) * NewMax
    3. Else: CurrentHealth = FMath::Min(CurrentHealth, NewMax)
    4. Set MaxHealth = NewMax
    5. Fire OnHealthChanged

KillInstantvoid

  • Description: Bypasses all resistances, immediately sets health to 0 and triggers death.
  • Parameters:
    Param Type Description
    Instigator Actor Who caused the kill
    DeathReason FText Reason displayed on death screen
  • Blueprint Authority: Server (if MP), Any (single-player)
  • Flow:
    1. Set CurrentHealth = 0
    2. Set DeathState = Dying
    3. Fire OnHealthChanged with CurrentHealth = 0
    4. Call HandleDeath with bInstant = true

ApplyDamageOverTimevoid

  • Description: Applies periodic damage with tick interval and total duration.
  • Parameters:
    Param Type Description
    DamagePerTick Float Damage applied each interval
    TickInterval Float Seconds between ticks
    TotalDuration Float Total seconds the DoT lasts
    DamageType E_DamageType Type of each damage tick
    SourceName FName Unique name to prevent stacking same DoT
    Instigator Actor Who applied the DoT
  • Flow:
    1. If SourceName already exists in ActiveDamageOverTime, clear existing timer
    2. Create a Timer by Event with loop = TickInterval
    3. On each tick: build S_DamageEvent, call ApplyDamage
    4. After TotalDuration: clear timer, remove from ActiveDamageOverTime

IsAliveBoolean

  • Flow: Return DeathState == Alive

AddResistancevoid

  • Parameters:
    Param Type Description
    NewResistance S_DamageResistance Resistance to add or update
  • Flow: If resistance for this DamageType exists, update Multiplier; if not, add to array.

RemoveResistancevoid

  • Parameters:
    Param Type Description
    DamageType E_DamageType Type to remove
  • Flow: Remove all entries matching this DamageType from resistance array.

Protected / Private Functions

HandleDeathvoid

  • Description: Internal death processing. Sets state, fires dispatchers, notifies GameMode.
  • Parameters:
    Param Type Description
    bInstant Boolean If true, skip dying state go straight to dead
  • Flow:
    1. Set DeathState = (bInstant ? Dead : Dying)
    2. Clear all timers (regen, DoT, invincibility)
    3. Fire OnDeath with DeathState
    4. Call I_Damageable::OnOwnerDied on owner if interface exists
    5. Get GM_CoreGameMode via GetWorld()->GetAuthGameMode()
    6. Call GM_CoreGameMode::HandlePlayerDead(LastDamageInstigator)

RegenTickvoid

  • Description: Timer callback — applies one tick of regeneration.
  • Flow:
    1. If CurrentHealth >= MaxHealth: stop regen timer, return
    2. CurrentHealth = FMath::Min(MaxHealth, CurrentHealth + RegenRate * TickInterval)
    3. Fire OnHealthChanged
    4. If CurrentHealth >= MaxHealth: stop regen timer

5. Event Dispatchers

Dispatcher Parameters Bind Access Description
OnHealthChanged float OldHealth, float NewHealth, float Delta Public Fired when health changes by any amount
OnDamageTaken S_DamageEvent DamageEvent, float EffectiveDamage Public Fired each time damage is applied
OnHealingReceived float HealAmount, GameplayTagContainer HealTags Public Fired each time healing is applied
OnHealthCritical float CurrentHealth, float MaxHealth Public Fired when health drops below critical threshold
OnDeath E_DeathState DeathState, Actor Instigator Public Fired when character dies or enters dying state
OnDeathStateChanged E_DeathState OldState, E_DeathState NewState Public Fired on any death state transition
OnInvincibilityChanged bool bIsInvincible Public Fired when invincibility state toggles

6. Overridden Events / Custom Events

Event: BeginPlay

  • Description: Initializes health, loads saved health if persisting, starts regen timer if enabled.
  • Flow:
    1. Set CurrentHealth = MaxHealth
    2. Check if owner implements I_Persistable
    3. If yes, try to load saved health value
    4. If bRegenEnabled: start regen timer with initial delay = 0
    5. Bind to any relevant GamePhase change dispatchers

Event: OnComponentDestroyed

  • Description: Clean up all active timers.
  • Flow:
    1. Clear all TimerHandles (Invincibility, Regen, all DoT timers)
    2. Clear ActiveDamageOverTime map

Custom Event: ApplyTemporaryInvincibility

  • Parameters: float Duration
  • Description: Grants brief invincibility, often used after respawn or dodge.
  • Flow:
    1. Set bIsInvincible = true
    2. Fire OnInvincibilityChanged(true)
    3. Start timer for Duration
    4. On timer end: set bIsInvincible = false, fire OnInvincibilityChanged(false)

7. Blueprint Graph Logic Flow

flowchart TD
    A[ApplyDamage called] --> B{DeathState == Dead?}
    B -->|Yes| C[Return 0 damage]
    B -->|No| D{bIsInvincible?}
    D -->|Yes| C
    D -->|No| E[Calculate resistance multiplier]
    E --> F[EffectiveDamage = Base * Multiplier]
    F --> G[CurrentHealth -= EffectiveDamage]
    G --> H[Reset RegenDelay timer]
    H --> I[Fire OnDamageTaken]
    I --> J{CurrentHealth <= 0?}
    J -->|Yes| K[HandleDeath]
    J -->|No| L{CurrentHealth <= CriticalThreshold?}
    L -->|Yes| M[Fire OnHealthCritical]
    L -->|No| N[Fire OnHealthChanged]
    K --> O[Fire OnDeath dispatcher]
    K --> P[Notify GameMode: HandlePlayerDead]
    K --> Q[Clear all timers]
    M --> N

8. Communication Matrix

Who Talks How What Is Sent
BPC_HealthSystem Dispatcher OnHealthChanged -> WBP_HUD, BPC_StressSystem
BPC_HealthSystem Dispatcher OnDeath -> GM_CoreGameMode, SS_SaveSystem, BPC_PlayerMetricsTracker
BPC_HealthSystem Dispatcher OnDamageTaken -> BPC_StressSystem, BPC_AdaptiveEnvironment
BPC_HealthSystem Interface I_Damageable.Execute_ApplyDamage route
BPC_HealthSystem Interface I_Persistable for save/load of CurrentHealth
External (Weapons, Traps) Interface I_Damageable -> ApplyDamage
BPC_HealthSystem Direct GM_CoreGameMode.HandlePlayerDead on death
BPC_HealthSystem Subsystem GI_GameFramework.GetGamePhase for phase-restricted actions

9. Validation / Testing Checklist

  • ApplyDamage with all E_DamageType values produces correct resistance-modified damage
  • Health never drops below 0 or exceeds MaxHealth
  • Regeneration does not start while RegenDelay timer is active
  • Regeneration stops when health reaches MaxHealth
  • Death sets DeathState properly and fires OnDeath exactly once
  • Temporary invincibility blocks all damage for its duration
  • Damage-over-time correctly applies ticks and cleans up timer on death
  • Critical health event fires only when crossing below threshold from above
  • Edge case: ApplyHealing on a dead character returns 0 with no dispatchers
  • Edge case: ApplyDamage with negative BaseAmount is treated as 0
  • Edge case: Multiple simultaneous DoT sources with same SourceName do not stack
  • All dispatchers are cleaned up on component destroy

10. Reuse Notes

  • This component can be used on AI characters and enemy pawns by exposing the same configuration variables.
  • For enemies, consider disabling regen (bRegenEnabled = false) unless a specific healing mechanic exists.
  • The resistance system supports temporary buffs via AddResistance / RemoveResistance at runtime.
  • If implementing a downed / bleeding state, extend E_DeathState with custom values and add a recovery timer check in HandleDeath.
  • Damage numbers displayed as floating text should bind to OnDamageTaken.
  • For multiplayer: all ApplyDamage logic runs on Server; clients receive repnotified CurrentHealth and fire local cosmetic dispatchers only.

11. Multiplayer Networking (Expanded)

Replicated Variables (Existing + New)

Variable Type Condition Description
CurrentHealth Float Replicated Using OnRep_CurrentHealth Clients sync via OnRep → dispatcher
DeathState E_DeathState Replicated Using OnRep_DeathState Death state synced; OnRep triggers death effects
bIsInvincible Boolean Replicated Invincibility visible to other players
MaxHealth Float Replicated Synced for UI percentage calculations

Server RPCs

RPC Direction Description
Server_ApplyDamage Client→Server Client reports taking damage. Server validates source, range, damage type.
Server_ApplyHealing Client→Server Client requests healing. Server validates heal source, applies.
Server_KillInstant Client→Server Only accepted from authoritative sources (GM, traps). Client calls are rejected.

Authority Gates

Function ApplyDamage(DamageEvent)
  Switch HasAuthority
    Authority:
      → Calculate resistance, apply damage, fire dispatchers
    Remote:
      → Return (clients never modify health directly)
      → Server_ApplyDamage is called by damage source (weapon/trap), not victim

Function ApplyHealing(HealAmount)
  Switch HasAuthority
    Authority:
      → Apply healing, fire dispatchers
    Remote:
      → Return
      → Consumable use calls Server_UseConsumable → server calls ApplyHealing

Client Prediction

  • Health bar: Client predicts damage flash on hit; server-corrected value arrives via OnRep_CurrentHealth.
  • Death: No prediction. OnRep_DeathState triggers all death effects (screen fade, ragdoll).
  • Healing: Client shows green flash immediately + predicted health; server corrects within one RTT.
  • Invincibility: Visual effect (shield shimmer) is local cosmetic; state is replicated.

OnRep Handlers

OnRep_CurrentHealth()
  → Broadcast OnHealthChanged(OldHealth, CurrentHealth, Delta)
  → UI updates identically to single-player path

OnRep_DeathState()
  → Broadcast OnDeathStateChanged(OldState, NewState)
  → If Dead: play death animation, screen effects, notify GM

Anti-Cheat

  • Server validates all Server_ApplyDamage calls: check instigator range, weapon fire rate, damage type validity.
  • Server never trusts DamageEvent.BaseAmount from client — always recalculates from weapon data.
  • Server_KillInstant is only callable from server-authoritative systems (traps, death zones, GM).

Blueprint Spec: Health System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.