Health and Damage - Basics
Learn the fundamentals of health tracking and damage systems in multiplayer games.
What You’ll Learn
- Basic health tracking
- Damage application
- Health bars with visual feedback
- Invincibility frames
Basic Health System
Use Case: Any game with player health
Simple health tracking with damage application.
Step 1: Define State
Start with basic health properties in your game state:
import { defineGame } from '@martini-kit/core';
export const game = defineGame({
setup: ({ playerIds }) => ({
players: Object.fromEntries(
playerIds.map((id) => [
id,
{
x: 400,
y: 300,
health: 100,
maxHealth: 100,
},
])
),
}),
}); Key Points:
health: Current health valuemaxHealth: Maximum health for calculating percentages
Step 2: Add Damage Action
Create an action to apply damage:
actions: {
takeDamage: {
apply: (state, context, input: { amount: number }) => {
const player = state.players[context.targetId]; // IMPORTANT: Use targetId!
if (!player) return;
// Apply damage
player.health -= input.amount;
// Clamp to 0
if (player.health < 0) {
player.health = 0;
}
console.log(`Player ${context.targetId} took ${input.amount} damage. Health: ${player.health}`);
},
},
} Why targetId?
context.targetId: The player receiving damage (correct)context.playerId: The player who initiated the action (wrong for damage recipient)
Step 3: Add Healing Action
Balance damage with healing:
heal: {
apply: (state, context, input: { amount: number }) => {
const player = state.players[context.targetId];
if (!player) return;
// Apply healing
player.health += input.amount;
// Clamp to max
if (player.health > player.maxHealth) {
player.health = player.maxHealth;
}
},
}, Step 4: Render Health Bar
Create a visual health bar that updates automatically.
Using HealthBarManager - Automatic health bars above sprites:
import Phaser from 'phaser';
import { PhaserAdapter, SpriteManager, HealthBarManager } from '@martini-kit/phaser';
export class GameScene extends Phaser.Scene {
private adapter!: PhaserAdapter;
private spriteManager!: SpriteManager;
private healthBars!: HealthBarManager;
create() {
this.adapter = new PhaserAdapter(runtime, this);
// Create sprite manager for players
this.spriteManager = new SpriteManager(this.adapter, this, {
collection: 'players',
createSprite: (player) => {
return this.add.circle(player.x, player.y, 20, 0x00aaff);
},
updateSprite: (sprite, player) => {
sprite.x = player.x;
sprite.y = player.y;
},
});
// Auto-attach health bars to all player sprites
this.healthBars = new HealthBarManager(this.adapter, {
spriteManager: this.spriteManager,
healthKey: 'health',
maxHealth: 100,
offset: { x: 0, y: -30 },
width: 50,
height: 5,
colorThresholds: {
high: { value: 50, color: 0x48bb78 }, // Green > 50%
medium: { value: 25, color: 0xeab308 }, // Yellow > 25%
low: { value: 0, color: 0xef4444 }, // Red <= 25%
},
});
}
update() {
// Auto-updates all health bars
this.healthBars.update();
}
} Benefits:
- ✅ Auto-creates health bars for all sprites
- ✅ Auto-positions above sprites
- ✅ Auto-scales based on health
- ✅ Auto-colors based on thresholds
- ✅ Just 2 lines of code!
What You’ve Built:
- ✅ Health tracking system
- ✅ Damage and healing actions
- ✅ Visual health bar with color coding
- ✅ Automatic updates on state changes
Damage with Invincibility Frames
Use Case: Prevent instant death from multiple hits
Temporary invincibility after taking damage prevents unfair rapid elimination.
Step 1: Add Invincibility State
Extend player state with invincibility tracking:
const INVINCIBILITY_DURATION = 1000; // ms
export const game = defineGame({
setup: ({ playerIds }) => ({
players: Object.fromEntries(
playerIds.map((id) => [
id,
{
x: 400,
y: 300,
health: 100,
isInvulnerable: false,
invulnerabilityTimer: 0, // ms remaining
},
])
),
}),
}); Step 2: Check Invincibility Before Damage
Update damage action to respect invincibility:
actions: {
takeDamage: {
apply: (state, context, input: { amount: number; attackerId?: string }) => {
const player = state.players[context.targetId];
if (!player) return;
// Check invincibility
if (player.isInvulnerable) {
console.log('Player is invulnerable!');
return; // No damage applied
}
// Apply damage
player.health -= input.amount;
// Grant invincibility
player.isInvulnerable = true;
player.invulnerabilityTimer = INVINCIBILITY_DURATION;
console.log(`Damage dealt by ${input.attackerId || 'unknown'}: ${input.amount}`);
},
},
} Step 3: Countdown Invincibility Timer
Use a tick action to decrement the timer:
tick: {
apply: (state, context, input: { delta: number }) => {
// Update invincibility timers for all players
for (const player of Object.values(state.players)) {
if (player.isInvulnerable) {
player.invulnerabilityTimer -= input.delta;
if (player.invulnerabilityTimer <= 0) {
player.isInvulnerable = false;
player.invulnerabilityTimer = 0;
}
}
}
},
}, Important: Call this action from your Phaser update() loop:
update(time: number, delta: number) {
this.runtime.dispatchAction('tick', { delta }, { broadcast: true });
} Step 4: Visual Feedback
Show invincibility with sprite flashing.
Using SpriteManager - Automatic sprite flashing with updateSprite callback:
import { PhaserAdapter, SpriteManager } from '@martini-kit/phaser';
create() {
this.adapter = new PhaserAdapter(runtime, this);
// Auto-manages player sprites with invincibility effects
this.spriteManager = new SpriteManager(this.adapter, this, {
collection: 'players',
createSprite: (player) => {
return this.add.circle(player.x, player.y, 20, 0x00aaff);
},
updateSprite: (sprite, player) => {
sprite.x = player.x;
sprite.y = player.y;
// Flash sprite when invulnerable
if (player.isInvulnerable) {
const flashPhase = Math.floor(Date.now() / 100) % 2;
sprite.setAlpha(flashPhase === 0 ? 0.3 : 1.0);
} else {
sprite.setAlpha(1.0);
}
},
});
} Benefits:
- ✅ Automatic sprite lifecycle management
- ✅ Built-in update callback for effects
- ✅ Minimal boilerplate
What You’ve Built:
- ✅ Invincibility frames after damage
- ✅ Prevents spam damage
- ✅ Visual flashing feedback
- ✅ Timed duration with automatic removal
See Also
- Advanced Health Systems - Death, respawn, regeneration
- Complex Systems - Team damage, damage numbers
- Shooting Mechanics - Dealing damage with projectiles