nodes/menu.pyΒΆ
Part of Clumsy Bird.
1"""MenuScene: the title screen shown before a run starts."""
2
3from config import ASSETS, WIDTH
4
5from simvx.core import Node, Sprite2D, Vec2, on_input
6from simvx.core.ui import AnchorPreset, Button, Control, Label, Panel
7
8from .ground import Ground
9from .play_scene import PlayScene
10
11
12class TitlePanel(Control):
13 """Centred title card: game name, credit, Play button and controls."""
14
15 def __init__(self, on_play, **kwargs):
16 super().__init__(**kwargs)
17 self.set_anchor_preset(AnchorPreset.FULL_RECT)
18
19 # A centred control's box comes from its margin pair, so both margins
20 # of an axis are set: left/right = -w/2 / +w/2 around the anchor.
21 card = self.add_child(Panel(name="Card"))
22 card.set_anchor_preset(AnchorPreset.CENTER)
23 card.margin_left, card.margin_right = -280, 280
24 card.margin_top, card.margin_bottom = -170, 170
25 card.bg_colour = (0.03, 0.06, 0.11, 0.88)
26
27 title = card.add_child(Label("CLUMSY BIRD", name="Title"))
28 title.set_anchor_preset(AnchorPreset.CENTER)
29 title.margin_left, title.margin_right = -260, 260
30 title.margin_top, title.margin_bottom = -140, -80
31 title.font_size = 46
32 title.alignment = "center"
33 title.text_colour = (1.0, 0.95, 0.6, 1.0)
34
35 credit = card.add_child(Label("SimVX port of ellisonleao/clumsy-bird", name="Credit"))
36 credit.set_anchor_preset(AnchorPreset.CENTER)
37 credit.margin_left, credit.margin_right = -260, 260
38 credit.margin_top, credit.margin_bottom = -70, -40
39 credit.font_size = 16
40 credit.alignment = "center"
41 credit.text_colour = (0.85, 0.9, 0.95, 1.0)
42
43 play = card.add_child(Button("Play", on_press=on_play, name="Play"))
44 play.set_anchor_preset(AnchorPreset.CENTER)
45 play.margin_left, play.margin_right = -100, 100
46 play.margin_top, play.margin_bottom = -20, 34
47 play.font_size = 20
48
49 controls = card.add_child(Label("Space or Click: flap", name="Controls"))
50 controls.set_anchor_preset(AnchorPreset.CENTER)
51 controls.margin_left, controls.margin_right = -260, 260
52 controls.margin_top, controls.margin_bottom = 60, 90
53 controls.font_size = 18
54 controls.alignment = "center"
55 controls.text_colour = (1.0, 1.0, 1.0, 1.0)
56
57 quit_hint = card.add_child(Label("Esc: quit", name="QuitHint"))
58 quit_hint.set_anchor_preset(AnchorPreset.CENTER)
59 quit_hint.margin_left, quit_hint.margin_right = -260, 260
60 quit_hint.margin_top, quit_hint.margin_bottom = 95, 121
61 quit_hint.font_size = 15
62 quit_hint.alignment = "center"
63 quit_hint.text_colour = (0.85, 0.9, 0.95, 1.0)
64
65
66class MenuScene(Node):
67 """Title screen. The Play button, a click, or Space starts a run.
68
69 Shares PlayScene's action table so the same bindings are live on both
70 roots; the scene tree re-registers them on every ``change_scene``.
71 """
72
73 input_actions = PlayScene.input_actions
74
75 def __init__(self, **kwargs):
76 super().__init__(name="MenuScene", **kwargs)
77 self._start_queued = False
78
79 def on_ready(self):
80 self.add_child(
81 Sprite2D(
82 texture=str(ASSETS / "bg.png"),
83 width=WIDTH,
84 height=504,
85 position=Vec2(WIDTH / 2, 252),
86 name="Background",
87 )
88 )
89 self.add_child(Ground(name="Ground"))
90 self.add_child(TitlePanel(self._queue_start, name="TitlePanel"))
91
92 @on_input(action="flap")
93 def _on_start_input(self, event) -> bool:
94 self._queue_start()
95 return True
96
97 @on_input(action="quit")
98 def _on_quit_input(self, event) -> bool:
99 self.app.quit()
100 return True
101
102 def _queue_start(self):
103 """Ask for the swap. ``change_scene`` destroys this node, so it never
104 runs inside input dispatch: ``on_update`` performs it."""
105 self._start_queued = True
106
107 def on_update(self, dt: float):
108 if self._start_queued and self.tree:
109 self._start_queued = False
110 self.tree.change_scene(PlayScene())