Python PyQt5 でブロック崩しをつくる

Python PyQt5 でブロック崩しをつくる

玉を打ち返す

まずは玉を打ち返せるようにします。
キー入力でバーを移動させることやバーと玉の当たり判定が必要になります。

キー入力でバーを移動させるには、def keyPressEventで入力を取得します。筆者のMacの構築環境だと矢印キーの入力が得られなかったので、h(左)とl(右)でvi風の入力にしました。あと玉の発出もenterキーでするようにしたが、macだとQt.Key_Enter の値と違うようなので、直接数値を指定しました(16777220はEnterキー)。
qtのkeyイベントの入力についてリンク参照。

上記のキー入力によって、def bar_moveを呼び出してバーを移動させます。

バーと玉の判定は若干自信がないのですが、四角の判定と上の2つの角の判定を行っています。
本当はリンク先のようにやったほうがいいけど、自力で作ったらコードのようになった。
バーの場合は、下と横は考えなくもいいと思うのでそのままにしています。
次のブロックを破壊する判定は、ちゃんと2領域プラス角4つにします。
判定もそうだけど加速度も考えなければいけないな。
横に当たった場合も跳ね返るなど(当たり前)

#!/usr/bin/env python
from PyQt5.QtWidgets import (QApplication, QWidget, QComboBox, QDialog,
        QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
        QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
        QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsPixmapItem)
from PyQt5.QtGui import (QIcon, QPixmap, QBrush, QPen, QColor)
from PyQt5 import (QtCore , QtGui)
from PyQt5.QtCore import (QLineF, QPointF, QRectF, Qt, QTimer)

from PIL import Image, ImageDraw
from glob import glob
import sys
import os
import re
import cv2

class Test_graphics(QGraphicsItem):
    def __init__(self):
        super(Test_graphics, self).__init__()
        self.ball_start = 0
        self.x = 195
        self.y = 368
        self.a = 1
        self.b = -1
        self.bar_x = 175

    def paint(self, painter, option, widget):
        painter.setPen(Qt.black)
        painter.drawEllipse(self.x, self.y, 10, 10)
        painter.setPen(Qt.red)
        painter.drawRect(self.bar_x, 379, 50, 10)

    def boundingRect(self):
        return QRectF(0,0,400,400)

    def redraw(self):
        if self.ball_start == 0:
            pass
        elif self.ball_start == 1:
            self.x = self.x + 10*self.a
            self.y = self.y + 10*self.b
            if self.x <= 0:
                self.a = 1
            elif self.x >= 390:
                self.a = -1
            else:
                pass
            if self.y <= 0:
                self.b = 1
            elif self.y >= 390:
                self.b = -1
            else:
                pass
            self.collision_ball()
            self.update()
        elif self.ball_start == 2:
            pass

    def bar_move(self,input_x):
        if self.bar_x + input_x < -5:
            pass
        elif self.bar_x + input_x > 355:
            pass
        else:
            self.bar_x = self.bar_x + input_x
            if self.ball_start == 0:
                self.x = self.x + input_x
        self.update()

    def ball_move(self):
        if self.ball_start == 0:
            self.ball_start = 1
        elif self.ball_start == 1:
            self.ball_start = 2
        elif self.ball_start == 2:
            self.ball_start = 1
        self.update()

    def collision_ball(self):
        r1 = (self.x - self.bar_x)**2 + (self.y - 359)**2
        r2 = (self.x - (self.bar_x+50))**2 + (self.y - 359)**2
        if ((self.y >= 359 and self.y <=385) and (self.bar_x <= self.x and self.bar_x +50 >= self.x)):
            self.b = -1
        elif (self.y >= 359 and self.y <=375) and (100 >= r1 or 100 >= r2):
            self.b = -1

class MainWindow(QWidget):
    def __init__(self,parent=None):
        super(MainWindow, self).__init__(parent)
        path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'ico.png')
        self.view = QGraphicsView()
        scene = QGraphicsScene()
        self.item = Test_graphics()
        self.view.setSceneRect(0, 0, 400, 400);
        item = QGraphicsPixmapItem(QPixmap(path))
        item.setOffset (110, 110)
        scene.addItem(item)
        scene.addItem(self.item)
        self.view.setScene(scene)
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.view)
        self.setLayout(mainLayout)
        #windowのタイトル変更
        self.setWindowTitle("Test App8")
        self.plot_times = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.time_Event)
        self.timer.start(100)

    def time_Event(self):
        self.item.redraw()

    def keyPressEvent(self, e):
        pressed = e.key()
        if e.key() == Qt.Key_H: # 左
            self.item.bar_move(-10)
        elif e.key() == Qt.Key_L: # 右
            self.item.bar_move(10)
        elif e.key() == 16777220:
            self.item.ball_move()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'ico.png')
    app.setWindowIcon(QIcon(path))
    P = MainWindow()
    P.show()
    sys.exit(app.exec_())

ブロックの配置やブロックの当たり判定、衝突時の跳ね返り

ブロックの配置やブロックの当たり判定、衝突時の跳ね返りを行いました。
ブロックの当たり判定は4角の判定を行うと感度が良すぎるので、玉の中心と四角形の辺で行いました。
以外にブロックの色を列ごとに変化させるのが苦労しました。

#!/usr/bin/env python
from PyQt5.QtWidgets import (QApplication, QWidget, QComboBox, QDialog,
        QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
        QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
        QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsPixmapItem)
from PyQt5.QtGui import (QIcon, QPixmap, QBrush, QPen, QColor)

from PyQt5 import (QtCore , QtGui)
from PyQt5.QtCore import (QLineF, QPointF, QRectF, Qt, QTimer)

from PIL import Image, ImageDraw

from glob import glob
import sys
import os
import re
import cv2

class Test_graphics(QGraphicsItem):
    def __init__(self):
        super(Test_graphics, self).__init__()
        self.ball_start = 0
        self.x = 195
        self.y = 368
        self.a = 1
        self.b = -1
        self.bar_x = 175
        self.block = [[1 for i in range(8)] for j in range(4)]

    def paint(self, painter, option, widget):
        #draw ball
        painter.setPen(Qt.black)
        painter.setBrush(Qt.NoBrush)
        painter.drawEllipse(self.x, self.y, 10, 10)
        #draw block
        painter.setPen(Qt.white)
        pen = [[255,0,0,255],[255,255,0,255],[0,255,0,255],[0,0,255,255]]

        for i in range(0,4,1):
            for j in range(0,8,1):
                if self.block[i][j] == 1:
                    r = pen[i][0]
                    g = pen[i][1]
                    b = pen[i][2]
                    a = pen[i][3]
                    painter.setBrush(QColor(r,g,b,a))
                    painter.drawRect(50*j,20*i, 50, 20)

        #draw bar
        painter.setPen(Qt.red)
        painter.setBrush(Qt.white)
        painter.drawRect(self.bar_x, 379, 50, 10)

    def boundingRect(self):
        return QRectF(0,0,400,400)

    def redraw(self):
        if self.ball_start == 0:
            pass
        elif self.ball_start == 1:
            self.x = self.x + 10*self.a
            self.y = self.y + 10*self.b
            if self.x <= 0:
                self.a = 1
            elif self.x >= 390:
                self.a = -1
            else:
                pass
            if self.y <= 0:
                self.b = 1
            elif self.y >= 390:
                self.b = -1
            else:
                pass
            self.collision_bar()
            self.collision_block()
            self.update()
        elif self.ball_start == 2:
            pass
    def bar_move(self,input_x):
        if self.bar_x + input_x < -5:
            pass
        elif self.bar_x + input_x > 355:
            pass
        else:
            self.bar_x = self.bar_x + input_x
            if self.ball_start == 0:
                self.x = self.x + input_x
        self.update()

    def ball_move(self):
        if self.ball_start == 0:
            self.ball_start = 1
        elif self.ball_start == 1:
            self.ball_start = 2
        elif self.ball_start == 2:
            self.ball_start = 1
        self.update()

    def collision_bar(self):
        r1 = (self.x - self.bar_x)**2 + (self.y - 359)**2
        r2 = (self.x - (self.bar_x+50))**2 + (self.y - 359)**2
        if ((self.y >= 359 and self.y <=385) and (self.bar_x <= self.x and self.bar_x +50 >= self.x)):
            self.b = -1
        elif (self.y >= 359 and self.y <=375) and (100 >= r1 or 100 >= r2):
            self.b = -1

    def collision_block(self):
        for i in range(0,4,1):
            for j in range(0,8,1):
                if self.block[i][j]== 0:
                    pass
                elif self.x > 50*j and self.x < 50*(j+1) and self.y > 20*i and self.y < 20*(i+1) :
                    self.block[i][j]= 0
                    if self.x > 50*j and self.x < 50*(j+1):
                        if self.b == 1:
                            self.b = -1
                        elif self.b == -1:
                            self.b = 1
                    elif self.y > 20*i and self.y < 20*(i+1):
                        if self.a == 1:
                            self.a = -1
                        elif self.a == -1:
                            self.a = 1

class MainWindow(QWidget):
    def __init__(self,parent=None):
        super(MainWindow, self).__init__(parent)
        path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'ico.png')
        self.view = QGraphicsView()
        scene = QGraphicsScene()
        self.item = Test_graphics()
        self.view.setSceneRect(0, 0, 400, 400);
        item = QGraphicsPixmapItem(QPixmap(path))
        item.setOffset (110, 110)
        scene.addItem(item)
        scene.addItem(self.item)
        self.view.setScene(scene)
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.view)
        self.setLayout(mainLayout)
        #windowのタイトル変更
        self.setWindowTitle("Test App9")
        self.plot_times = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.time_Event)
        self.timer.start(100)

    def time_Event(self):
        self.item.redraw()

    def keyPressEvent(self, e):
        pressed = e.key()
        if e.key() == Qt.Key_H: # 左
            self.item.bar_move(-10)
        elif e.key() == Qt.Key_L: # 右
            self.item.bar_move(10)
        elif e.key() == 16777220:
            self.item.ball_move()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'ico.png')
    app.setWindowIcon(QIcon(path))
    P = MainWindow()
    P.show()
    sys.exit(app.exec_())

アプリ画面です。

音と点数のカウント(文字の表示)を追加

pyqtでの音の出し方がわからなかったので、pyaudioとsubprocessで実行しています。


#!/usr/bin/env python
from PyQt5.QtWidgets import (QApplication, QWidget, QComboBox, QDialog,
        QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
        QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
        QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsPixmapItem, QGraphicsTextItem)
from PyQt5.QtGui import (QIcon, QPixmap, QBrush, QPen, QColor)
from PyQt5 import (QtCore , QtGui)
from PyQt5.QtCore import (QLineF, QPointF, QRectF, Qt, QTimer)
from PIL import Image, ImageDraw
from glob import glob
import sys
import os
import re
import cv2
import subprocess

class Test_graphics(QGraphicsItem):
    def __init__(self):
        super(Test_graphics, self).__init__()
        self.ball_start = 0
        self.x = 195
        self.y = 368
        self.a = 1
        self.b = -1
        self.bar_x = 175
        self.block = [[1 for i in range(8)] for j in range(4)]
        self.point = 0
        self.game_over = 0
        self.sound = os.path.join(os.path.dirname(sys.modules[__name__].__file__), "cannon_sound.py")

    def paint(self, painter, option, widget):
        #draw ball
        painter.setPen(Qt.black)
        painter.setBrush(Qt.white)
        painter.drawEllipse(self.x, self.y, 10, 10)
        #draw block
        painter.setPen(Qt.white)
        pen = [[255,0,0,255],[255,255,0,255],[0,255,0,255],[0,0,255,255]]

        for i in range(0,4,1):
            for j in range(0,8,1):
                if self.block[i][j] == 1:
                    r = pen[i][0]
                    g = pen[i][1]
                    b = pen[i][2]
                    a = pen[i][3]
                    painter.setPen(Qt.white)
                    painter.setBrush(QColor(r,g,b,a))
                    painter.drawRect(50*j,20*i, 50, 20)
                    painter.setPen(Qt.white)
                    painter.setFont(QtGui.QFont("System" , 12, QtGui.QFont.Normal, False))
                    painter.drawText(QtCore.QRect(50*j,20*i, 50, 20) ,Qt.AlignCenter ,"10")
        #draw bar
        painter.setPen(Qt.red)
        painter.setBrush(Qt.white)
        painter.drawRect(self.bar_x, 379, 50, 10)

        if self.game_over == 1:
            painter.setPen(Qt.red)
            painter.setFont(QtGui.QFont("System" , 60, QtGui.QFont.Bold, False))
            painter.drawText(QtCore.QRect(0,0,400,400) ,Qt.AlignCenter ,"GAME OVER")

    def boundingRect(self):
        return QRectF(0,0,400,400)

    def redraw(self):
        if self.ball_start == 0:
            pass
        elif self.ball_start == 1:
            self.x = self.x + 10*self.a
            self.y = self.y + 10*self.b
            if self.x <= 0:
                self.a = 1
            elif self.x >= 390:
                self.a = -1
            else:
                pass
            if self.y <= 0:
                self.b = 1
            elif self.y >= 390:
                #self.b = -1
                self.game_over = 1
                self.ball_start == 0

            else:
                pass
            self.collision_bar()
            self.collision_block()
            self.update()
        elif self.ball_start == 2:
            pass
        return self.point
    def bar_move(self,input_x):
        if self.bar_x + input_x < -5:
            pass
        elif self.bar_x + input_x > 355:
            pass
        else:
            self.bar_x = self.bar_x + input_x
            if self.ball_start == 0:
                self.x = self.x + input_x
        self.update()

    def ball_move(self):
        if self.ball_start == 0:
            self.ball_start = 1
        elif self.ball_start == 1:
            self.ball_start = 2
        elif self.ball_start == 2:
            self.ball_start = 1
        self.update()

    def collision_bar(self):
        r1 = (self.x - self.bar_x)**2 + (self.y - 359)**2
        r2 = (self.x - (self.bar_x+50))**2 + (self.y - 359)**2
        if ((self.y >= 359 and self.y <=385) and (self.bar_x <= self.x and self.bar_x +50 >= self.x)):
            self.b = -1
        elif (self.y >= 359 and self.y <=375) and (100 >= r1 or 100 >= r2):
            self.b = -1

    def collision_block(self):
        for i in range(0,4,1):
            for j in range(0,8,1):
                if self.block[i][j]== 0:
                    pass
                elif self.x > 50*j and self.x < 50*(j+1) and self.y > 20*i and self.y < 20*(i+1) :
                    self.block[i][j]= 0
                    subprocess.Popen(self.sound )
                    self.point = self.point + 10
                    if self.x > 50*j and self.x < 50*(j+1):
                        if self.b == 1:
                            self.b = -1
                        elif self.b == -1:
                            self.b = 1
                    elif self.y > 20*i and self.y < 20*(i+1):
                        if self.a == 1:
                            self.a = -1
                        elif self.a == -1:
                            self.a = 1

class MainWindow(QWidget):
    def __init__(self,parent=None):
        super(MainWindow, self).__init__(parent)
        path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'ico.png')
        self.view = QGraphicsView()
        scene = QGraphicsScene()
        self.item = Test_graphics()
        self.view.setSceneRect(0, 0, 400, 400);
        item = QGraphicsPixmapItem(QPixmap(path))
        item.setOffset (110, 110)
        self.text = QGraphicsTextItem("")
        self.text.setFont( QtGui.QFont( 'System Black', 30 ) )
        self.text.setTransform( QtGui.QTransform().translate( 250, 330))
        scene.addItem(item)
        scene.addItem(self.text)
        scene.addItem(self.item)
        self.view.setScene(scene)
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.view)
        self.setLayout(mainLayout)
        self.setWindowTitle("Test App10")
        self.plot_times = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.time_Event)
        self.timer.start(100)
        if os.path.isdir("./png") == False :
            os.mkdir("./png")
        else:
            pass

    def time_Event(self):
        point = self.item.redraw()
        if point >= 100 :
            point = str(point) + "Point"
        elif point >= 10 :
            point = "0" + str(point) + "Point"
        else :
            point = "00" + str(point) + "Point"
        self.text.setPlainText(str(point))

    def keyPressEvent(self, e):
        pressed = e.key()
        if e.key() == Qt.Key_H: # 左
            self.item.bar_move(-10)
        elif e.key() == Qt.Key_L: # 右
            self.item.bar_move(10)
        elif e.key() == 16777220:
            self.item.ball_move()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    path = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'ico.png')
    app.setWindowIcon(QIcon(path))
    P = MainWindow()
    P.show()
    sys.exit(app.exec_())
#!/usr/bin/env python
import wave
import pyaudio
import sys
import os
import re

CHUNK = 1024
filename = os.path.join(os.path.dirname(sys.modules[__name__].__file__), 'cannon1.wav')
wf = wave.open(filename, "r")
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True)
data = wf.readframes(CHUNK)
while len(data) > 0:
    stream.write(data)
    data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()

Pythonカテゴリの最新記事