# @name Flappy Pixel # @desc Tiny one-button Flappy game for AWTRIX NG # @version 0.1 import math class FlappyPixel var bird_y var velocity var pipe_x var pipe_gap_y var score var highscore var running var game_over var last_ms var accumulator # Game tuning var frame_ms var gravity var flap_power def init() self.highscore = store.get("highscore") if self.highscore == nil self.highscore = 0 end self.frame_ms = 100 self.gravity = 1 self.flap_power = -2 self.reset_game() end def reset_game() self.bird_y = 3 self.velocity = 0 self.pipe_x = width() - 1 self.new_pipe() self.score = 0 self.running = false self.game_over = false self.last_ms = 0 self.accumulator = 0 end def new_pipe() # Gap starts at y 1..4. # Gap itself is 3 pixels high. self.pipe_gap_y = 1 + (math.rand() % 4) end def on_show() self.last_ms = now_ms() end def on_button(btn) if btn != "select" return end if self.game_over self.reset_game() self.running = true self.velocity = self.flap_power self.last_ms = now_ms() return end if !self.running self.running = true end self.velocity = self.flap_power end def update_game() if !self.running || self.game_over return end # Gravity self.velocity += self.gravity # Limit falling speed if self.velocity > 2 self.velocity = 2 end self.bird_y += self.velocity # Ceiling / floor collision if self.bird_y < 0 || self.bird_y > 7 self.crash() return end # Move pipe left self.pipe_x -= 1 # Bird lives at X = 5 var bird_x = 5 # Pipe is two pixels wide if bird_x >= self.pipe_x && bird_x <= self.pipe_x + 1 if self.bird_y < self.pipe_gap_y || self.bird_y > self.pipe_gap_y + 2 self.crash() return end end # Pipe passed bird if self.pipe_x + 1 == bird_x - 1 self.score += 1 end # New pipe if self.pipe_x < -2 self.pipe_x = width() self.new_pipe() end end def crash() self.game_over = true self.running = false if self.score > self.highscore self.highscore = self.score store.set("highscore", self.highscore) end end def draw_bird() var bird_x = 5 # body pixel(bird_x, self.bird_y, 0xFFFF00) # little orange beak pixel(bird_x + 1, self.bird_y, 0xFF8800) # wing animation while flying if self.running if (now_ms() % 400) < 200 if self.bird_y < 7 pixel(bird_x - 1, self.bird_y + 1, 0xFFFFFF) end else if self.bird_y > 0 pixel(bird_x - 1, self.bird_y - 1, 0xFFFFFF) end end end end def draw_pipe() var gap_start = self.pipe_gap_y var gap_end = self.pipe_gap_y + 2 # Top pipe if gap_start > 0 rect_fill(self.pipe_x, 0, 2, gap_start, 0x00CC44) end # Bottom pipe if gap_end < 7 rect_fill( self.pipe_x, gap_end + 1, 2, 7 - gap_end, 0x00CC44 ) end end def draw_idle() # Bird still visible self.draw_bird() # Small hint: blinking select indicator if (now_ms() % 1000) < 500 pixel(15, 3, 0xFFFFFF) pixel(16, 3, 0xFFFFFF) pixel(17, 3, 0xFFFFFF) end end def draw_game_over() # Flash crash if (now_ms() % 500) < 250 clear(0x300000) else clear() end font("small") text(1, 6, str(self.score), 0xFFFFFF) text(13, 6, "HI", 0x888888) text(22, 6, str(self.highscore), 0xFFFF00) end def draw() clear() if self.game_over self.draw_game_over() return end var current = now_ms() if self.last_ms == 0 self.last_ms = current end var elapsed = current - self.last_ms self.last_ms = current # Avoid massive jump after app switching if elapsed > 500 elapsed = 0 end if self.running self.accumulator += elapsed while self.accumulator >= self.frame_ms self.update_game() self.accumulator -= self.frame_ms end end self.draw_pipe() if self.running self.draw_bird() else self.draw_idle() end end end return FlappyPixel()