Skip to content

Commit 3ae75eb

Browse files
authored
Update health.mdx
1 parent 504c950 commit 3ae75eb

1 file changed

Lines changed: 140 additions & 0 deletions

File tree

docs/building-blocks/health.mdx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,143 @@ Health has the following methods:
1111
- `setShield(number)` - Set the shield value
1212
- `showHealthbar()` - Show the health bar
1313
- `hideHealthbar()` - Hide the health bar
14+
15+
```ts
16+
import { Behavior, EntityDestroyed, RawPixi, value } from "@dreamlab/engine";
17+
import * as PIXI from "@dreamlab/vendor/pixi.ts";
18+
19+
export default class Health extends Behavior {
20+
@value()
21+
public health = 100;
22+
23+
@value()
24+
public maxHealth = 100;
25+
26+
@value()
27+
shield = 0;
28+
29+
@value()
30+
xOffset = 0;
31+
32+
@value()
33+
yOffset = 0;
34+
35+
@value()
36+
scale = 1;
37+
38+
rawPixi!: RawPixi;
39+
graphics!: PIXI.Graphics;
40+
41+
// Style knobs
42+
private readonly BAR_WIDTH = 64; // total width of the bar (px)
43+
private readonly BAR_HEIGHT = 8; // total height of the bar (px)
44+
private readonly RADIUS = 3; // corner radius (px)
45+
private readonly PADDING = 1; // inner padding between outline and fill (px)
46+
private readonly OUTLINE = 1; // outline thickness (px)
47+
48+
// Colors
49+
private readonly COLOR_BG = "#1e1f26";
50+
private readonly COLOR_OUTLINE = "#2f3342";
51+
private readonly COLOR_HEALTH = "#3ed36f";
52+
private readonly COLOR_HEALTH_LOW = "#e55039";
53+
private readonly COLOR_SHIELD = "#55b6ff";
54+
private readonly CONST_SCALEDOWN_MULTIPLIER = 0.1; // so everything isn't huge;
55+
56+
onInitialize(): void {
57+
if (!this.game.isClient()) return;
58+
this.createHealthbarIfNotExists();
59+
}
60+
61+
createHealthbarIfNotExists() {
62+
if (!this.game.isClient()) return;
63+
if (this.rawPixi) return;
64+
this.rawPixi = this.game.local.spawn({ type: RawPixi, name: "HealthBar" });
65+
this.graphics = new PIXI.Graphics();
66+
67+
// Add once; we'll just redraw the Graphics' geometry each change
68+
this.rawPixi.container?.addChild(this.graphics);
69+
70+
this.values.get("health")?.onChanged(() => this.redrawHealthbar());
71+
this.values.get("maxHealth")?.onChanged(() => this.redrawHealthbar());
72+
this.values.get("shield")?.onChanged(() => this.redrawHealthbar());
73+
this.redrawHealthbar();
74+
75+
this.listen(this.entity, EntityDestroyed, () => {
76+
this.rawPixi.destroy();
77+
});
78+
}
79+
80+
updatePosition() {
81+
this.rawPixi.globalTransform.position.x = this.entity.pos.x + this.xOffset;
82+
this.rawPixi.globalTransform.position.y = this.entity.pos.y + this.yOffset;
83+
84+
this.rawPixi.globalTransform.position.x -= this.BAR_WIDTH
85+
* (this.scale * this.CONST_SCALEDOWN_MULTIPLIER) / 2; // center it
86+
}
87+
88+
onTick() {
89+
if (!this.game.isClient()) return;
90+
this.createHealthbarIfNotExists();
91+
this.updatePosition();
92+
}
93+
94+
redrawHealthbar(): void {
95+
const max = Math.max(0, this.maxHealth);
96+
const hp = Math.max(0, Math.min(this.health, max));
97+
const hasShield = this.shield > 0 && max > 0;
98+
99+
// Avoid division by zero; if max is 0, show empty bar
100+
const ratio = max > 0 ? hp / max : 0;
101+
102+
// Health bar fill color shifts when low
103+
const healthColor = ratio <= 0.25 ? this.COLOR_HEALTH_LOW : this.COLOR_HEALTH;
104+
105+
const w = this.BAR_WIDTH;
106+
const h = this.BAR_HEIGHT;
107+
const innerW = w - 2 * (this.PADDING + this.OUTLINE);
108+
const innerH = h - 2 * (this.PADDING + this.OUTLINE);
109+
110+
const healthFillW = Math.max(0, Math.min(innerW, Math.round(innerW * ratio)));
111+
112+
// Shield: show as a thin bar above health, proportional to shield/max
113+
const shieldRatio = max > 0 ? Math.max(0, Math.min(1, this.shield / max)) : 0;
114+
const shieldFillW = Math.max(0, Math.min(innerW, Math.round(innerW * shieldRatio)));
115+
const shieldHeight = hasShield ? Math.max(2, Math.floor(innerH * 0.35)) : 0;
116+
117+
// Left-aligned means (0,0) is the top-left of the bar
118+
this.graphics.clear();
119+
120+
// Background (rounded)
121+
this.graphics
122+
.roundRect(0, 0, w, h, this.RADIUS)
123+
.fill({ color: this.COLOR_BG });
124+
125+
// Outline
126+
this.graphics
127+
.roundRect(0, 0, w, h, this.RADIUS)
128+
.stroke({ color: this.COLOR_OUTLINE, width: this.OUTLINE });
129+
130+
// Health fill (rounded only on left; but Graphics API rounds all corners, so draw a plain rect inside padding)
131+
if (healthFillW > 0) {
132+
this.graphics
133+
.rect(this.OUTLINE + this.PADDING, this.OUTLINE + this.PADDING, healthFillW, innerH)
134+
.fill({ color: healthColor });
135+
}
136+
137+
// Shield overlay as a slim strip at the top, left-aligned
138+
if (shieldHeight > 0 && shieldFillW > 0) {
139+
this.graphics
140+
.rect(
141+
this.OUTLINE + this.PADDING,
142+
this.OUTLINE + this.PADDING,
143+
shieldFillW,
144+
shieldHeight,
145+
)
146+
.fill({ color: this.COLOR_SHIELD, alpha: 0.9 });
147+
}
148+
149+
this.graphics.scale.set(this.scale * this.CONST_SCALEDOWN_MULTIPLIER);
150+
}
151+
}
152+
153+
```

0 commit comments

Comments
 (0)