MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk_wrap.examples.tetris.main.TetrisGame Class Reference
Inheritance diagram for mxvk_wrap.examples.tetris.main.TetrisGame:

Public Member Functions

None __init__ (self)
 A playable 2D Tetris window using mxvk_wrap graphics.
None draw (self)
None on_event (self, event)
None reset (self)

Public Attributes

str asset_dir = Path(__file__).resolve().parent / "data"
 Load Tetris images, create the board, and start the first piece.
 background = mx.Sprite(self, self.asset_dir / "psychedelic_background.png", vertex_shader=str(self.asset_dir / "sprite.vert.spv"), fragment_shader=str(self.asset_dir / "background.frag.spv"))
 background_start_time = time.monotonic()
list blocks = [mx.Sprite(self, self.asset_dir / path) for path in BLOCK_FILES]
tuple cells = ()
 Select and spawn a random tetromino at the top of the board.
 clear_block = mx.Sprite(self, self.asset_dir / "block_gray.png")
float clear_started = 0.0
 Start a fresh game with an empty board and score.
tuple clearing_rows = ()
 Start a fresh game with an empty board and score.
float fall_elapsed = 0.0
 Start a fresh game with an empty board and score.
 font = None
float frame_accumulator = 0.0
 Start a fresh game with an empty board and score.
bool game_over = False
 Start a fresh game with an empty board and score.
list grid = []
 Start a fresh game with an empty board and score.
 last_frame_time = time.monotonic()
 Start a fresh game with an empty board and score.
int level = 1
 Start a fresh game with an empty board and score.
int lines = 0
 Start a fresh game with an empty board and score.
bool mouse_pressed = False
 Handle game controls from MXVK keyboard events.
float mouse_x = 0.0
 Handle game controls from MXVK keyboard events.
float mouse_y = 0.0
 Handle game controls from MXVK keyboard events.
int next_color = 0
 Start a fresh game with an empty board and score.
int piece_color = 0
 Select and spawn a random tetromino at the top of the board.
int piece_x = 0
 Select and spawn a random tetromino at the top of the board.
int piece_y = 0
 Select and spawn a random tetromino at the top of the board.
int score = 0
 Start a fresh game with an empty board and score.
float simulation_time = 0.0
 Start a fresh game with an empty board and score.

Protected Member Functions

bool _clear_flash_visible (self)
bool _collides (self, int x, int y, tuple[tuple[int, int],...] cells)
None _draw_cell (self, int color, int board_x, int board_y, int left, int top, int cell_size)
float _fall_interval (self)
None _finish_line_clear (self)
Path _font_path (self)
None _hard_drop (self)
None _lock_piece (self)
bool _move (self, int offset_x, int offset_y)
None _new_piece (self)
None _rotate (self)
None _simulate_step (self)
None _update (self)

Detailed Description

Definition at line 50 of file main.py.

Constructor & Destructor Documentation

◆ __init__()

None mxvk_wrap.examples.tetris.main.TetrisGame.__init__ ( self)

A playable 2D Tetris window using mxvk_wrap graphics.

Definition at line 53 of file main.py.

53 def __init__(self) -> None:
54 ## @brief Load Tetris images, create the board, and start the first piece.
55 self.asset_dir = Path(__file__).resolve().parent / "data"
56 super().__init__("MXVK Wrap 2D Tetris", 960, 720, vsync=True)
57 self.font = None
58 try:
59 self.font = mx.Font(self._font_path(), 24)
60 self.background = mx.Sprite(self, self.asset_dir / "psychedelic_background.png", vertex_shader=str(self.asset_dir / "sprite.vert.spv"), fragment_shader=str(self.asset_dir / "background.frag.spv"))
61 self.background.enable_extended_uniforms()
62 self.blocks = [mx.Sprite(self, self.asset_dir / path) for path in BLOCK_FILES]
63 self.clear_block = mx.Sprite(self, self.asset_dir / "block_gray.png")
64 except Exception:
65 if self.font is not None:
66 self.font.close()
67 self.close()
68 raise
69 self.grid: list[list[int]] = []
70 self.cells: tuple[tuple[int, int], ...] = ()
71 self.piece_color = 0
72 self.next_color = 0
73 self.piece_x = 0
74 self.piece_y = 0
75 self.score = 0
76 self.lines = 0
77 self.level = 1
78 self.game_over = False
79 self.clearing_rows: tuple[int, ...] = ()
80 self.clear_started = 0.0
81 self.background_start_time = time.monotonic()
82 self.mouse_x = 0.0
83 self.mouse_y = 0.0
84 self.mouse_pressed = False
85 self.simulation_time = 0.0
86 self.fall_elapsed = 0.0
87 self.frame_accumulator = 0.0
88 self.last_frame_time = time.monotonic()
89 self.reset()
90

Member Function Documentation

◆ _clear_flash_visible()

bool mxvk_wrap.examples.tetris.main.TetrisGame._clear_flash_visible ( self)
protected

Definition at line 189 of file main.py.

189 def _clear_flash_visible(self) -> bool:
190 ## @brief Return whether completed rows should show the flash texture.
191 return int((self.simulation_time - self.clear_started) / CLEAR_FLASH_INTERVAL) % 2 == 0
192

◆ _collides()

bool mxvk_wrap.examples.tetris.main.TetrisGame._collides ( self,
int x,
int y,
tuple[tuple[int, int], ...] cells )
protected

Definition at line 125 of file main.py.

125 def _collides(self, x: int, y: int, cells: tuple[tuple[int, int], ...]) -> bool:
126 ## @brief Return whether a piece position overlaps a wall, floor, or block.
127 ## @param x Candidate board X coordinate.
128 ## @param y Candidate board Y coordinate.
129 ## @param cells Tetromino cells relative to @p x and @p y.
130 ## @return True when the placement is invalid.
131 for cell_x, cell_y in cells:
132 board_x = x + cell_x
133 board_y = y + cell_y
134 if board_x < 0 or board_x >= BOARD_WIDTH or board_y >= BOARD_HEIGHT:
135 return True
136 if board_y >= 0 and self.grid[board_y][board_x] != EMPTY:
137 return True
138 return False
139

◆ _draw_cell()

None mxvk_wrap.examples.tetris.main.TetrisGame._draw_cell ( self,
int color,
int board_x,
int board_y,
int left,
int top,
int cell_size )
protected

Definition at line 268 of file main.py.

268 def _draw_cell(self, color: int, board_x: int, board_y: int, left: int, top: int, cell_size: int) -> None:
269 ## @brief Queue one block sprite at a board location.
270 ## @param color Index into the loaded block sprites.
271 ## @param board_x Board column.
272 ## @param board_y Board row.
273 ## @param left Board left screen coordinate.
274 ## @param top Board top screen coordinate.
275 ## @param cell_size Block width and height in pixels.
276 self.blocks[color].draw(left + board_x * cell_size, top + board_y * cell_size, width=cell_size, height=cell_size)
277

◆ _fall_interval()

float mxvk_wrap.examples.tetris.main.TetrisGame._fall_interval ( self)
protected

Definition at line 203 of file main.py.

203 def _fall_interval(self) -> float:
204 ## @brief Return the current automatic drop interval in seconds.
205 return max(0.08, 0.75 - (self.level - 1) * 0.06)
206

◆ _finish_line_clear()

None mxvk_wrap.examples.tetris.main.TetrisGame._finish_line_clear ( self)
protected

Definition at line 177 of file main.py.

177 def _finish_line_clear(self) -> None:
178 ## @brief Collapse flashing rows, award points, and spawn the next piece.
179 cleared = len(self.clearing_rows)
180 cleared_rows = set(self.clearing_rows)
181 self.grid = [[EMPTY for _ in range(BOARD_WIDTH)] for _ in range(cleared)] + [row for index, row in enumerate(self.grid) if index not in cleared_rows]
182 self.lines += cleared
183 self.score += (0, 100, 300, 500, 800)[cleared] * self.level
184 self.level = self.lines // 10 + 1
185 self.clearing_rows = ()
186 self.clear_started = 0.0
187 self._new_piece()
188

◆ _font_path()

Path mxvk_wrap.examples.tetris.main.TetrisGame._font_path ( self)
protected

Definition at line 91 of file main.py.

91 def _font_path(self) -> Path:
92 ## @brief Return the supplied Tetris font path.
93 ## @return The TrueType font stored beside the Tetris block artwork.
94 path = self.asset_dir / "font.ttf"
95 if not path.is_file():
96 raise FileNotFoundError(f"missing Tetris font: {path}")
97 return path
98

◆ _hard_drop()

None mxvk_wrap.examples.tetris.main.TetrisGame._hard_drop ( self)
protected

Definition at line 193 of file main.py.

193 def _hard_drop(self) -> None:
194 ## @brief Drop the active piece to its lowest valid row and lock it.
195 if self.game_over:
196 return
197 distance = 0
198 while self._move(0, 1):
199 distance += 1
200 self.score += distance * 2
201 self._lock_piece()
202

◆ _lock_piece()

None mxvk_wrap.examples.tetris.main.TetrisGame._lock_piece ( self)
protected

Definition at line 165 of file main.py.

165 def _lock_piece(self) -> None:
166 ## @brief Merge the active piece into the board and clear completed lines.
167 for cell_x, cell_y in self.cells:
168 board_y = self.piece_y + cell_y
169 if board_y >= 0:
170 self.grid[board_y][self.piece_x + cell_x] = self.piece_color
171 self.clearing_rows = tuple(index for index, row in enumerate(self.grid) if EMPTY not in row)
172 if self.clearing_rows:
173 self.clear_started = self.simulation_time
174 return
175 self._new_piece()
176

◆ _move()

bool mxvk_wrap.examples.tetris.main.TetrisGame._move ( self,
int offset_x,
int offset_y )
protected

Definition at line 140 of file main.py.

140 def _move(self, offset_x: int, offset_y: int) -> bool:
141 ## @brief Move the active piece when its target placement is valid.
142 ## @param offset_x Horizontal cell offset.
143 ## @param offset_y Vertical cell offset.
144 ## @return True when the piece moved.
145 if self.game_over or self._collides(self.piece_x + offset_x, self.piece_y + offset_y, self.cells):
146 return False
147 self.piece_x += offset_x
148 self.piece_y += offset_y
149 return True
150

◆ _new_piece()

None mxvk_wrap.examples.tetris.main.TetrisGame._new_piece ( self)
protected

Definition at line 115 of file main.py.

115 def _new_piece(self) -> None:
116 ## @brief Select and spawn a random tetromino at the top of the board.
117 self.piece_color = self.next_color
118 self.next_color = random.randrange(len(PIECES))
119 self.cells = PIECES[self.piece_color]
120 self.piece_x = 3
121 self.piece_y = 0
122 if self._collides(self.piece_x, self.piece_y, self.cells):
123 self.game_over = True
124

◆ _rotate()

None mxvk_wrap.examples.tetris.main.TetrisGame._rotate ( self)
protected

Definition at line 151 of file main.py.

151 def _rotate(self) -> None:
152 ## @brief Rotate the active piece clockwise, with simple wall kicks.
153 if self.game_over or self.piece_color == 3:
154 return
155 rotated = tuple((-cell_y, cell_x) for cell_x, cell_y in self.cells)
156 minimum_x = min(cell_x for cell_x, _ in rotated)
157 minimum_y = min(cell_y for _, cell_y in rotated)
158 rotated = tuple((cell_x - minimum_x, cell_y - minimum_y) for cell_x, cell_y in rotated)
159 for kick in (0, -1, 1, -2, 2):
160 if not self._collides(self.piece_x + kick, self.piece_y, rotated):
161 self.cells = rotated
162 self.piece_x += kick
163 return
164

◆ _simulate_step()

None mxvk_wrap.examples.tetris.main.TetrisGame._simulate_step ( self)
protected

Definition at line 207 of file main.py.

207 def _simulate_step(self) -> None:
208 ## @brief Advance one fixed-rate simulation step independent of rendering.
209 if self.clearing_rows:
210 if self.simulation_time - self.clear_started >= CLEAR_FLASH_DURATION:
211 self._finish_line_clear()
212 return
213 if self.game_over:
214 return
215 self.fall_elapsed += SIMULATION_STEP
216 while self.fall_elapsed >= self._fall_interval():
217 self.fall_elapsed -= self._fall_interval()
218 if not self._move(0, 1):
219 self._lock_piece()
220 return
221

◆ _update()

None mxvk_wrap.examples.tetris.main.TetrisGame._update ( self)
protected

Definition at line 222 of file main.py.

222 def _update(self) -> None:
223 ## @brief Run fixed 60 Hz simulation steps to keep game timing frame-rate independent.
224 now = time.monotonic()
225 elapsed = min(now - self.last_frame_time, MAX_FRAME_DELTA)
226 self.last_frame_time = now
227 self.frame_accumulator += max(0.0, elapsed)
228 while self.frame_accumulator >= SIMULATION_STEP:
229 self.frame_accumulator -= SIMULATION_STEP
230 self.simulation_time += SIMULATION_STEP
231 self._simulate_step()
232

◆ draw()

None mxvk_wrap.examples.tetris.main.TetrisGame.draw ( self)

Definition at line 278 of file main.py.

278 def draw(self) -> None:
279 ## @brief Draw the background, playfield, active piece, and HUD each frame.
280 self._update()
281 width, height = self.swapchain_extent
282 if width <= 0 or height <= 0:
283 return
284 self.background.set_mouse(self.mouse_x, self.mouse_y, self.mouse_pressed)
285 self.background.set_uniform(0, 0.0, 0.0, float(width), float(height))
286 self.background.set_uniform(2, 0.0, time.monotonic() - self.background_start_time, 0.0, 0.0)
287 self.background.draw(0, 0, width=width, height=height)
288 cell_size = max(16, min((height - 100) // (BOARD_HEIGHT + 1), (width - 260) // (BOARD_WIDTH + 2)))
289 board_width = BOARD_WIDTH * cell_size
290 board_height = BOARD_HEIGHT * cell_size
291 left = max(24, (width - board_width) // 2 - 100)
292 top = max(50, (height - board_height) // 2)
293 for board_y, row in enumerate(self.grid):
294 for board_x, color in enumerate(row):
295 if color != EMPTY:
296 if board_y in self.clearing_rows and self._clear_flash_visible():
297 self.clear_block.draw(left + board_x * cell_size, top + board_y * cell_size, width=cell_size, height=cell_size)
298 else:
299 self._draw_cell(color, board_x, board_y, left, top, cell_size)
300 if not self.game_over and not self.clearing_rows:
301 for cell_x, cell_y in self.cells:
302 self._draw_cell(self.piece_color, self.piece_x + cell_x, self.piece_y + cell_y, left, top, cell_size)
303 panel_x = left + board_width + 28
304 self.draw_text("2D TETRIS", panel_x, top, self.font, mx.Color(255, 245, 180))
305 self.draw_text(f"Score {self.score}", panel_x, top + 54, self.font)
306 self.draw_text(f"Lines {self.lines}", panel_x, top + 88, self.font, mx.Color(180, 225, 255))
307 self.draw_text(f"Level {self.level}", panel_x, top + 122, self.font, mx.Color(180, 255, 190))
308 self.draw_text("Next", panel_x, top + 182, self.font, mx.Color(255, 245, 180))
309 preview_cell = max(12, cell_size * 3 // 5)
310 preview_cells = PIECES[self.next_color]
311 preview_min_x = min(cell_x for cell_x, _ in preview_cells)
312 preview_min_y = min(cell_y for _, cell_y in preview_cells)
313 for cell_x, cell_y in preview_cells:
314 self.blocks[self.next_color].draw(panel_x + (cell_x - preview_min_x) * preview_cell, top + 220 + (cell_y - preview_min_y) * preview_cell, width=preview_cell, height=preview_cell)
315 self.draw_text("Arrows: move / rotate", panel_x, top + 340, self.font)
316 self.draw_text("Space: hard drop", panel_x, top + 374, self.font)
317 if self.game_over:
318 self.draw_text("GAME OVER", left + cell_size, top + board_height // 2 - 24, self.font, mx.Color(255, 100, 100))
319 self.draw_text("Press R to restart", left + cell_size, top + board_height // 2 + 14, self.font, mx.Color(255, 255, 255))
320
321

◆ on_event()

None mxvk_wrap.examples.tetris.main.TetrisGame.on_event ( self,
event )

Definition at line 233 of file main.py.

233 def on_event(self, event) -> None:
234 ## @brief Handle game controls from MXVK keyboard events.
235 ## @param event MXVK event received by @c App.
236 if event.mouse_motion:
237 self.mouse_x = event.x
238 self.mouse_y = event.y
239 return
240 if event.mouse_button_down or event.mouse_button_up:
241 self.mouse_x = event.x
242 self.mouse_y = event.y
243 self.mouse_pressed = event.mouse_button_down
244 return
245 if not event.key_down:
246 return
247 if event.key == mx.KEY_ESCAPE:
248 self.quit()
249 elif self.clearing_rows:
250 return
251 elif self.game_over:
252 if event.key == mx.KEY_R:
253 self.reset()
254 elif event.key == mx.KEY_LEFT:
255 self._move(-1, 0)
256 elif event.key == mx.KEY_RIGHT:
257 self._move(1, 0)
258 elif event.key == mx.KEY_DOWN:
259 if self._move(0, 1):
260 self.score += 1
261 else:
262 self._lock_piece()
263 elif event.key == mx.KEY_UP:
264 self._rotate()
265 elif event.key == mx.KEY_SPACE:
266 self._hard_drop()
267

◆ reset()

None mxvk_wrap.examples.tetris.main.TetrisGame.reset ( self)

Definition at line 99 of file main.py.

99 def reset(self) -> None:
100 ## @brief Start a fresh game with an empty board and score.
101 self.grid = [[EMPTY for _ in range(BOARD_WIDTH)] for _ in range(BOARD_HEIGHT)]
102 self.score = 0
103 self.lines = 0
104 self.level = 1
105 self.game_over = False
106 self.clearing_rows = ()
107 self.clear_started = 0.0
108 self.simulation_time = 0.0
109 self.fall_elapsed = 0.0
110 self.frame_accumulator = 0.0
111 self.last_frame_time = time.monotonic()
112 self.next_color = random.randrange(len(PIECES))
113 self._new_piece()
114

Member Data Documentation

◆ asset_dir

str mxvk_wrap.examples.tetris.main.TetrisGame.asset_dir = Path(__file__).resolve().parent / "data"

Load Tetris images, create the board, and start the first piece.

Definition at line 55 of file main.py.

◆ background

mxvk_wrap.examples.tetris.main.TetrisGame.background = mx.Sprite(self, self.asset_dir / "psychedelic_background.png", vertex_shader=str(self.asset_dir / "sprite.vert.spv"), fragment_shader=str(self.asset_dir / "background.frag.spv"))

Definition at line 60 of file main.py.

◆ background_start_time

mxvk_wrap.examples.tetris.main.TetrisGame.background_start_time = time.monotonic()

Definition at line 81 of file main.py.

◆ blocks

list mxvk_wrap.examples.tetris.main.TetrisGame.blocks = [mx.Sprite(self, self.asset_dir / path) for path in BLOCK_FILES]

Definition at line 62 of file main.py.

◆ cells

mxvk_wrap.examples.tetris.main.TetrisGame.cells = ()

Select and spawn a random tetromino at the top of the board.

Move the active piece when its target placement is valid.

Parameters
offset_xHorizontal cell offset.
offset_yVertical cell offset.
Returns
True when the piece moved.

Definition at line 70 of file main.py.

◆ clear_block

mxvk_wrap.examples.tetris.main.TetrisGame.clear_block = mx.Sprite(self, self.asset_dir / "block_gray.png")

Definition at line 63 of file main.py.

◆ clear_started

mxvk_wrap.examples.tetris.main.TetrisGame.clear_started = 0.0

Start a fresh game with an empty board and score.

Return whether completed rows should show the flash texture.

Definition at line 80 of file main.py.

◆ clearing_rows

tuple mxvk_wrap.examples.tetris.main.TetrisGame.clearing_rows = ()

Start a fresh game with an empty board and score.

Definition at line 79 of file main.py.

◆ fall_elapsed

float mxvk_wrap.examples.tetris.main.TetrisGame.fall_elapsed = 0.0

Start a fresh game with an empty board and score.

Definition at line 86 of file main.py.

◆ font

mxvk_wrap.examples.tetris.main.TetrisGame.font = None

Definition at line 57 of file main.py.

◆ frame_accumulator

float mxvk_wrap.examples.tetris.main.TetrisGame.frame_accumulator = 0.0

Start a fresh game with an empty board and score.

Definition at line 87 of file main.py.

◆ game_over

bool mxvk_wrap.examples.tetris.main.TetrisGame.game_over = False

Start a fresh game with an empty board and score.

Definition at line 78 of file main.py.

◆ grid

mxvk_wrap.examples.tetris.main.TetrisGame.grid = []

Start a fresh game with an empty board and score.

Definition at line 69 of file main.py.

◆ last_frame_time

mxvk_wrap.examples.tetris.main.TetrisGame.last_frame_time = time.monotonic()

Start a fresh game with an empty board and score.

Definition at line 88 of file main.py.

◆ level

int mxvk_wrap.examples.tetris.main.TetrisGame.level = 1

Start a fresh game with an empty board and score.

Definition at line 77 of file main.py.

◆ lines

int mxvk_wrap.examples.tetris.main.TetrisGame.lines = 0

Start a fresh game with an empty board and score.

Definition at line 76 of file main.py.

◆ mouse_pressed

mxvk_wrap.examples.tetris.main.TetrisGame.mouse_pressed = False

Handle game controls from MXVK keyboard events.

Parameters
eventMXVK event received by App.

Definition at line 84 of file main.py.

◆ mouse_x

mxvk_wrap.examples.tetris.main.TetrisGame.mouse_x = 0.0

Handle game controls from MXVK keyboard events.

Parameters
eventMXVK event received by App.

Definition at line 82 of file main.py.

◆ mouse_y

mxvk_wrap.examples.tetris.main.TetrisGame.mouse_y = 0.0

Handle game controls from MXVK keyboard events.

Parameters
eventMXVK event received by App.

Definition at line 83 of file main.py.

◆ next_color

int mxvk_wrap.examples.tetris.main.TetrisGame.next_color = 0

Start a fresh game with an empty board and score.

Definition at line 72 of file main.py.

◆ piece_color

mxvk_wrap.examples.tetris.main.TetrisGame.piece_color = 0

Select and spawn a random tetromino at the top of the board.

Definition at line 71 of file main.py.

◆ piece_x

mxvk_wrap.examples.tetris.main.TetrisGame.piece_x = 0

Select and spawn a random tetromino at the top of the board.

Definition at line 73 of file main.py.

◆ piece_y

mxvk_wrap.examples.tetris.main.TetrisGame.piece_y = 0

Select and spawn a random tetromino at the top of the board.

Definition at line 74 of file main.py.

◆ score

int mxvk_wrap.examples.tetris.main.TetrisGame.score = 0

Start a fresh game with an empty board and score.

Definition at line 75 of file main.py.

◆ simulation_time

float mxvk_wrap.examples.tetris.main.TetrisGame.simulation_time = 0.0

Start a fresh game with an empty board and score.

Definition at line 85 of file main.py.


The documentation for this class was generated from the following file:
  • python-examples/mxvk_wrap/examples/tetris/main.py