目录
- 0. 前言
- 1. 砖块类定义
- 2. 挡板类定义
- 3. 碰撞检测
- 4. 小球和游戏初始化
- 5. 完整代码
- 6. 运行效果演示
- 7. Pyinstaller 编译exe程序
- PyQt5
0. 前言
本文使用 PyQt5实现一个打砖块小游戏
操作系统:Windows10 专业版
开发环境:Pycahrm Comunity 2022.3
Python解释器版本:Python3.8
第三方库:PyQt5
1. 砖块类定义
在构造函数__init__中,Brick类接受两个参数x和y,表示砖块初始化时的位置。通过调用super().__init__来初始化QGraphicsRectItem的基类,传入参数0, 0, 40, 20来设置砖块的初始大小。然后使用setPos方法将砖块的位置设置为给定的x和y。
接下来,使用setBrush方法设置砖块的颜色为红色。QColor(150, 0, 0)表示红色,其中150表示红色通道的亮度,而0表示绿色和蓝色通道的亮度为0。
使用setFlag方法设置QGraphicsItem.ItemIsSelectable标志,表示砖块可以被选中。
destroy方法用于移除砖块。在该方法中,使用self.scene.removeItem(self)将砖块从场景中移除。
class Brick(QtWidgets.QGraphicsRectItem):def __init__(self, x, y):super(Brick, self).__init__(0, 0, 40, 20)self.setPos(x, y)self.setBrush(QtGui.QColor(150, 0, 0))self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)def destroy(self):scene.removeItem(self)
2. 挡板类定义
这段代码定义了一个名为Paddle的类,该类继承自QGraphicsRectItem,用于表示打砖块游戏中的挡板。
在函数__init__中,Paddle类使用super().__init__方法来初始化QGraphicsRectItem的基类,设置挡板的初始大小为80像素宽和10像素高。然后使用setPos方法将挡板的位置设置为(360, 540)。
接下来,使用setBrush()方法设置挡板的颜色为蓝色。QColor(0, 0, 150)表示蓝色,其中0表示红色和绿色通道的亮度为0,而150表示蓝色通道的亮度为150。
使用setFlag方法设置QGraphicsItem.ItemIsMovable标志,表示挡板可以通过鼠标拖动来移动。
moveLeft方法用于将挡板向左移动。在该方法中,首先判断当前挡板的x坐标是否大于等于10,以确保挡板不会超出左边界。如果满足条件,则使用setPos方法将挡板的x坐标减去20,实现向左移动。
moveRight方法用于将挡板向右移动。在该方法中,首先判断当前挡板的x坐标是否小于等于710,以确保挡板不会超出右边界。如果满足条件,则使用setPos方法将挡板的x坐标加上20,实现向右移动。
class Paddle(QtWidgets.QGraphicsRectItem):def __init__(self):super(Paddle, self).__init__(0, 0, 80, 10)self.setPos(360, 540)self.setBrush(QtGui.QColor(0, 0, 150))self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)def moveLeft(self):if self.x() >= 10:self.setPos(self.x() - 20, self.y())def moveRight(self):if self.x() <= 710:self.setPos(self.x() + 20, self.y())
3. 碰撞检测
定义名为Ball的类,该类继承自QGraphicsEllipseItem,用于表示打砖块游戏中的小球。
在构造函数__init__中,Ball类使用super().__init__方法来初始化QGraphicsEllipseItem的基类,设置小球的初始大小为20像素直径。然后使用setPos方法将小球的位置设置为(390, 520)。
接下来,使用setBrush方法设置小球的颜色为绿色。QColor(0, 150, 0)表示绿色,其中0表示红色和蓝色通道的亮度为0,而150表示绿色通道的亮度为150。
Ball类还定义了dx和dy两个变量,分别表示小球在x轴和y轴上的速度。在构造函数中,将dx和dy初始化为-5,表示小球在水平和垂直方向上的速度为-5。
score变量用于记录游戏得分,初始值为0。
advance方法用于在每一帧更新时,更新小球的位置和状态。首先判断是否是更新阶段,如果不是,则直接返回。然后通过改变小球的位置,使用setPos方法将小球的x坐标增加dx,y坐标增加dy,实现小球的移动。
接下来,进行碰撞检测,判断小球是否与边界、挡板或砖块发生碰撞,并根据碰撞结果进行相应的处理。
class Ball(QtWidgets.QGraphicsEllipseItem):def __init__(self):super(Ball, self).__init__(0, 0, 20, 20)self.setPos(390, 520)self.setBrush(QtGui.QColor(0, 150, 0))self.dx = -5self.dy = -5self.score = 0def advance(self, phase):if not phase:returnself.setPos(self.x() + self.dx, self.y() + self.dy)# 左右边界反弹if self.x() <= 0 or self.x() >= 780:self.dx *= -1# 上边界反弹if self.y() <= 0:self.dy *= -1# 下边界游戏结束if self.y() >= 600:exit()# 触碰挡板反弹if self.collidesWithItem(paddle):self.dy *= -1# 计分数组for brick in bricks:if self.collidesWithItem(brick):brick.destroy()bricks.remove(brick)self.dy *= -1self.score += 10break
4. 小球和游戏初始化
这段代码定义了一个名为MainScene的类,它是基于QtWidgets.QGraphicsScene的子类。这个类用来创建游戏场景,并包括初始化场景大小、添加挡板、添加球、初始化砖块位置和计分面板等功能。
在初始化方法__init__中,设置了场景的大小为800x600,并声明了全局变量bricks、paddle、ball和score。然后创建了一个Paddle对象,并将其添加到场景中,接着创建了一个Ball对象,并将其也添加到场景中。
接下来使用一个循环来初始化砖块的位置,创建了20个Brick对象,并根据位置将其添加到场景中,并将这些砖块对象存储在bricks列表中。
在代码的最后,创建了一个QLabel对象score用于显示得分,并设置其位置和字体样式,然后将其添加到场景中。同时定义了一个keyPressEvent方法,用来捕捉键盘按键事件,当按下左方向键时,调用paddle对象的moveLeft方法,当按下右方向键时,调用paddle对象的moveRight方法。
在advance方法中,利用父类的advance方法来更新场景状态,同时根据当前球的得分来更新显示的得分文本。
class MainScene(QtWidgets.QGraphicsScene):def __init__(self, parent=None):super(MainScene, self).__init__(parent)self.setSceneRect(0, 0, 800, 600)global bricks, paddle, ball, scorepaddle = Paddle()self.addItem(paddle)ball = Ball()self.addItem(ball)# 初始化砖块位置bricks = []for i in range(20):brick = Brick((i % 4) * 200, (i // 4) * 40)if random.randint(0,2):self.addItem(brick)bricks.append(brick)score = QtWidgets.QLabel()# 设置得分面板位置score.move(350,580)score.setText("Score: 00 ")score.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))self.addWidget(score)def keyPressEvent(self, event):# 左方向键if event.key() == QtCore.Qt.Key_Left:paddle.moveLeft()# 右方向键elif event.key() == QtCore.Qt.Key_Right:paddle.moveRight()# 计分显示def advance(self):super(MainScene, self).advance()score.setText("Score: {} ".format(str(ball.score)))
5. 完整代码
只需要这一个文件就能运行:
main.py
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
import randomclass Brick(QtWidgets.QGraphicsRectItem):def __init__(self, x, y):super(Brick, self).__init__(0, 0, 40, 20)self.setPos(x, y)self.setBrush(QtGui.QColor(150, 0, 0))self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)def destroy(self):scene.removeItem(self)class Paddle(QtWidgets.QGraphicsRectItem):def __init__(self):super(Paddle, self).__init__(0, 0, 80, 10)self.setPos(360, 540)self.setBrush(QtGui.QColor(0, 0, 150))self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)def moveLeft(self):if self.x() >= 10:self.setPos(self.x() - 20, self.y())def moveRight(self):if self.x() <= 710:self.setPos(self.x() + 20, self.y())class Ball(QtWidgets.QGraphicsEllipseItem):def __init__(self):super(Ball, self).__init__(0, 0, 20, 20)self.setPos(390, 520)self.setBrush(QtGui.QColor(0, 150, 0))self.dx = -5self.dy = -5self.score = 0def advance(self, phase):if not phase:returnself.setPos(self.x() + self.dx, self.y() + self.dy)# 左右边界反弹if self.x() <= 0 or self.x() >= 780:self.dx *= -1# 上边界反弹if self.y() <= 0:self.dy *= -1# 下边界游戏结束if self.y() >= 600:exit()# 触碰挡板反弹if self.collidesWithItem(paddle):self.dy *= -1# 计分数组for brick in bricks:if self.collidesWithItem(brick):brick.destroy()bricks.remove(brick)self.dy *= -1self.score += 10breakclass MainScene(QtWidgets.QGraphicsScene):def __init__(self, parent=None):super(MainScene, self).__init__(parent)self.setSceneRect(0, 0, 800, 600)global bricks, paddle, ball, scorepaddle = Paddle()self.addItem(paddle)ball = Ball()self.addItem(ball)# 初始化砖块位置bricks = []for i in range(20):brick = Brick((i % 4) * 200, (i // 4) * 40)if random.randint(0,2):self.addItem(brick)bricks.append(brick)score = QtWidgets.QLabel()# 设置得分面板位置score.move(350,580)score.setText("Score: 00 ")score.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))self.addWidget(score)def keyPressEvent(self, event):# 左方向键if event.key() == QtCore.Qt.Key_Left:paddle.moveLeft()# 右方向键elif event.key() == QtCore.Qt.Key_Right:paddle.moveRight()# 计分显示def advance(self):super(MainScene, self).advance()score.setText("Score: {} ".format(str(ball.score)))if __name__ == '__main__':app = QApplication([])window = QMainWindow()window.setWindowTitle("打砖块小游戏 ——By IoT_H2")view = QtWidgets.QGraphicsView()scene = MainScene(window)view.setScene(scene)window.setCentralWidget(view)timer = QtCore.QTimer()timer.timeout.connect(scene.advance)timer.start(20)window.show()sys.exit(app.exec_())
6. 运行效果演示
由于我是使用了random库生成砖块位置,所以每一局场景都是不同的:
7. Pyinstaller 编译exe程序
输入这个:
Pyinstaller -F main.py
等待编译完成
在./dist可以找到main.exe
双击它就能进入打砖块小游戏啦
PyQt5
基于PyQt5的图形化界面开发——天气应用
基于PyQt5的图形化界面开发——自制MQTT客户端
基于PyQt5的图形化界面开发——Windows内存资源监视助手[附带编译exe教程]
基于PyQt5的图形化界面开发——模拟医院管理系统
基于PyQt5的图形化界面开发——自制ssh工具
基于PyQt5的图形化界面开发——PyQt示例_计算器
基于PyQt5的图形化界面开发——PyQt示例_扫雷
基于PyQt5的图形化界面开发——自制Redis图形化客户端(文末附源码)
基于PyQt5的图形化界面开发——堆栈动画演示
基于PyQt5的图形化界面开发——队列动画演示