From e2190c8dc83d58b9fcc74094585b0399f64b7529 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Sun, 7 Apr 2024 19:35:35 +0200 Subject: [PATCH] add birds to runner game --- soweli/data.h | 41 ++++++++++++++++---- soweli/soweli.c | 46 +++++++++++------------ soweli/ssd1306.h | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 32 deletions(-) diff --git a/soweli/data.h b/soweli/data.h index 4655117..944eb7d 100644 --- a/soweli/data.h +++ b/soweli/data.h @@ -1,6 +1,18 @@ -#define OBSTACLE_TYPES 5 -const unsigned char soweli_a[8] = { +const u16 digit_font[] = { + 0b111101101101111, // 0 + 0b010110010010111, // 1 + 0b111001111100111, // 2 + 0b111001111001111, // 3 + 0b101101111001001, // 4 + 0b111100111001111, // 5 + 0b111100111101111, // 6 + 0b111001001001001, // 7 + 0b111101111101111, // 8 + 0b111101111001111, // 9 +}; + +const u8 soweli_a[8] = { 0b01111111, 0b10000000, 0b10101000, @@ -11,7 +23,7 @@ const unsigned char soweli_a[8] = { 0b01010000, }; -const unsigned char soweli_b[8] = { +const u8 soweli_b[8] = { 0b01111111, 0b10000000, 0b10101000, @@ -22,7 +34,14 @@ const unsigned char soweli_b[8] = { 0b00000101, }; -const unsigned char obstacle_sprites[OBSTACLE_TYPES][8] = { +#define GROUND_OBSTACLE_TYPES 5 +#define OBSTACLE_TYPES (GROUND_OBSTACLE_TYPES + 1) + +const u8 obstacle_heights[OBSTACLE_TYPES] = {6, 4, 4, 8, 5, 8}; +const u8 obstacle_widths[OBSTACLE_TYPES] = {8, 8, 8, 7, 5, 7}; // (width + 8 )/2 +const u8 obstacle_y_offset[OBSTACLE_TYPES] = {0, 0, 0, 0, 0, 18}; + +const u8 obstacle_sprites[OBSTACLE_TYPES][8] = { { 0b00000000, 0b00000000, @@ -38,7 +57,6 @@ const unsigned char obstacle_sprites[OBSTACLE_TYPES][8] = { 0b00000000, 0b00000000, 0b00000000, - 0b00111100, 0b01100110, 0b11000011, @@ -74,7 +92,14 @@ const unsigned char obstacle_sprites[OBSTACLE_TYPES][8] = { 0b00011000, 0b00111100, }, + { + 0b00100000, + 0b00010000, + 0b00001000, + 0b01010100, + 0b00000010, + 0b00011110, + 0b00010000, + 0b00010000, + }, }; - -const unsigned char obstacle_heights[OBSTACLE_TYPES] = {6, 4, 4, 8, 5}; -const unsigned char obstacle_widths[OBSTACLE_TYPES] = {8, 8, 8, 7, 5}; \ No newline at end of file diff --git a/soweli/soweli.c b/soweli/soweli.c index 3e1cad1..2de111d 100644 --- a/soweli/soweli.c +++ b/soweli/soweli.c @@ -109,14 +109,19 @@ void remove_obstacle() obstacle_count--; } -void spawn_obstacle(u8 pos) +void spawn_ground_obstacle(u8 pos) { if (obstacle_count >= MAX_OBSTACLES) return; obstacle_positions[obstacle_count] = pos; - obstacle_types[obstacle_count] = rand8() % OBSTACLE_TYPES; + obstacle_types[obstacle_count] = rand8() % GROUND_OBSTACLE_TYPES; obstacle_count++; } +void spawn_air_obstacle(u8 pos) +{ + spawn_ground_obstacle(pos); + obstacle_types[obstacle_count - 1] = 5; +} void update() { @@ -136,7 +141,9 @@ void update() obstacle_positions[i] -= SCROLL_SPEED; u8 type = obstacle_types[i]; u8 dist_x = gfx_abs((s16)obstacle_positions[i] - (s16)PLAYER_X); - if (dist_x < obstacle_widths[type] && (player_y >> Y_PRECISION) < obstacle_heights[type]) + u8 dist_y = gfx_abs((s16)obstacle_y_offset[type] - (s16)(player_y >> Y_PRECISION)); + + if (dist_x < obstacle_widths[type] && dist_y < obstacle_heights[type]) { is_running = 0; } @@ -145,34 +152,25 @@ void update() { remove_obstacle(); } - if (time_to_obstacle < 5) + if (time_to_obstacle == 0) { - u8 count = 1 + (rand8() & rand8() & 1) + (rand8() & rand8() & rand8() & rand8() & 1); - for (u8 i = 0; i < count; i++) + if (rand8() & rand8() & 1) + spawn_air_obstacle(128); + else { - spawn_obstacle(128 + i * 8); + spawn_ground_obstacle(128); + if (rand8() & rand8() & 1 && obstacle_types[obstacle_count - 1] != 5) + spawn_ground_obstacle(136); } - time_to_obstacle = (rand8() & 31) + 50; + + time_to_obstacle = (rand8() & 31) + 45; } time_to_obstacle -= 1; } void render_digit(u8 digit, u8 x, u8 y) { - const u16 digit_font[] = { - 0b111101101101111, // 0 - 0b010110010010111, // 1 - 0b111001111100111, // 2 - 0b111001111001111, // 3 - 0b101101111001001, // 4 - 0b111100111001111, // 5 - 0b111100111101111, // 6 - 0b111001001001001, // 7 - 0b111101111101111, // 8 - 0b111101111001111, // 9 - }; u16 pixels = digit_font[digit]; - ssd1306_drawPixel(x + 0, y + 0, (pixels >> 14) & 1); ssd1306_drawPixel(x + 1, y + 0, (pixels >> 13) & 1); ssd1306_drawPixel(x + 2, y + 0, (pixels >> 12) & 1); @@ -218,13 +216,13 @@ void render() for (u8 i = 0; i < obstacle_count; i++) { u8 type = obstacle_types[i]; - ssd1306_drawImage(obstacle_positions[i], 31 - 8, obstacle_sprites[type], 8, 8, 0); + ssd1306_drawImage(obstacle_positions[i], 31 - 8 - obstacle_y_offset[type], obstacle_sprites[type], 8, 8, 0); } if (time & RUN_ANIM_SPEED && is_running) - ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_a, 8, 8, 0); + ssd1306_drawImage(PLAYER_X, 31 - 8 - (player_y >> Y_PRECISION), soweli_a, 8, 8, 0); else - ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_b, 8, 8, 0); + ssd1306_drawImage(PLAYER_X, 31 - 8 - (player_y >> Y_PRECISION), soweli_b, 8, 8, 0); } /* White Noise Generator State */ diff --git a/soweli/ssd1306.h b/soweli/ssd1306.h index 7fb246d..7af5d02 100644 --- a/soweli/ssd1306.h +++ b/soweli/ssd1306.h @@ -232,6 +232,103 @@ void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color) ssd1306_buffer[addr] &= ~(1 << (y & 7)); } +/* + * plot a pixel in the buffer + */ +void ssd1306_xorPixel(uint8_t x, uint8_t y) +{ + uint16_t addr; + + /* clip */ + if (x >= SSD1306_W) + return; + if (y >= SSD1306_H) + return; + + /* compute buffer address */ + addr = x + SSD1306_W * (y / 8); + + ssd1306_buffer[addr] ^= (1 << (y & 7)); +} + +/* + * draw a an image from an array, directly into to the display buffer + * the color modes allow for overwriting and even layering (sprites!) + */ +void ssd1306_drawImage(uint8_t x, uint8_t y, const unsigned char *input, uint8_t width, uint8_t height, uint8_t color_mode) +{ + uint8_t x_absolute; + uint8_t y_absolute; + uint8_t pixel; + uint8_t bytes_to_draw = width / 8; + uint16_t buffer_addr; + + for (uint8_t line = 0; line < height; line++) + { + y_absolute = y + line; + if (y_absolute >= SSD1306_H) + { + break; + } + + // SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data + // bitmask for current pixel in vertical (output) byte + uint8_t v_mask = 1 << (y_absolute & 7); + + for (uint8_t byte = 0; byte < bytes_to_draw; byte++) + { + uint8_t input_byte = input[byte + line * bytes_to_draw]; + + for (pixel = 0; pixel < 8; pixel++) + { + x_absolute = x + 8 * (bytes_to_draw - byte) + pixel; + if (x_absolute >= SSD1306_W) + { + break; + } + // looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8 + buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8); + // state of current pixel + uint8_t input_pixel = input_byte & (1 << pixel); + + switch (color_mode) + { + case 0: + // write pixels as they are + ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0); + break; + case 1: + // write pixels after inversion + ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0); + break; + case 2: + // 0 clears pixel + ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask; + break; + case 3: + // 1 sets pixel + ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0; + break; + case 4: + // 0 sets pixel + ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0; + break; + case 5: + // 1 clears pixel + ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF; + break; + } + } +#if SSD1306_LOG_IMAGE == 1 + printf("%02x ", input_byte); +#endif + } +#if SSD1306_LOG_IMAGE == 1 + printf("\n\r"); +#endif + } +} + /* * fast vert line */