DEFINE OLED_WIDTH 128 DEFINE OLED_HEIGHT 128 DEFINE SCALE_FACTOR 10 DEFINE GRAVITY 2 DEFINE JUMP_STRENGTH -20 DEFINE FRAME_DURATION 25 // ------ bird ------ DEFINE BIRD_START_X 20 DEFINE BIRD_START_Y 60 DEFINE BIRD_RADIUS 3 VAR bird_velocity_y = 0 VAR bird_y_scaled = BIRD_START_Y * SCALE_FACTOR // ------- walls ------- DEFINE WALL_THICKNESS_PIXELS 6 DEFINE WALL_START_POS_X 150 DEFINE DEFAULT_WALL_SPACING 40 DEFINE WALL_RESET_THRESHOLD_SCALED (-1 * WALL_THICKNESS_PIXELS * SCALE_FACTOR) DEFINE DEFAULT_WALL_GAP_SIZE 40 DEFINE DEFAULT_WALL_Y (OLED_HEIGHT/2) DEFINE WALL_GAP_SIZE_MIN 38 DEFINE WALL_GAP_SIZE_MAX 53 DEFINE WALL_GAP_Y_LOWER_POS_MIN (WALL_GAP_SIZE_MAX+5) DEFINE WALL_GAP_Y_LOWER_POS_MAX (OLED_HEIGHT-10) DEFINE WALL_OBJ_SIZE 6 DEFINE WALL_OBJ_OFFSET_X_POS_SCALED 0 DEFINE WALL_OBJ_OFFSET_GAP_Y_POS 2 DEFINE WALL_OBJ_OFFSET_GAP_SIZE 4 DEFINE WALL_1_OBJ_ADDR 0xF400 DEFINE WALL_2_OBJ_ADDR (WALL_1_OBJ_ADDR + WALL_OBJ_SIZE) DEFINE WALL_3_OBJ_ADDR (WALL_2_OBJ_ADDR + WALL_OBJ_SIZE) DEFINE WALL_4_OBJ_ADDR (WALL_3_OBJ_ADDR + WALL_OBJ_SIZE) VAR wall_scroll_speed_scaled = 15 VAR score = 0 FUN wall_init() POKE16(WALL_1_OBJ_ADDR+WALL_OBJ_OFFSET_X_POS_SCALED, WALL_START_POS_X*SCALE_FACTOR+DEFAULT_WALL_SPACING*SCALE_FACTOR*0) POKE16(WALL_1_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_Y_POS, DEFAULT_WALL_Y) POKE16(WALL_1_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_SIZE, DEFAULT_WALL_GAP_SIZE) POKE16(WALL_2_OBJ_ADDR+WALL_OBJ_OFFSET_X_POS_SCALED, WALL_START_POS_X*SCALE_FACTOR+DEFAULT_WALL_SPACING*SCALE_FACTOR*1) POKE16(WALL_2_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_Y_POS, DEFAULT_WALL_Y) POKE16(WALL_2_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_SIZE, DEFAULT_WALL_GAP_SIZE) POKE16(WALL_3_OBJ_ADDR+WALL_OBJ_OFFSET_X_POS_SCALED, WALL_START_POS_X*SCALE_FACTOR+DEFAULT_WALL_SPACING*SCALE_FACTOR*2) POKE16(WALL_3_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_Y_POS, DEFAULT_WALL_Y) POKE16(WALL_3_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_SIZE, DEFAULT_WALL_GAP_SIZE) POKE16(WALL_4_OBJ_ADDR+WALL_OBJ_OFFSET_X_POS_SCALED, WALL_START_POS_X*SCALE_FACTOR+DEFAULT_WALL_SPACING*SCALE_FACTOR*3) POKE16(WALL_4_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_Y_POS, DEFAULT_WALL_Y) POKE16(WALL_4_OBJ_ADDR+WALL_OBJ_OFFSET_GAP_SIZE, DEFAULT_WALL_GAP_SIZE) END_FUN FUN draw_wall(wall_obj_addr) VAR left_edge_pos = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_X_POS_SCALED) / SCALE_FACTOR VAR wall_gap_y_lower = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_GAP_Y_POS) VAR wall_gap_size = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_GAP_SIZE) VAR right_edge_pos = left_edge_pos + WALL_THICKNESS_PIXELS // Bounds checking to prevent drawing off-screen (which might wrap or error) IF left_edge_pos >= 0 PASS ELSE IF left_edge_pos >= -WALL_THICKNESS_PIXELS left_edge_pos = 0 ELSE RETURN END_IF IF right_edge_pos < OLED_WIDTH PASS ELSE IF right_edge_pos < OLED_WIDTH + WALL_THICKNESS_PIXELS right_edge_pos = OLED_WIDTH-1 ELSE RETURN END_IF OLED_RECT left_edge_pos OLED_HEIGHT right_edge_pos wall_gap_y_lower 1 OLED_RECT left_edge_pos wall_gap_y_lower-wall_gap_size right_edge_pos 0 1 END_FUN FUN move_wall(wall_obj_addr) VAR wall_x_scaled = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_X_POS_SCALED) wall_x_scaled -= wall_scroll_speed_scaled IF wall_x_scaled <= WALL_RESET_THRESHOLD_SCALED POKE16(wall_obj_addr+WALL_OBJ_OFFSET_GAP_Y_POS, RANDINT(WALL_GAP_Y_LOWER_POS_MIN, WALL_GAP_Y_LOWER_POS_MAX)) POKE16(wall_obj_addr+WALL_OBJ_OFFSET_GAP_SIZE, RANDINT(WALL_GAP_SIZE_MIN, WALL_GAP_SIZE_MAX)) wall_x_scaled = WALL_START_POS_X * SCALE_FACTOR score += 1 IF score % 4 == 0 wall_scroll_speed_scaled += 1 END_IF END_IF POKE16(wall_obj_addr+WALL_OBJ_OFFSET_X_POS_SCALED, wall_x_scaled) END_FUN FUN wall_update() move_wall(WALL_1_OBJ_ADDR) move_wall(WALL_2_OBJ_ADDR) move_wall(WALL_3_OBJ_ADDR) move_wall(WALL_4_OBJ_ADDR) draw_wall(WALL_1_OBJ_ADDR) draw_wall(WALL_2_OBJ_ADDR) draw_wall(WALL_3_OBJ_ADDR) draw_wall(WALL_4_OBJ_ADDR) END_FUN FUN game_over() VAR i = 0 WHILE i < 3 SWC_FILL 64 64 64 DELAY 250 SWC_FILL 0 0 0 DELAY 250 i += 1 END_WHILE SWC_FILL 64 0 0 OLED_CLEAR OLED_CURSOR 0 40 OLED_CPRINT Game Over! OLED_CURSOR 0 60 OLED_CPRINT Score: $score OLED_CURSOR 0 80 OLED_CPRINT Press any key OLED_UPDATE _BLOCKING_READKEY SWC_RESET 99 HALT END_FUN FUN check_wall_collision(wall_obj_addr) VAR wall_left_x = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_X_POS_SCALED) / SCALE_FACTOR VAR wall_right_x = wall_left_x + WALL_THICKNESS_PIXELS // Check 1: Is the bird's X (pixel origin) inside the wall's X range? IF (BIRD_START_X >= wall_left_x) && (BIRD_START_X <= wall_right_x) VAR gap_floor_y = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_GAP_Y_POS) VAR gap_size = PEEK16(wall_obj_addr+WALL_OBJ_OFFSET_GAP_SIZE) VAR gap_ceiling_y = gap_floor_y - gap_size VAR bird_pixel_y = bird_y_scaled / SCALE_FACTOR // Check 2: Is the bird's Y (pixel origin) hitting the solid parts? // Hit bottom pipe OR Hit top pipe IF (bird_pixel_y >= gap_floor_y) || (bird_pixel_y <= gap_ceiling_y) game_over() END_IF END_IF END_FUN FUN bird_update() bird_velocity_y += GRAVITY bird_y_scaled += bird_velocity_y VAR bird_draw_y = bird_y_scaled / SCALE_FACTOR OLED_CIRCLE BIRD_START_X bird_draw_y BIRD_RADIUS 1 // collision detection, floor and ceiling IF (bird_draw_y <= 0) || (bird_draw_y >= OLED_HEIGHT) game_over() END_IF check_wall_collision(WALL_1_OBJ_ADDR) check_wall_collision(WALL_2_OBJ_ADDR) check_wall_collision(WALL_3_OBJ_ADDR) check_wall_collision(WALL_4_OBJ_ADDR) END_FUN wall_init() WHILE 1 VAR frame_start = _TIME_MS OLED_CLEAR // Input handling VAR key = _READKEY IF key > 0 bird_velocity_y = JUMP_STRENGTH END_IF OLED_LINE 0 0 127 0 OLED_LINE 0 127 127 127 bird_update() wall_update() OLED_CURSOR 110 5 OLED_PRINT $score OLED_UPDATE VAR next_frame = frame_start + FRAME_DURATION WHILE _TIME_MS < next_frame PASS END_WHILE END_WHILE