"""Breakout!""" ___title___ = "Breakout" ___license___ = "MIT" ___categories___ = ["Games"] ___dependencies___ = ["app", "ugfx_helper", "buttons"] from tilda import Buttons import ugfx, ugfx_helper, dialogs import time import app import random import math background_colour = ugfx.BLACK framerate = 60 SCREEN_WIDTH = 240 SCREEN_HEIGHT = 320 class Ball: def __init__(self, x = 5.0, y = 5.0, dx = 2, dy = 2): self.colour = ugfx.WHITE self.diameter = 4 self.x = x self.y = y self.dy = dx self.dx = dy def centerX(self): return self.x + self.diameter / 2 def centerY(self): return self.y + self.diameter / 2 def left(self): return self.x def right(self): return self.x + self.diameter def top(self): return self.y def bottom(self): return self.y + self.diameter def draw(self): ugfx.fill_ellipse(int(self.x), int(self.y), self.diameter, self.diameter, self.colour) def clear(self): ugfx.fill_ellipse(int(self.x), int(self.y), self.diameter, self.diameter, background_colour) def bounceX(self): self.dx *= -1 def bounceY(self): self.dy *= -1 def bounceUpwards(self, ratioFromMiddle): speed = math.sqrt(self.dx * self.dx + self.dy * self.dy) self.dx = math.sin(ratioFromMiddle) * speed self.dy = math.cos(ratioFromMiddle) * speed * -1 def tick(self): self.x += self.dx self.y += self.dy if self.x < 0 or self.x + self.diameter > SCREEN_WIDTH: self.bounceX() if self.y < 0 or self.y + self.diameter > SCREEN_HEIGHT: self.bounceY() def hasCollidedWith(self, item): return self.right() >= item.left() and self.left() <= item.right() and self.top() <= item.bottom() and self.bottom() >= item.top() def isHorizontalCollision(self, item): return self.centerY() >= item.top() and self.centerY() <= item.bottom() def isVerticalCollision(self, item): return self.centerX() >= item.left() and self.centerX() <= item.right() def hasHitTop(self, item): return self.y + self.diameter >= item.top() def horizontalPositionFromMiddle(self, item): return min(1, max(0, (self.centerX() - item.left()) / (item.right() - item.left()))) - 1 class Paddle: def __init__(self, x = SCREEN_WIDTH / 2, width = SCREEN_WIDTH // 4, dx = 10): self.x = x self.dx = dx self.width = width self.height = 4 self.colour = ugfx.WHITE def left(self): return self.x - self.width / 2 def right(self): return self.x + self.width / 2 def top(self): return self.bottom() - self.height def bottom(self): return SCREEN_HEIGHT def draw(self): ugfx.area(int(self.left()), int(self.top()), self.width, self.height, self.colour) def clear(self): ugfx.area(int(self.left()), int(self.top()), self.width, self.height, background_colour) def tick(self): if Buttons.is_pressed(Buttons.JOY_Right) and self.right() < SCREEN_WIDTH: self.x += self.dx if Buttons.is_pressed(Buttons.JOY_Left) and self.left() > 0: self.x -= self.dx class Block: def __init__(self, x, y, width, height, colour = ugfx.WHITE): self.x = x self.y = y self.width = width self.height = height self.colour = colour self.visible = True def left(self): return self.x def right(self): return self.x + self.width def top(self): return self.y def bottom(self): return self.y + self.height def draw(self): colour = self.colour if self.visible else background_colour ugfx.area(int(self.left()), int(self.top()), self.width, self.height, colour) def clear(self): ugfx.area(int(self.left()), int(self.top()), self.width, self.height, background_colour) def hide(self): self.visible = False self.clear() # Clear LEDs leds = Neopix() leds.display([0,0,0]) leds.display([0,0,0]) ugfx_helper.init() ugfx.clear(background_colour) def randomColour(): return random.randint(0, 0xffffff) def gameEnd(score): ugfx.text(5, 5, str(score) + ' POINTS!!!', ugfx.WHITE) for i in range(0, 10): leds.display([randomColour(), 0]) time.sleep(0.1) leds.display([0, randomColour()]) time.sleep(0.1) leds.display([0, 0]) time.sleep(1) def gameOver(score): ugfx.text(5, 5, 'GAME OVER', ugfx.WHITE) ugfx.text(5, 30, str(score) + ' points', ugfx.WHITE) for i in range(0, 5): leds.display([0xff0000, 0]) time.sleep(0.2) leds.display([0, 0xff0000]) time.sleep(0.2) leds.display([0, 0]) time.sleep(1) def runGame(): paddle = Paddle() direction = random.random() - 0.5 initial_speed_up = 4 ball = Ball(x = SCREEN_WIDTH / 2, y = SCREEN_HEIGHT / 2, dx = math.cos(direction) * initial_speed_up, dy = math.sin(direction) * initial_speed_up) blocks = \ [Block(x = x, y = 30, width = 36, height = 10, colour = ugfx.RED) for x in range(24, SCREEN_WIDTH - 24, 40)] + \ [Block(x = x, y = 44, width = 36, height = 10, colour = ugfx.GREEN) for x in range(24, SCREEN_WIDTH - 24, 40)] + \ [Block(x = x, y = 58, width = 36, height = 10, colour = ugfx.BLUE) for x in range(24, SCREEN_WIDTH - 24, 40)] + \ [Block(x = x, y = 72, width = 36, height = 10, colour = ugfx.YELLOW) for x in range(24, SCREEN_WIDTH - 24, 40)] + \ [Block(x = x, y = 86, width = 36, height = 10, colour = ugfx.ORANGE) for x in range(24, SCREEN_WIDTH - 24, 40)] def invisibleBlocks(): return [block for block in blocks if not(block.visible)] for block in blocks: block.draw() while True: paddle.draw() ball.draw() time.sleep(1.0 / framerate) paddle.clear() ball.clear() paddle.tick() ball.tick() if Buttons.is_pressed(Buttons.BTN_Menu): gameRunning = False if all([not(block.visible) for block in blocks]): gameEnd(score = 50 + len(invisibleBlocks())) break if ball.hasHitTop(paddle): if ball.hasCollidedWith(paddle): ball.bounceUpwards(ball.horizontalPositionFromMiddle(paddle)) else: gameOver(score = len(invisibleBlocks())) break for block in blocks: if block.visible and ball.hasCollidedWith(block): block.hide() if ball.isHorizontalCollision(block): ball.bounceX() if ball.isVerticalCollision(block): ball.bounceY() runGame() app.restart_to_default()