HUD Anchors¶
a game HUD pinned to screen corners that scales with the window.
▶ Run in browserTags: ui hud anchors layout responsive
A game-style heads-up display whose pieces are anchored to the viewport edges, not placed at absolute coordinates: a health bar hugs the top-left, the score sits in the top-right, and a hint label is centred along the bottom. Resize the window and every element stays glued to its corner while the bar and labels keep their margins, because each top-level Control uses an AnchorPreset plus symmetric margins rather than a fixed position.
What it demonstrates¶
Anchoring top-level Controls to viewport corners with
set_anchor_preset():TOP_LEFT,TOP_RIGHT,CENTER_BOTTOM, never an absoluteposition.Margins as pixel offsets from the chosen anchor, so a corner gadget keeps a consistent gutter as the window grows or shrinks.
A live health bar (background Panel + foreground fill Panel) whose width tracks a value, driven each frame from
on_update.A score readout and a centred hint that follow the bottom/top edges on resize.
Controls: Up / Down - Heal / take damage (animate the health bar) ESC - Quit
Source¶
1"""HUD Anchors: a game HUD pinned to screen corners that scales with the window.
2
3A game-style heads-up display whose pieces are anchored to the viewport edges,
4not placed at absolute coordinates: a health bar hugs the top-left, the score
5sits in the top-right, and a hint label is centred along the bottom. Resize the
6window and every element stays glued to its corner while the bar and labels
7keep their margins, because each top-level Control uses an AnchorPreset plus
8symmetric margins rather than a fixed position.
9
10# /// simvx
11# tags = ["ui", "hud", "anchors", "layout", "responsive"]
12# web = { root = "HudAnchorsDemo", width = 800, height = 600, responsive = true }
13# ///
14
15## What it demonstrates
16
17- Anchoring top-level Controls to viewport corners with `set_anchor_preset()`:
18 `TOP_LEFT`, `TOP_RIGHT`, `CENTER_BOTTOM`, never an absolute `position`.
19- Margins as pixel offsets from the chosen anchor, so a corner gadget keeps a
20 consistent gutter as the window grows or shrinks.
21- A live health bar (background Panel + foreground fill Panel) whose width
22 tracks a value, driven each frame from `on_update`.
23- A score readout and a centred hint that follow the bottom/top edges on resize.
24
25Controls:
26 Up / Down - Heal / take damage (animate the health bar)
27 ESC - Quit
28"""
29
30from simvx.core import (
31 AnchorPreset,
32 Colour,
33 Input,
34 InputMap,
35 Key,
36 Label,
37 Node,
38 Panel,
39 Vec2,
40)
41from simvx.graphics import App
42
43WIDTH, HEIGHT = 800, 600
44BAR_W, BAR_H = 220.0, 22.0 # health bar size in pixels
45PAD = 16.0 # gutter from the anchored corner
46
47
48class HudAnchorsDemo(Node):
49 """Root node: builds a corner-anchored HUD and animates the health bar."""
50
51 def on_ready(self):
52 InputMap.add_action("heal", [Key.UP])
53 InputMap.add_action("hurt", [Key.DOWN])
54 InputMap.add_action("quit", [Key.ESCAPE])
55
56 self._health = 0.75 # 0..1
57 self._score = 0
58
59 # --- Health bar, top-left ---------------------------------------
60 # The bar background is the anchored top-level Control; the fill is a
61 # child that the on_update loop resizes to reflect current health.
62 bar_bg = Panel(name="HealthBarBG")
63 bar_bg.set_anchor_preset(AnchorPreset.TOP_LEFT)
64 bar_bg.margin_left = PAD
65 bar_bg.margin_top = PAD
66 bar_bg.size = Vec2(BAR_W, BAR_H)
67 bar_bg.bg_colour = Colour.hex("#202028")
68 self.add_child(bar_bg)
69
70 self._bar_fill = Panel(name="HealthBarFill")
71 self._bar_fill.set_anchor_preset(AnchorPreset.TOP_LEFT)
72 self._bar_fill.margin_left = 2
73 self._bar_fill.margin_top = 2
74 self._bar_fill.size = Vec2((BAR_W - 4) * self._health, BAR_H - 4)
75 self._bar_fill.bg_colour = Colour.hex("#43C463")
76 bar_bg.add_child(self._bar_fill)
77
78 self._hp_label = Label("HP")
79 self._hp_label.set_anchor_preset(AnchorPreset.CENTER)
80 self._hp_label.font_size = 12.0
81 self._hp_label.text_colour = Colour.WHITE
82 self._hp_label.alignment = "center"
83 bar_bg.add_child(self._hp_label)
84
85 # --- Score, top-right -------------------------------------------
86 # TOP_RIGHT anchors both edges to the right side; negative right margin
87 # pushes the box inward, so it tracks the right edge on resize.
88 self._score_label = Label("Score: 0")
89 self._score_label.set_anchor_preset(AnchorPreset.TOP_RIGHT)
90 self._score_label.size = Vec2(180, 24)
91 self._score_label.margin_left = -180 - PAD
92 self._score_label.margin_right = PAD
93 self._score_label.margin_top = PAD
94 self._score_label.font_size = 16.0
95 self._score_label.text_colour = Colour.hex("#FFD166")
96 self._score_label.alignment = "right"
97 self.add_child(self._score_label)
98
99 # --- Hint, bottom-centre ----------------------------------------
100 # CENTER_BOTTOM keeps the label horizontally centred and pinned to the
101 # bottom edge; symmetric left/right margins centre a fixed-width box.
102 hint = Label("Up / Down to change health | ESC to quit")
103 hint.set_anchor_preset(AnchorPreset.CENTER_BOTTOM)
104 hint.margin_left = -240
105 hint.margin_right = 240
106 hint.margin_top = -36
107 hint.margin_bottom = -12
108 hint.font_size = 13.0
109 hint.text_colour = Colour.LIGHT_GRAY
110 hint.alignment = "center"
111 self.add_child(hint)
112
113 def on_update(self, dt: float):
114 if Input.is_action_pressed("quit"):
115 self.app.quit()
116 return
117
118 # Animate health and bump the score so the HUD is visibly live.
119 if Input.is_action_pressed("heal"):
120 self._health = min(1.0, self._health + dt * 0.6)
121 if Input.is_action_pressed("hurt"):
122 self._health = max(0.0, self._health - dt * 0.6)
123
124 self._score += 1
125 self._score_label.text = f"Score: {self._score}"
126
127 # Resize the fill to match health and recolour toward red when low.
128 self._bar_fill.size = Vec2((BAR_W - 4) * self._health, BAR_H - 4)
129 if self._health < 0.3:
130 self._bar_fill.bg_colour = Colour.hex("#E63946")
131 elif self._health < 0.6:
132 self._bar_fill.bg_colour = Colour.hex("#F4A261")
133 else:
134 self._bar_fill.bg_colour = Colour.hex("#43C463")
135 self._hp_label.text = f"HP {int(self._health * 100)}"
136
137
138if __name__ == "__main__":
139 app = App(title="SimVX HUD Anchors", width=WIDTH, height=HEIGHT)
140 app.run(HudAnchorsDemo())