EMF_Camp_Badge/breakout/main.py

236 lines
6.8 KiB
Python

"""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()