基于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 = -5
        self.dy = -5
        self.score = 0

    def advance(self, phase):
        if not phase:
            return

        self.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 *= -1
                self.score += 10
                break

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, score

        paddle = 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 random


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)


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 = -5
        self.dy = -5
        self.score = 0

    def advance(self, phase):
        if not phase:
            return

        self.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 *= -1
                self.score += 10
                break


class MainScene(QtWidgets.QGraphicsScene):
    def __init__(self, parent=None):
        super(MainScene, self).__init__(parent)
        self.setSceneRect(0, 0, 800, 600)

        global bricks, paddle, ball, score

        paddle = 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的图形化界面开发——队列动画演示

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT_H2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值