You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Turn mpong into Breakout clone
This commit is contained in:
+162
-22
@@ -2,12 +2,23 @@
|
||||
#include "py/dynruntime.h"
|
||||
|
||||
// Global BSS (non-static) state is required for native modules.
|
||||
size_t g_line_y;
|
||||
uint16_t *g_framebuffer;
|
||||
size_t g_framebuffer_len;
|
||||
size_t g_framebuffer_width;
|
||||
size_t g_framebuffer_height;
|
||||
|
||||
int g_paddle_x;
|
||||
int g_paddle_width;
|
||||
int g_paddle_height;
|
||||
int g_ball_x;
|
||||
int g_ball_y;
|
||||
int g_ball_vx;
|
||||
int g_ball_vy;
|
||||
|
||||
#define BRICK_ROWS 4
|
||||
#define BRICK_COLS 8
|
||||
uint8_t g_bricks[BRICK_ROWS][BRICK_COLS];
|
||||
|
||||
// readfile(filename): return first 10 bytes of a file as bytes
|
||||
static mp_obj_t readfile(mp_obj_t filename_obj) {
|
||||
mp_obj_t open_fun = mp_load_global(MP_QSTR_open);
|
||||
@@ -26,6 +37,63 @@ static mp_obj_t readfile(mp_obj_t filename_obj) {
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(readfile_obj, readfile);
|
||||
|
||||
static inline int clamp_int(int value, int min_value, int max_value) {
|
||||
if (value < min_value) {
|
||||
return min_value;
|
||||
}
|
||||
if (value > max_value) {
|
||||
return max_value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline size_t framebuffer_max_pixels(void) {
|
||||
const size_t max_pixels = g_framebuffer_len / sizeof(uint16_t);
|
||||
const size_t total_pixels = g_framebuffer_width * g_framebuffer_height;
|
||||
return (max_pixels < total_pixels) ? max_pixels : total_pixels;
|
||||
}
|
||||
|
||||
static void draw_pixel(int x, int y, uint16_t color) {
|
||||
if (x < 0 || y < 0) {
|
||||
return;
|
||||
}
|
||||
if ((size_t)x >= g_framebuffer_width || (size_t)y >= g_framebuffer_height) {
|
||||
return;
|
||||
}
|
||||
const size_t idx = (size_t)y * g_framebuffer_width + (size_t)x;
|
||||
const size_t max_pixels = framebuffer_max_pixels();
|
||||
if (idx >= max_pixels) {
|
||||
return;
|
||||
}
|
||||
g_framebuffer[idx] = color;
|
||||
}
|
||||
|
||||
static void draw_rect(int x, int y, int w, int h, uint16_t color) {
|
||||
if (w <= 0 || h <= 0) {
|
||||
return;
|
||||
}
|
||||
for (int yy = 0; yy < h; yy++) {
|
||||
for (int xx = 0; xx < w; xx++) {
|
||||
draw_pixel(x + xx, y + yy, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_ball(void) {
|
||||
g_ball_x = (int)g_framebuffer_width / 2;
|
||||
g_ball_y = (int)g_framebuffer_height / 2;
|
||||
g_ball_vx = 1;
|
||||
g_ball_vy = -1;
|
||||
}
|
||||
|
||||
static void reset_bricks(void) {
|
||||
for (int row = 0; row < BRICK_ROWS; row++) {
|
||||
for (int col = 0; col < BRICK_COLS; col++) {
|
||||
g_bricks[row][col] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init(framebuffer, width, height): store a reference to the framebuffer and dimensions.
|
||||
static mp_obj_t init(mp_obj_t framebuffer_obj, mp_obj_t width_obj, mp_obj_t height_obj) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
@@ -36,53 +104,125 @@ static mp_obj_t init(mp_obj_t framebuffer_obj, mp_obj_t width_obj, mp_obj_t heig
|
||||
g_framebuffer_width = (size_t)mp_obj_get_int(width_obj);
|
||||
g_framebuffer_height = (size_t)mp_obj_get_int(height_obj);
|
||||
|
||||
g_paddle_width = (int)g_framebuffer_width / 5;
|
||||
g_paddle_height = 4;
|
||||
g_paddle_x = ((int)g_framebuffer_width - g_paddle_width) / 2;
|
||||
|
||||
reset_ball();
|
||||
reset_bricks();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_3(init_obj, init);
|
||||
|
||||
// render(): draw a moving black line on the RGB565 framebuffer as a deterministic test pattern.
|
||||
// render(): draw a simple Breakout-style frame and advance the game simulation.
|
||||
static mp_obj_t render(void) {
|
||||
if (g_framebuffer == NULL || g_framebuffer_width == 0 || g_framebuffer_height == 0) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
uint16_t *pixels = g_framebuffer;
|
||||
const size_t len = g_framebuffer_len;
|
||||
const size_t width = g_framebuffer_width;
|
||||
const size_t height = g_framebuffer_height;
|
||||
const size_t max_pixels = len / sizeof(uint16_t);
|
||||
const size_t total_pixels = width * height;
|
||||
const size_t fill_pixels = (max_pixels < total_pixels) ? max_pixels : total_pixels;
|
||||
const size_t fill_pixels = framebuffer_max_pixels();
|
||||
|
||||
// Fill the framebuffer with white so the black line is visible.
|
||||
for (size_t i = 0; i < fill_pixels; i++) { pixels[i] = 0xFFFF; } // RGB565 white
|
||||
// Clear to black.
|
||||
for (size_t i = 0; i < fill_pixels; i++) { g_framebuffer[i] = 0x0000; } // RGB565 black
|
||||
|
||||
// Draw a horizontal black line across the current row.
|
||||
if (g_line_y < height) {
|
||||
const size_t base = g_line_y * width;
|
||||
for (size_t x = 0; x < width; x++) {
|
||||
const size_t idx = base + x;
|
||||
if (idx >= fill_pixels) {
|
||||
break;
|
||||
// Update ball position.
|
||||
g_ball_x += g_ball_vx;
|
||||
g_ball_y += g_ball_vy;
|
||||
|
||||
// Wall collisions.
|
||||
if (g_ball_x <= 0) {
|
||||
g_ball_x = 0;
|
||||
g_ball_vx = 1;
|
||||
} else if (g_ball_x >= (int)width - 1) {
|
||||
g_ball_x = (int)width - 1;
|
||||
g_ball_vx = -1;
|
||||
}
|
||||
|
||||
if (g_ball_y <= 0) {
|
||||
g_ball_y = 0;
|
||||
g_ball_vy = 1;
|
||||
}
|
||||
|
||||
// Brick layout.
|
||||
const int brick_gap = 2;
|
||||
const int brick_rows = BRICK_ROWS;
|
||||
const int brick_cols = BRICK_COLS;
|
||||
const int brick_height = 6;
|
||||
const int brick_area_width = (int)width - (brick_gap * (brick_cols + 1));
|
||||
const int brick_width = (brick_area_width > 0) ? (brick_area_width / brick_cols) : 0;
|
||||
const int brick_offset_y = 8;
|
||||
|
||||
// Brick collision.
|
||||
if (brick_width > 0 && g_ball_y <= brick_offset_y + brick_rows * (brick_height + brick_gap)) {
|
||||
for (int row = 0; row < brick_rows; row++) {
|
||||
for (int col = 0; col < brick_cols; col++) {
|
||||
if (!g_bricks[row][col]) {
|
||||
continue;
|
||||
}
|
||||
const int bx = brick_gap + col * (brick_width + brick_gap);
|
||||
const int by = brick_offset_y + row * (brick_height + brick_gap);
|
||||
if (g_ball_x >= bx && g_ball_x < bx + brick_width && g_ball_y >= by && g_ball_y < by + brick_height) {
|
||||
g_bricks[row][col] = 0;
|
||||
g_ball_vy = -g_ball_vy;
|
||||
row = brick_rows;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pixels[idx] = 0x0000; // RGB565 black
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the line for the next call, wrapping at the bottom.
|
||||
g_line_y++;
|
||||
if (g_line_y >= height) {
|
||||
g_line_y = 0;
|
||||
// Paddle collision.
|
||||
const int paddle_y = (int)height - g_paddle_height - 4;
|
||||
if (g_ball_y >= paddle_y - 1 && g_ball_y <= paddle_y + g_paddle_height) {
|
||||
if (g_ball_x >= g_paddle_x && g_ball_x <= g_paddle_x + g_paddle_width) {
|
||||
g_ball_y = paddle_y - 1;
|
||||
g_ball_vy = -1;
|
||||
const int paddle_center = g_paddle_x + g_paddle_width / 2;
|
||||
if (g_ball_x < paddle_center) {
|
||||
g_ball_vx = -1;
|
||||
} else if (g_ball_x > paddle_center) {
|
||||
g_ball_vx = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ball fell below paddle: reset.
|
||||
if (g_ball_y >= (int)height - 1) {
|
||||
reset_ball();
|
||||
}
|
||||
|
||||
// Draw bricks.
|
||||
if (brick_width > 0) {
|
||||
for (int row = 0; row < brick_rows; row++) {
|
||||
for (int col = 0; col < brick_cols; col++) {
|
||||
if (!g_bricks[row][col]) {
|
||||
continue;
|
||||
}
|
||||
const int bx = brick_gap + col * (brick_width + brick_gap);
|
||||
const int by = brick_offset_y + row * (brick_height + brick_gap);
|
||||
draw_rect(bx, by, brick_width, brick_height, 0xF800); // RGB565 red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw paddle and ball.
|
||||
draw_rect(g_paddle_x, paddle_y, g_paddle_width, g_paddle_height, 0xFFFF); // RGB565 white
|
||||
draw_pixel(g_ball_x, g_ball_y, 0xFFFF);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(render_obj, render);
|
||||
|
||||
// move_paddle(delta): print delta for debugging.
|
||||
// move_paddle(delta): move the paddle horizontally by delta.
|
||||
static mp_obj_t move_paddle(mp_obj_t delta_obj) {
|
||||
int delta = mp_obj_get_int(delta_obj);
|
||||
mp_printf(&mp_plat_print, "delta: %d\n", delta);
|
||||
if (g_framebuffer_width > 0) {
|
||||
g_paddle_x = clamp_int(g_paddle_x + delta, 0, (int)g_framebuffer_width - g_paddle_width);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(move_paddle_obj, move_paddle);
|
||||
|
||||
@@ -10,7 +10,7 @@ import sys
|
||||
if sys.platform == "esp32":
|
||||
import mpong_xtensawin as mpong
|
||||
else:
|
||||
import mpong_amd64 as mpong
|
||||
import mpong_x64 as mpong
|
||||
|
||||
class MPong(Activity):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user