Created
July 20, 2020 02:31
-
-
Save llimllib/f71168da1815c701c1f928fe48479fa7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from enum import Enum | |
import math | |
import pyglet | |
from pyglet import event | |
from pyglet import shapes | |
from pyglet.window import key | |
# * lose game when ball hits bottom | |
# * change angle when it hits the paddle | |
# * make brick width dependent on screen width | |
# * space bar to start | |
# * smooth motion | |
# * handle resize (?) | |
# * probably could be skipped in the name of time | |
# examples/media_player.py in the pyglet repo is an excellent example to work from: | |
# https://github.com/pyglet/pyglet/blob/0b785dc8ffac0da86513e825552b49f839173596/examples/media_player.py | |
# colors from my web tut | |
# "#FF1C0A", "#FFFD0A", "#00A308", "#0008DB", "#EB0093" | |
# def hex2tup(hex_): | |
# return (int(hex_[1:3], 16), int(hex_[3:5], 16), int(hex_[5:7], 16)) | |
brickcolors = { | |
"red": (255, 28, 10), | |
"yellow": (255, 253, 10), | |
"green": (0, 163, 8), | |
"blue": (0, 8, 219), | |
"magenta": (235, 0, 147), | |
} | |
class Ball(shapes.Circle): | |
RADIUS = 10 | |
HALF_RADIUS = 5 | |
def __init__(self, x, y, max_width, max_height, color=(255, 255, 255), batch=None): | |
super(Ball, self).__init__(x, y, self.RADIUS, color=color, batch=batch) | |
self._radius = self.RADIUS | |
self.max_width = max_width | |
self.max_height = max_height | |
self.x_velocity = max_width / 4 | |
self.y_velocity = -max_height / 4 | |
def _update_position(self): | |
super(Ball, self)._update_position() | |
def update(self, dt): | |
hit_bottom = False | |
if (self.x + self.HALF_RADIUS) > self.max_width and self.x_velocity > 0: | |
self.x_velocity *= -1 | |
if (self.x - self.HALF_RADIUS) < 0 and self.x_velocity < 0: | |
self.x_velocity *= -1 | |
if (self.y + self.HALF_RADIUS) > self.max_height and self.y_velocity > 0: | |
self.y_velocity *= -1 | |
if (self.y - self.HALF_RADIUS) < 0 and self.y_velocity < 0: | |
self.y_velocity *= -1 | |
hit_bottom = True | |
self.x += dt * self.x_velocity | |
self.y += dt * self.y_velocity | |
if hit_bottom: | |
return (self.x, self.y) | |
return None | |
class Paddle(shapes.Rectangle): | |
WIDTH = 100 | |
HEIGHT = 20 | |
def __init__(self, x, y, color=(128, 128, 128), batch=None): | |
super(Paddle, self).__init__(x, y, self.WIDTH, self.HEIGHT, color, batch) | |
class Brick(shapes.Rectangle): | |
HEIGHT = 20 | |
def __init__(self, x, y, width, color=(128, 128, 128), batch=None): | |
super(Brick, self).__init__(x, y, width, self.HEIGHT, color, batch) | |
class BreakoutWindow(pyglet.window.Window): | |
WIDTH = 960 | |
HEIGHT = 960 | |
CENTERX = WIDTH * 0.5 | |
CENTERY = HEIGHT * 0.5 | |
# margin between the left of the window and the first brick, and the right | |
# of the window and the last brick | |
MARGIN = 10 | |
# padding between bricks | |
PADDING = 10 | |
PADDLE_RATE = 400 | |
COLUMNS = 5 | |
def __init__(self): | |
super(BreakoutWindow, self).__init__(self.WIDTH, self.HEIGHT) | |
self.batch = pyglet.graphics.Batch() | |
self.fps = pyglet.text.Label( | |
"0", | |
font_size=12, | |
x=20, | |
y=40, | |
anchor_x="center", | |
anchor_y="center", | |
batch=self.batch, | |
) | |
self.ball = Ball( | |
self.CENTERX, | |
self.HEIGHT / 5, | |
self.WIDTH, | |
self.HEIGHT, | |
color=(255, 255, 255), | |
batch=self.batch, | |
) | |
self.paddle = Paddle(self.CENTERX - (Paddle.WIDTH / 2), 0, batch=self.batch) | |
brickwidth = ( | |
self.WIDTH - 2 * self.MARGIN - (self.COLUMNS - 1) * self.PADDING | |
) / self.COLUMNS | |
self.bricks = [] | |
for row, color in enumerate(brickcolors.values()): | |
rowy = self.HEIGHT - Brick.HEIGHT - self.MARGIN - row * (Brick.HEIGHT + 5) | |
for col in range(self.COLUMNS): | |
self.bricks.append( | |
Brick( | |
self.MARGIN + col * (brickwidth + self.PADDING), | |
rowy, | |
brickwidth, | |
color=color, | |
batch=self.batch, | |
) | |
) | |
# XXX: forward this responsibility to paddle? | |
self.left_is_down = False | |
self.right_is_down = False | |
self.game_over = False | |
self.mousex = None | |
pyglet.clock.schedule_interval(self.update, 1 / 60) | |
def on_key_press(self, symbol, modifiers): | |
if symbol == key.LEFT: | |
self.left_is_down = True | |
if symbol == key.RIGHT: | |
self.right_is_down = True | |
# press x to quit | |
if symbol == key.X: | |
pyglet.app.exit() | |
def on_key_release(self, symbol, modifiers): | |
if symbol == key.LEFT: | |
self.left_is_down = False | |
if symbol == key.RIGHT: | |
self.right_is_down = False | |
def on_mouse_motion(self, x, y, dx, dy): | |
self.mousex = x | |
def update(self, dt): | |
# TODO: let the user start a new game or s/t | |
if self.game_over: | |
self.game_over_label = pyglet.text.Label( | |
"GAME OVER", | |
font_size=96, | |
x=self.CENTERX, | |
y=self.CENTERY, | |
anchor_x="center", | |
anchor_y="center", | |
batch=self.batch, | |
) | |
self.batch.draw() | |
return | |
window.clear() | |
if self.left_is_down: | |
if self.paddle.x > 0: | |
self.paddle.x -= dt * self.PADDLE_RATE | |
elif self.right_is_down: | |
if self.paddle.x < self.WIDTH - self.paddle.WIDTH: | |
self.paddle.x += dt * self.PADDLE_RATE | |
elif self.mousex is not None: | |
# updating paddle.x on every mouse move gives horribly jumpy | |
# animation, so save it for the update. We still get jumpy motion, | |
# but slightly better. How to fix for real? What's going on? | |
self.paddle.x = self.mousex - self.paddle.WIDTH / 2 | |
self.mousex = None | |
hit_bottom = self.ball.update(dt) | |
if hit_bottom: | |
if not self.paddle.x < hit_bottom[0] < self.paddle.x + self.paddle.WIDTH: | |
self.game_over = True | |
# add an FPS meter | |
self.fps.text = f"{pyglet.clock.get_fps():0.0f}" | |
self.batch.draw() | |
if __name__ == "__main__": | |
window = BreakoutWindow() | |
pyglet.app.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment