add birds to runner game

This commit is contained in:
Crispy 2024-04-07 19:35:35 +02:00
parent 42eda85fd0
commit e2190c8dc8
3 changed files with 152 additions and 32 deletions

View file

@ -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, 0b01111111,
0b10000000, 0b10000000,
0b10101000, 0b10101000,
@ -11,7 +23,7 @@ const unsigned char soweli_a[8] = {
0b01010000, 0b01010000,
}; };
const unsigned char soweli_b[8] = { const u8 soweli_b[8] = {
0b01111111, 0b01111111,
0b10000000, 0b10000000,
0b10101000, 0b10101000,
@ -22,7 +34,14 @@ const unsigned char soweli_b[8] = {
0b00000101, 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,
0b00000000, 0b00000000,
@ -38,7 +57,6 @@ const unsigned char obstacle_sprites[OBSTACLE_TYPES][8] = {
0b00000000, 0b00000000,
0b00000000, 0b00000000,
0b00000000, 0b00000000,
0b00111100, 0b00111100,
0b01100110, 0b01100110,
0b11000011, 0b11000011,
@ -74,7 +92,14 @@ const unsigned char obstacle_sprites[OBSTACLE_TYPES][8] = {
0b00011000, 0b00011000,
0b00111100, 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};

View file

@ -109,14 +109,19 @@ void remove_obstacle()
obstacle_count--; obstacle_count--;
} }
void spawn_obstacle(u8 pos) void spawn_ground_obstacle(u8 pos)
{ {
if (obstacle_count >= MAX_OBSTACLES) if (obstacle_count >= MAX_OBSTACLES)
return; return;
obstacle_positions[obstacle_count] = pos; obstacle_positions[obstacle_count] = pos;
obstacle_types[obstacle_count] = rand8() % OBSTACLE_TYPES; obstacle_types[obstacle_count] = rand8() % GROUND_OBSTACLE_TYPES;
obstacle_count++; obstacle_count++;
} }
void spawn_air_obstacle(u8 pos)
{
spawn_ground_obstacle(pos);
obstacle_types[obstacle_count - 1] = 5;
}
void update() void update()
{ {
@ -136,7 +141,9 @@ void update()
obstacle_positions[i] -= SCROLL_SPEED; obstacle_positions[i] -= SCROLL_SPEED;
u8 type = obstacle_types[i]; u8 type = obstacle_types[i];
u8 dist_x = gfx_abs((s16)obstacle_positions[i] - (s16)PLAYER_X); 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; is_running = 0;
} }
@ -145,34 +152,25 @@ void update()
{ {
remove_obstacle(); remove_obstacle();
} }
if (time_to_obstacle < 5) if (time_to_obstacle == 0)
{ {
u8 count = 1 + (rand8() & rand8() & 1) + (rand8() & rand8() & rand8() & rand8() & 1); if (rand8() & rand8() & 1)
for (u8 i = 0; i < count; i++) 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; time_to_obstacle -= 1;
} }
void render_digit(u8 digit, u8 x, u8 y) 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]; u16 pixels = digit_font[digit];
ssd1306_drawPixel(x + 0, y + 0, (pixels >> 14) & 1); ssd1306_drawPixel(x + 0, y + 0, (pixels >> 14) & 1);
ssd1306_drawPixel(x + 1, y + 0, (pixels >> 13) & 1); ssd1306_drawPixel(x + 1, y + 0, (pixels >> 13) & 1);
ssd1306_drawPixel(x + 2, y + 0, (pixels >> 12) & 1); ssd1306_drawPixel(x + 2, y + 0, (pixels >> 12) & 1);
@ -218,13 +216,13 @@ void render()
for (u8 i = 0; i < obstacle_count; i++) for (u8 i = 0; i < obstacle_count; i++)
{ {
u8 type = obstacle_types[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) 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 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 */ /* White Noise Generator State */

View file

@ -232,6 +232,103 @@ void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color)
ssd1306_buffer[addr] &= ~(1 << (y & 7)); 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 * fast vert line
*/ */