Created
January 18, 2019 00:42
-
-
Save shiracamus/5a865babb411183fb6e1e3807a319cce 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
import tkinter as tk | |
class Shape: | |
def __init__(self, x, y, width, height, center=False): | |
if center: | |
x -= width // 2 | |
y -= height // 2 | |
self.x1 = x | |
self.y1 = y | |
self.x2 = x + width | |
self.y2 = y + height | |
class Paddle(Shape): | |
def __init__(self, x, y, width=45, height=8, speed=6, color="blue"): | |
super().__init__(x, y, width, height, center=True) | |
self.speed = speed | |
self.color = color | |
self.name = "paddle" | |
def right(self, event): | |
self.x1 += self.speed | |
self.x2 += self.speed | |
def left(self, event): | |
self.x1 -= self.speed | |
self.x2 -= self.speed | |
def move(self): | |
pass | |
def limit(self, area): | |
adjust = (max(self.x1, area.x1) - self.x1 or | |
min(self.x2, area.x2) - self.x2) | |
self.x1 += adjust | |
self.x2 += adjust | |
class Ball(Shape): | |
def __init__(self, x, y, size=10, dx=2, dy=2, color="red"): | |
super().__init__(x, y, size, size, center=True) | |
self.dx = dx | |
self.dy = dy | |
self.color = color | |
self.name = "ball" | |
def move(self): | |
self.x1 += self.dx | |
self.y1 += self.dy | |
self.x2 += self.dx | |
self.y2 += self.dy | |
def limit(self, area): | |
if self.x1 <= area.x1 or area.x2 <= self.x2: | |
self.dx *= -1 | |
if self.y1 <= area.y1 or area.y2 <= self.y2: | |
self.dy *= -1 | |
def bound(self, target): | |
hit_x = max(self.x1, target.x1) <= min(self.x2, target.x2) | |
hit_y = max(self.y1, target.y1) <= min(self.y2, target.y2) | |
if not (hit_x and hit_y): | |
return False | |
center_x = (self.x1 + self.x2) // 2 | |
center_y = (self.y1 + self.y2) // 2 | |
if (self.dx > 0 and center_x <= target.x1 or | |
self.dx < 0 and target.x2 <= center_x): | |
self.dx *= -1 | |
if (self.dy > 0 and center_y <= target.y1 or | |
self.dy < 0 and target.y2 <= center_y): | |
self.dy *= -1 | |
return True | |
class Block(Shape): | |
def __init__(self, x, y, width, height, gap_x=0, gap_y=0, center=False, | |
color="orange", point=1): | |
super().__init__(x + gap_x, y + gap_y, | |
width - gap_x * 2, height - gap_y * 2, center=center) | |
self.point = point | |
self.color = color | |
self.name = f"block{x}.{y}" | |
self.exists = True | |
def break_and_bound(self, target): | |
if self.exists and target.bound(self): | |
self.exists = False | |
return self.point | |
else: | |
return 0 | |
def is_broken(self): | |
return not self.exists | |
class BlockRow: | |
def __init__(self, color, point): | |
self.color = color | |
self.point = point | |
class Blocks: | |
#ROWS = [BlockRow("orange", 1)] * 3 | |
ROWS = BlockRow("cyan", 10), BlockRow("yellow", 20), BlockRow("orange", 30) | |
def __init__(self, x, y, width, height, columns=12, rows=None): | |
rows = (rows or self.ROWS)[::-1] | |
w = width // columns | |
h = height // len(rows) | |
self.blocks = [Block(x + dx, y + dy, w, h, gap_x=6, gap_y=12, | |
color=row.color, point=row.point) | |
for dy, row in zip(range(0, h * len(rows) + 1, h), rows) | |
for dx in range(0, w * columns + 1, w)] | |
def __iter__(self): | |
return iter(self.blocks) | |
def break_and_bound(self, target): | |
return sum(block.break_and_bound(target) for block in self.blocks) | |
def are_wiped(self): | |
return all(block.is_broken() for block in self.blocks) | |
class Wall(Shape): | |
CATCH_LINES = 3 | |
def __init__(self, x, y, width, height): | |
super().__init__(x, y, width, height) | |
self.catch_line = self.y2 - self.CATCH_LINES | |
def catch(self, target): | |
return target.y2 >= self.catch_line | |
class Score: | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
self.score = 0 | |
self.name = "score" | |
def __str__(self): | |
return str(self.score) | |
def add(self, point): | |
self.score += point | |
class BreakoutModel: | |
def __init__(self, width, height): | |
self.paddle = Paddle(width // 2, height - 30) | |
self.ball = Ball(width // 3, height * 2 // 3) | |
self.blocks = Blocks(0, 40, width, 120) | |
self.wall = Wall(0, 0, width, height) | |
self.score = Score(width - 70, 20) | |
self.message = None | |
def update(self): | |
self.paddle.move() | |
self.paddle.limit(self.wall) | |
self.ball.move() | |
self.ball.limit(self.wall) | |
self.ball.bound(self.paddle) | |
point = self.blocks.break_and_bound(self.ball) | |
self.score.add(point) | |
self.over() | |
self.clear() | |
def over(self): | |
self.message = self.wall.catch(self.ball) and "GAME OVER(T_T)" | |
def clear(self): | |
self.message = self.blocks.are_wiped() and "GAME CLEAR(^0^)" | |
class BreakoutView: | |
SCORE_FONT = ('FixedSys', 16) | |
MESSAGE_FONT = ('FixedSys', 40) | |
def __init__(self, window, width, height): | |
self.canvas = tk.Canvas(window, width=width, height=height) | |
self.center = (width // 2, height // 2) | |
window.title("ブロック崩し") | |
window.minsize(width, height) | |
window.maxsize(width, height) | |
self.draw_rectangle = self.canvas.create_rectangle | |
self.draw_oval = self.canvas.create_oval | |
self.draw_text = self.canvas.create_text | |
def update(self, model): | |
self.ball(model.ball) | |
self.paddle(model.paddle) | |
self.blocks(model.blocks) | |
self.score(model.score) | |
self.message(model.message) | |
self.canvas.pack() | |
def delete(self, model): | |
self.canvas.delete(model.name) | |
def paddle(self, paddle): | |
self.delete(paddle) | |
self.draw_rectangle(paddle.x1, paddle.y1, paddle.x2, paddle.y2, | |
fill=paddle.color, tag=paddle.name) | |
def ball(self, ball): | |
self.delete(ball) | |
self.draw_oval(ball.x1, ball.y1, ball.x2, ball.y2, | |
fill=ball.color, tag=ball.name) | |
def blocks(self, blocks): | |
for block in blocks: | |
self.delete(block) | |
if block.exists: | |
self.draw_rectangle(block.x1, block.y1, block.x2, block.y2, | |
fill=block.color, tag=block.name) | |
def score(self, score): | |
self.delete(score) | |
self.draw_text(score.x, score.y, text=f"Score = {score}", | |
font=self.SCORE_FONT, tag=score.name) | |
def message(self, message): | |
if message: | |
self.draw_text(*self.center, text=message, font=self.MESSAGE_FONT) | |
class Breakout: | |
TICK = 20 # 更新間隔 | |
def __init__(self, width, height): | |
self.controller = window = tk.Tk() | |
self.model = BreakoutModel(width, height) | |
self.view = BreakoutView(window, width, height) | |
self.view.update(self.model) | |
self.keybind() | |
def keybind(self): | |
self.controller.bind("q", self.controller.quit) | |
self.controller.bind("<Right>", self.model.paddle.right) | |
self.controller.bind("<Left>", self.model.paddle.left) | |
def start(self): | |
self.update() | |
try: | |
self.controller.mainloop() | |
except KeyboardInterrupt: | |
self.controller.quit() | |
def update(self): | |
try: | |
if self.model.message: | |
input("Hit return key to end") | |
self.controller.quit() | |
else: | |
self.model.update() | |
self.view.update(self.model) | |
self.controller.after(self.TICK, self.update) | |
except KeyboardInterrupt: | |
self.controller.quit() | |
if __name__ == '__main__': | |
Breakout(width=600, height=480).start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment