0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

tkinterでブロック崩し

Last updated at Posted at 2024-01-13

tkinterを使ってブロック崩しを作りました。ell様のシューティングゲームを参考にさせていただきました。https://qiita.com/ell/items/f0f74865c07710f1eab8

だいぶお粗末なものです。壁際でたまにバグります。
当たり判定が上手くできなかった。
できたらこれにAI搭載したい。環境が固定じゃない→状態価値関数を簡単に作れないから、DQNになるのかな?やってみます。

ソースコード

ブロック崩し
import tkinter as tk
import random
from PIL import Image, ImageTk
from abc import ABC, abstractmethod

"""
クリックでスタート
カーソルで操作
"""

# ステージは縦20マス×横20マス
STAGE1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
          0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0,
          0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]

STAGE2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]

STAGE3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
          1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
          1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
          1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1,
          1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]

STAGES = [STAGE1, STAGE2, STAGE3]


WINDOW_HEIGHT = 600 # ウィンドウの高さ
WINDOW_WIDTH = 800 # ウィンドウの横幅

BAR_Y = 550 # バーの高さ
BAR_HEIGHT = 5 # バーの高さの半分,10
BAR_WIDTH = 50 # バーの横幅の半分,80

BROC_HEIGHT = 20 # ブロックの高さ
BROC_WIDTH = 40 # ブロックの横幅

BALL_RADIUS = 8 # ボールの半径
BALL_DY = 8 # ボールがSPEEDごとに進む距離
BALL_SPEED = 10 # ボールのスピード(10 ms)

COLLISION_DETECTION = 5 # 当たり判定

TEXT_CONGRATULATIONS_SIZE = 50  # congratularionsのサイズ
TEXT_GAMECLEAR_SIZE = 60        # gameclearのサイズ
TEXT_GAMEOVER_SIZE = 90         # gameoverのサイズ


class Bar: # プレイヤーが操作するバー
    def __init__(self, x, y=BAR_Y):
        self.x = x
        self.y = y
        self.draw()
        self.bind()
        
    def draw(self):
        self.id = cv.create_rectangle(
            self.x-BAR_WIDTH, self.y+BAR_HEIGHT, self.x+BAR_WIDTH, self.y-BAR_HEIGHT, fill="red")
    
    def bind(self):
        cv.bind("<Motion>", self.moved)
        
    def moved(self, event):
        if event.x > 20 and event.x < WINDOW_WIDTH-20:
            dx = event.x - self.x
            cv.move(self.id, dx, 0)
            if ball.rolling == False:
                cv.move(ball.id, dx, 0)
                ball.x = event.x
            self.x = event.x
    
    
class Ball: # ボール
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.rolling = False
        self.draw()
        self.bind()
        
    def draw(self):
        self.id = cv.create_oval(
            self.x-BALL_RADIUS, self.y-BAR_HEIGHT, self.x+BALL_RADIUS, self.y-BALL_RADIUS*2-BAR_HEIGHT, fill="blue")
        
    def bind(self):
        cv.bind("<ButtonPress>", self.fire)
    
    def fire(self, event): # ボールを発射
        if self.rolling == False:
            self.rolling = True
            dx = 0
            dy = -BALL_DY
            self.roll(dx, dy)
        
    def roll(self, dx, dy): # ボールが動く
        dx, dy = self.collision(dx, dy)
        cv.move(self.id, dx, dy)
        self.x += dx
        self.y += dy
        root.after(BALL_SPEED, self.roll, dx, dy)
        
    def collision(self, dx, dy):
        # 壁との衝突
        if self.x <= COLLISION_DETECTION or self.x >= WINDOW_WIDTH-COLLISION_DETECTION:
            dx *= -1
        if self.y <= 0:
            dy *= -1
        if self.y >= WINDOW_HEIGHT:
            gameover()
        # バーとの衝突
        if (self.x > bar.x-BAR_WIDTH-COLLISION_DETECTION) and (self.y > bar.y-BAR_HEIGHT-COLLISION_DETECTION) and \
            (self.x < bar.x+BAR_WIDTH+COLLISION_DETECTION) and (self.y < bar.y+BAR_HEIGHT+COLLISION_DETECTION):
            dx = (self.x - bar.x) / 4
            dy = -BALL_DY
        # ブロックとの衝突
        for block in blocks:
            if block.exist:
                if self.y < block.y+BROC_HEIGHT and self.y > block.y:
                    if (self.x < block.x+COLLISION_DETECTION and self.x > block.x-COLLISION_DETECTION)\
                        or (self.x < block.x+BROC_WIDTH+COLLISION_DETECTION and self.x > block.x+BROC_WIDTH-COLLISION_DETECTION):
                        block.on_hit()
                        dx *= -1
                if self.x < block.x+BROC_WIDTH and self.x > block.x:
                    if (self.y < block.y+COLLISION_DETECTION and self.y > block.y-COLLISION_DETECTION)\
                        or (self.y < block.y+BROC_HEIGHT+COLLISION_DETECTION and self.y > block.y+BROC_HEIGHT-COLLISION_DETECTION):
                        block.on_hit()
                        dy *= -1
            
        return dx, dy


class Block(ABC): # ブロック
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.exist = True
        self.draw()
        
    @abstractmethod
    def draw(self):
        ...
        
    @abstractmethod
    def on_hit(self):
        ...

        
class BreakableBlock(Block): # 壊せるブロック
        
    def draw(self):
        self.id = cv.create_rectangle(
        self.x, self.y, self.x+BROC_WIDTH, self.y+BROC_HEIGHT, fill="yellow")
        
    def on_hit(self):
        self.exist = False
        cv.delete(self.id)
        

class UnbreakableBlock(Block): # 壊せないブロック
    
    def draw(self):
        self.id = cv.create_rectangle(
        self.x, self.y, self.x+BROC_WIDTH, self.y+BROC_HEIGHT, fill="red")
        
    def on_hit(self):
        pass
        

def gameclear(): # ゲームクリア
    cnt = 0
    for block in breakableblocks:
        if block.exist == True:
            cnt += 1
    if cnt == 0:
        cv.create_text(WINDOW_WIDTH//2, WINDOW_HEIGHT//2-80, text="Congratulations!",
                       fill="lime", font=("System", TEXT_CONGRATULATIONS_SIZE))
        cv.create_text(WINDOW_WIDTH//2, WINDOW_HEIGHT//2+20, text="GAME CLEAR!",
                       fill="lime", font=("System", TEXT_GAMECLEAR_SIZE))
    root.after(1000, gameclear)
    
def gameover(): # ゲームオーバー
    cv.create_text(WINDOW_WIDTH//2, WINDOW_HEIGHT//2, text="GAME OVER",
                   fill="red", font=("System", TEXT_GAMEOVER_SIZE))
    
    
if __name__ == "__main__":
    # 初期描画
    root = tk.Tk()
    root.title("ブロック崩し")
    cv = tk.Canvas(root, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, bg="white")
    cv.pack()
    
    # メニューバー
    menubar = tk.Menu(root)
    root.configure(menu=menubar)
    menubar.add_command(label="QUIT", underline=0, command=root.quit)
    
    # インスタンス生成
    bar = Bar(WINDOW_WIDTH//2, BAR_Y)
    ball = Ball(WINDOW_WIDTH//2,BAR_Y)
    stage = random.choice(STAGES)
    blocks = []
    breakableblocks = []
    for i in range(400):
        if stage[i] == 1:
            block_i = BreakableBlock(i%20*40, (i//20)*20)
            blocks.append(block_i)
            breakableblocks.append(block_i)
        elif stage[i] == 2:
            block_i = UnbreakableBlock(i%20*40, (i//20)*20)
            blocks.append(block_i)
            
    gameclear()
    
    root.mainloop()
0
1
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?