Health Systems
Build robust health, damage, and death systems with regeneration.
Introduction
Health systems are fundamental to most games. This guide covers health management, damage types, regeneration, shields, and death handling with event-driven architecture.
Guide 15 of 25
Screenshot
Step-by-Step Guide
Create a health component
Add health properties: currentHealth, maxHealth, shield, invulnerability timer.
Implement damage types
Define damage types (normal, fire, poison, fall, drowning) with different resistances and effects.
Add health regeneration
Regenerate health after 5 seconds without damage. Use a regen rate (5 HP/sec), cancel on hit.
Implement shields
Shields absorb damage before health. Shields regenerate faster but delay before regenerating (3s).
Handle death
On health reaching 0, trigger death event, play animation, disable input, invoke respawn.
Use events for damage
Emit tookDamage events with amount and type. UI, effects, and audio subscribe.
FluxScript Example
Here is a complete FluxScript implementation for this guide:
on start {
player.maxHealth = 100
player.health = 100
player.maxShield = 50
player.shield = 50
player.invulnerable = false
player.invulnTimer = 0
player.regenDelay = 0
player.isDead = false
}
on update(dt) {
if player.isDead return
if player.invulnTimer > 0 {
player.invulnTimer -= dt
if player.invulnTimer <= 0 player.invulnerable = false
}
if player.regenDelay > 0 {
player.regenDelay -= dt
} else if player.health < player.maxHealth {
player.health = math.min(player.health + 5 * dt, player.maxHealth)
emit("healthChanged", { health: player.health })
}
}
func takeDamage(target, amount, type) {
if target.invulnerable || target.isDead return
resistance = target.getResistance(type)
actualDamage = amount * (1 - resistance)
if target.shield > 0 {
shieldDamage = math.min(target.shield, actualDamage)
target.shield -= shieldDamage
actualDamage -= shieldDamage
}
if actualDamage > 0 {
target.health -= actualDamage
target.regenDelay = 5.0
emit("tookDamage", { target: target, amount: actualDamage, type: type })
ui.flash("damage", 0.3)
}
if target.health <= 0 {
target.health = 0
target.isDead = true
emit("playerDied", { target: target })
}
}Node Graph Example
Prefer visual programming? Here is the equivalent Node Graph:
[On Update] --> [Branch: !isDead]
--> [Update: invulnTimer]
--> [Branch: regenDelay > 0]
+-- True --> [Countdown]
+-- False --> [Regen: +5*dt] --> [Emit: healthChanged]
[Function: takeDamage] --> [Check: invulnerable/isDead]
--> [Apply: resistance] --> [Shield absorbs first]
--> [Remaining -> health] --> [Emit: tookDamage]
--> [Branch: health <= 0] --> [Emit: playerDied]Optimization Tips
Keep your implementation fast and efficient with these optimization tips:
- Use events for damage communication. Emit tookDamage events — UI, effects, and audio subscribe decoupled.
- Clamp health between 0 and maxHealth. Negative or excess health causes UI bugs and death check failures.
- Use damage type resistances. Creates varied gameplay (fire armor, poison immunity) without complex logic.
- Separate shield and health regen timings. Shields regen faster (15/s after 3s), health slower (5/s after 5s).
Common Mistakes
Directly modifying health from multiple sources
Use a centralized takeDamage() function. Direct modifications bypass resistances and death checks.
No invulnerability frames after damage
Add 0.5-1s invulnerability after a hit. Without it, rapid-fire enemies drain health instantly.
