全网首份pyQt6飞机大战

全网首份pyQt6飞机大战

该飞机大战具备较为完善的框架

可自动调整飞机与屏幕的位置

具备单点开火,半自动开火,全自动开火三种开火状态

每个作品作者花了不少头发,希望路过的大神多多留言点赞,对于不足的地方可以提出更完善的解决方法,我也会吸取教训,也会继续保持开源精神,奈何作者家境贫寒,需要不断的加班上班来养活自己,没有太多的时间精力去研究更多其他游戏,也欢迎有经济条件的大神也可以请我喝一瓶可乐,感激不尽。下面是飞机大战的图片及源码,图片素材是网上搜集的,如有侵权的请联系删除。在这里插入图片描述在这里插入图片描述在这里插入图片描述

要运行该游戏需要安装python环境 并且在控制台输入pip install pyqt6进行安装环境

使用openGL 驱动需显卡支持

完整包下载地址:点击这里

import random
import sys
import time

from PyQt6 import QtOpenGLWidgets
from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import QLabel, QApplication


# 飞机大战主类
class PlaneBigWar(QtOpenGLWidgets.QOpenGLWidget):
    """
        控制方式:
            子弹发射:
                1. 使用鼠标单点发射
                2. 使用X半自动发射
                3. 使用空格自动发射

        # 屏幕宽高,内部部件与飞机位置会随着屏幕大小改变自动改变
        screenWidth = 1300
        screenHeight = 700
        # 初始生命分数
        life = 3
        score = 0
        # 存储子弹与敌人的列表
        enemies = []
        bullets = []
        # 游戏状态控制,初始为开始状态
        STATES = {"START": 0, "RUNNING": 1, "PAUSE": 2, "GAME_OVER": 3}
        state = STATES["START"]
        # 时间间隔静态方法,可提供其他类使用
        def isActionTime(lastTime, interval)

    """
    # 屏幕宽高,内部部件与飞机位置会随着屏幕大小改变自动改变
    screenWidth = 1300
    screenHeight = 700
    # 初始生命分数
    life = 3
    score = 0
    # 存储子弹与敌人的列表
    enemies = []
    bullets = []
    # 游戏状态控制,初始为开始状态
    STATES = {"START": 0, "RUNNING": 1, "PAUSE": 2, "GAME_OVER": 3}
    state = STATES["START"]

    # 时间间隔静态方法,可提供其他类使用
    @staticmethod
    def isActionTime(lastTime, interval):
        if lastTime == 0:
            return True
        currentTime = time.time()
        return currentTime - lastTime >= interval

    # 初始化主类构造器
    def __init__(self):
        super().__init__()
        # 初始化相关变量
        self.timer = QTimer(self)
        # 初始化UI窗口
        self.initUI()
        # 入场对象的时间间隔0.5
        self.lastTime = 0
        self.interval = 0.5
        # 与英雄机碰撞的时间间隔0.5
        self.hitLastTime = 0
        self.hitInterval = 0.5
        # 初始化爆炸特效
        self.booms = [QPixmap(f"./images/b{i}.png") for i in range(1, 12)]
        # 初始化logo图片
        logo = QPixmap("./images/LOGO.png")
        # 初始化分数牌图片
        indicator = QPixmap("./images/score.png")
        # 初始化游戏开始提示图片图片
        startGame = QPixmap("./images/startGame.png")
        # 初始化暂停提示图片
        gamePause = QPixmap("./images/game_pause_nor.png")
        # 初始化图片提示图片
        gameOver = QPixmap("./images/over.png")
        # 初始化再来一次提示图片
        again = QPixmap("./images/again.png")
        # 初始化背景图片
        self.bg = QPixmap("./images/bg235.jpg")
        # 初始化英雄图片
        self.h = QPixmap("./images/hero1.png")
        # 初始化子弹图片
        self.b = QPixmap("./images/bullet1.png")
        # 初始化敌人1图片
        self.e1 = QPixmap("./images/enemy1.png")
        # 初始化敌人2图片
        self.e2 = QPixmap("./images/enemy2.png")
        # 初始化敌人3图片
        self.e3 = QPixmap("./images/enemy3.png")
        # 初始化天空类
        self.sky = Sky(self, self.bg)
        # 初始化英雄类
        self.hero = Hero(self, self.h, self.booms)
        # 初始化Logo类(图片)
        self.logo = Remind(self, PlaneBigWar.screenWidth // 2 - logo.width() // 2,
                           PlaneBigWar.screenHeight // 2 - logo.height() // 2, logo)
        # 初始化分数牌类(图片)
        self.indicator = Remind(self, PlaneBigWar.screenWidth - indicator.width(), 0, indicator)
        # 初始化游戏开始类(图片)
        self.startGame = Remind(self, PlaneBigWar.screenWidth // 2 - startGame.width() // 2,
                                PlaneBigWar.screenHeight // 2 + logo.height() // 2 + startGame.height(),
                                startGame)
        # 初始化暂停类(图片)
        self.gamePause = Remind(self, PlaneBigWar.screenWidth // 2 - gamePause.width() // 2,
                                PlaneBigWar.screenHeight // 2 - gamePause.height() // 2, gamePause)
        # 初始化游戏结束类(图片)
        self.gameOver = Remind(self, PlaneBigWar.screenWidth // 2 - gameOver.width() // 2,
                               PlaneBigWar.screenHeight // 2 - gameOver.height() // 2, gameOver)
        # 初始化再来一次类(图片)
        self.again = Remind(self, PlaneBigWar.screenWidth // 2 - again.width() // 2,
                            PlaneBigWar.screenHeight // 2 + gameOver.height() // 2 + again.height(),
                            again)
        # 初始化生命文字类
        self.lifeLabel = Text(self, PlaneBigWar.screenWidth - indicator.width() + 140, 51)
        # 初始化分数文字类
        self.scoreLabel = Text(self, PlaneBigWar.screenWidth - indicator.width() + 140, 13)

    def initUI(self):
        """
            初始化UI界面
        """
        self.initializeGL()
        # 设置窗口大小,并设置不可自动调整大小
        self.resize(PlaneBigWar.screenWidth, PlaneBigWar.screenHeight)
        self.setFixedSize(PlaneBigWar.screenWidth, PlaneBigWar.screenHeight)
        # 设置游戏标题
        self.setWindowTitle('飞机大战')
        # 设置鼠标可被自动侦测
        self.setMouseTracking(True)
        # 显示窗口
        self.show()
        # 初始化定时器自动运行run方法
        self.timer.start(10)
        self.timer.timeout.connect(self.run)

    def run(self):
        """
            程序运行体,该方法每10毫秒被调用一次
            功能与游戏状态息息相关
        """
        self.componentPaint()
        # 开始状态
        if PlaneBigWar.state == PlaneBigWar.STATES["START"]:
            self.logo.img.raise_()
            self.startGame.img.raise_()
            self.logo.paint()
            self.startGame.paint()
        # 运行状态
        elif PlaneBigWar.state == PlaneBigWar.STATES["RUNNING"]:
            self.componentEnter()
            self.componentStep()
            self.componentHit()
            self.componentDelete()

        # 暂停状态
        elif PlaneBigWar.state == PlaneBigWar.STATES["PAUSE"]:
            self.gamePause.paint()
            self.gamePause.img.raise_()

        # 游戏结束状态
        elif PlaneBigWar.state == PlaneBigWar.STATES["GAME_OVER"]:
            self.gameOver.paint()
            self.again.paint()
            self.again.img.raise_()
            self.gameOver.img.raise_()

        # 判断英雄总生命是否小于1,如果是将游戏状态设置为结束
        if PlaneBigWar.life < 1:
            PlaneBigWar.state = PlaneBigWar.STATES["GAME_OVER"]

    def enterEvent(self, event):
        """
        该方法判断鼠标是否移入了程序窗口
        如果游戏状态为暂停状态将之恢复为运行状态
        """
        if PlaneBigWar.state == PlaneBigWar.STATES["PAUSE"]:
            PlaneBigWar.state = PlaneBigWar.STATES["RUNNING"]
            self.gamePause.img.hide()

    def leaveEvent(self, event):
        """
            该方法判断鼠标是否移出了程序窗口
            如果游戏状态为运行状态将之设置为暂停状态
        """
        if PlaneBigWar.state == PlaneBigWar.STATES["RUNNING"]:
            PlaneBigWar.state = PlaneBigWar.STATES["PAUSE"]

    def mousePressEvent(self, event):
        """
        鼠标点击事件
        状态为开始时,点击鼠标设置为运行状态
        状态为结束时,点击鼠标
        游戏将被初始化为开始状态
        游戏运行时,激活单发发射子弹模式
        """
        if not self.hero.isDead:
            self.hero.shoot()
        if PlaneBigWar.state == PlaneBigWar.STATES["START"]:
            PlaneBigWar.state = PlaneBigWar.STATES["RUNNING"]
            self.logo.img.hide()
            self.startGame.img.hide()
        elif PlaneBigWar.state == PlaneBigWar.STATES["GAME_OVER"]:
            PlaneBigWar.life = 3
            PlaneBigWar.score = 0
            for enemy in PlaneBigWar.enemies:
                enemy.img.hide()
            for bullet in PlaneBigWar.bullets:
                bullet.img.hide()
            PlaneBigWar.enemies = []
            PlaneBigWar.bullets = []
            self.hero.img.hide()
            self.gameOver.img.hide()
            self.again.img.hide()
            self.hero = Hero(self, self.h, self.booms)
            PlaneBigWar.state = PlaneBigWar.STATES["START"]

    def keyPressEvent(self, QKeyEvent):
        """
        键盘按键事件,按下x键启用半自动模式
        按下空格键时,启动自动发射模式
        """
        if QKeyEvent.key() == Qt.Key.Key_X:
            if not self.hero.isDead:
                self.hero.shoot()
        if QKeyEvent.key() == Qt.Key.Key_Space:
            if self.hero.autoShoot:
                self.hero.autoShoot = False
            else:
                self.hero.autoShoot = True

    def mouseMoveEvent(self, event):
        """
        鼠标移动事件,活着的英雄机将跟随鼠标移动
        """
        if PlaneBigWar.state == PlaneBigWar.STATES["RUNNING"] and not self.hero.isDead:
            mousePos = event.position()
            pos = (int(mousePos.x()), int(mousePos.y()))
            self.hero.step(pos)

    def componentPaint(self):
        """
        游戏绘制组件,将绘制游戏中显示的图片与文字
        """
        self.sky.paint()
        self.indicator.paint()
        self.indicator.img.raise_()
        self.lifeLabel.paint(f"{PlaneBigWar.life}")
        self.lifeLabel.label.raise_()
        self.scoreLabel.paint(f"{PlaneBigWar.score}")
        self.scoreLabel.label.raise_()
        self.hero.paint()
        for enemy in PlaneBigWar.enemies:
            enemy.paint()
        for bullet in PlaneBigWar.bullets:
            bullet.paint()

    def componentHit(self):
        """
        游戏碰撞组件,用于检测某个物体与某个物体是否发生碰撞
        """
        for enemy in PlaneBigWar.enemies:
            if enemy.hit(self.hero):
                self.hero.punish()
                enemy.punish()
            for bullet in PlaneBigWar.bullets:
                if bullet.hit(enemy) and (not bullet.isDead or not enemy.isDead):
                    enemy.punish()
                    bullet.punish()

    def componentEnter(self):
        """
        游戏物体入场组件,用于添加对象至游戏
        """
        if self.hero.autoShoot and not self.hero.isDead:
            self.hero.shoot()
        if not PlaneBigWar.isActionTime(self.lastTime, self.interval):
            return
        self.lastTime = time.time()
        num = random.randint(0, 9)
        if num < 5:
            PlaneBigWar.enemies.append(Enemy(self, 1, 3, 1, self.e1, self.booms, random.randint(4, 5)))
        elif num <= 8:
            PlaneBigWar.enemies.append(Enemy(self, 2, 7, 5, self.e2, self.booms, random.randint(3, 4)))
        elif num == 9:
            if PlaneBigWar.enemies.__len__() == 0 or PlaneBigWar.enemies[0].type != 3:
                PlaneBigWar.enemies.insert(0, Enemy(self, 3, 10, 30, self.e3, self.booms, random.randint(2, 3)))

    def componentDelete(self):
        """
        删除组件,将物体状态isDelete为True的对象进行移除
        """
        for enemy in PlaneBigWar.enemies:
            enemy.isOutBounds()
            if enemy.isDelete:
                enemy.img.hide()
                PlaneBigWar.enemies.remove(enemy)
        for bullet in PlaneBigWar.bullets:
            bullet.isOutBounds()
            if bullet.isDelete:
                bullet.img.hide()
                PlaneBigWar.bullets.remove(bullet)
        if not PlaneBigWar.isActionTime(self.hitLastTime, self.hitInterval):
            return
        self.hitLastTime = time.time()
        if self.hero.life <= 0:
            self.hero.img.hide()
            if PlaneBigWar.life > 0:
                self.hero = Hero(self, self.h, self.booms)
                PlaneBigWar.life -= 1

    def componentStep(self):
        """
        游戏对象移动组件,用于移动游戏中的对象
        """
        self.sky.step()
        for enemy in PlaneBigWar.enemies:
            enemy.step()
        for bullet in PlaneBigWar.bullets:
            bullet.step()


class Sky(object):
    """
        天空类,该类作为游戏的背景,自动创建两张相同的背景从上向下移动
    """

    def __init__(self, canvas, img, speed=1):
        self.imgLab1 = QLabel(canvas)
        self.imgLab1.setPixmap(img)
        self.imgLab2 = QLabel(canvas)
        self.imgLab2.setPixmap(img)
        self.imgLab1.setMouseTracking(True)
        self.imgLab2.setMouseTracking(True)
        self.imgLab1.lower()
        self.imgLab2.lower()
        self.speed = speed
        self.x = 0
        self.y = 0
        self.width = img.width()
        self.height = img.height()

    def paint(self):
        self.imgLab1.show()
        self.imgLab2.show()
        self.imgLab1.move(self.x, self.y % self.height)
        self.imgLab2.move(self.x, (self.y - self.height) % self.height - self.height)

    def step(self):
        if self.y % self.height == 0:
            self.y = 0
        self.y += self.speed


class FlayingObject(object):
    """
    游戏物体父类,所有能被添加到游戏中的物体都继承于此类
    """

    def __init__(self, canvas, x, y, life, img, imgs, speed=2):
        self.canvas = canvas
        self.img = QLabel(canvas)
        self.img.setPixmap(img)
        self.img.setMouseTracking(True)
        self.imgs = imgs
        self.x = x
        self.y = y
        self.life = life
        self.speed = speed
        self.width = img.width()
        self.height = img.height()
        self.img.move(self.x, self.y)
        self.index = 0
        self.hitLastTime = 0
        self.hitInterval = 0.5
        self.isDead = False
        self.isDelete = False

    def isOutBounds(self):
        """
        越界方法,判断是否超出了屏幕的高度
        """
        if self.y > PlaneBigWar.screenHeight:
            self.isDelete = True

    def hit(self, obj):
        """
        碰撞算法,判断两个对象是否发生碰撞5为偏差值,使游戏更自然
        """
        x1 = self.x - obj.width + 5
        x2 = self.x + self.width - 5
        y1 = self.y - obj.height + 5
        y2 = self.y + self.height - 5
        return x1 <= obj.x <= x2 and y1 <= obj.y <= y2

    def paint(self):
        """
        绘制自己的方法,如果活着,正常绘制,死了激活动画效果,动画播放完毕激活删除
        """
        if self.isDead:
            if self.index > self.imgs.__len__() - 1:
                self.isDelete = True
            if (isinstance(self, Enemy) or isinstance(self, Hero)) and self.index < self.imgs.__len__():
                self.img.setPixmap(self.imgs[self.index])
                self.img.move(self.x - 20, self.y - 20)
                self.img.resize(self.imgs[0].width(), self.imgs[0].height())
                self.index += 1
        else:
            self.img.move(self.x, self.y)
        if self.isDelete:
            self.img.hide()
        else:
            self.img.show()

    def step(self):
        """
        移动方法,但多数子类都是从上向下所以默认从上向下.
        有特殊需求重写此方法
        """
        self.y += self.speed

    def punish(self):
        """
        惩罚与奖励方法,调用此方法该对象会获得惩罚,如果敌人获得惩罚,英雄将获得奖励
        """
        self.life -= 1
        if self.life < 1:
            self.isDead = True
            if isinstance(self, Enemy):
                if not PlaneBigWar.isActionTime(self.hitLastTime, self.hitInterval):
                    return
                self.hitLastTime = time.time()
                PlaneBigWar.score += self.score
            if isinstance(self, Bullet):
                self.isDelete = True


class Hero(FlayingObject):
    """英雄类,具备发生子弹功能"""

    def __init__(self, canvas, img, imgs):
        FlayingObject.__init__(self, canvas, PlaneBigWar.screenWidth // 2 - img.width() // 2,
                               int(PlaneBigWar.screenHeight * 0.7), 1, img, imgs)
        # 自动射击
        self.autoShoot = False
        # 射击延迟
        self.shootLastTime = 0
        self.shootInterval = 0.1
        # 子弹类型
        self.bulletType = 0

    def step(self, pos):
        """
        重写父类移动方法,定位至pos的位置
        """
        self.x = pos[0] - self.width // 2
        self.y = pos[1] - self.height // 2

    def shoot(self):
        """
        子弹设计方法
        bulletType 0: 普通子弹
        bulletType 1: 双排子弹
        bulletType 2: 散弹
        """
        if not PlaneBigWar.isActionTime(self.shootLastTime, self.shootInterval):
            return
        self.shootLastTime = time.time()
        if self.bulletType == 0:
            PlaneBigWar.bullets.append(
                Bullet(self.canvas, self.x + self.width // 2 - self.canvas.b.width() // 2,
                       self.y - self.canvas.b.height(), 1, self.canvas.b))


class Enemy(FlayingObject):
    """
    敌人类,游戏的敌人对象模板
    """

    def __init__(self, canvas, type, life, score, img, speed, imgs: list):
        FlayingObject.__init__(self, canvas, random.randint(0, PlaneBigWar.screenWidth - img.width()), -img.height(),
                               life, img, speed, imgs)
        # 敌人类型
        self.type = type
        # 敌人分数
        self.score = score


class Award(FlayingObject):
    """
    奖励类,英雄机碰到此物获得奖励
    """

    def __init__(self, canvas, type, life, code, img, speed, imgs: list):
        FlayingObject.__init__(self, canvas, random.randint(0, PlaneBigWar.screenWidth - img.width()), -img.height(),
                               life, img, speed, imgs)
        # 奖励类型
        self.type = type
        # 奖品代码
        self.code = code


class Bullet(FlayingObject):
    """
    子弹类,英雄或敌人可发射用于攻击
    """

    def __init__(self, canvas, x, y, life, img, speedX=0, speedY=-20):
        FlayingObject.__init__(self, canvas, x, y, life, img, [], speedY)
        self.speedX = speedX

    def step(self):
        """
        子弹移动方法
        """
        self.y += self.speed
        self.x += self.speedX

    def isOutBounds(self):
        """
        子弹越界方法
        """
        if self.x > PlaneBigWar.screenWidth or self.x < -self.width or self.y > PlaneBigWar.screenHeight or self.y < -self.height:
            self.isDelete = True


class Remind(FlayingObject):
    """
    提示类,用于显示图片信息的类
    """

    def __init__(self, canvas, x, y, img):
        FlayingObject.__init__(self, canvas, x, y, 1, img, [])


class Text(FlayingObject):
    """
    文本类,用于显示文字
    """

    def __init__(self, canvas, x, y, size=30, fontStyle="simhei"):
        self.canvas = canvas
        self.label = QLabel(canvas)
        self.label.resize(75, 30)
        self.label.setMouseTracking(True)
        self.label.setStyleSheet("QLabel{color:rgba(245,245,245,1);"
                                 "font-size:" + str(size) + "px;"
                                                            "font-weight:bold;"
                                                            "font-family:" + fontStyle + ";}")
        self.x = x
        self.y = y
        self.label.move(self.x, self.y)
        self.width = self.label.width()
        self.height = self.label.height()

    def paint(self, text):
        """
        绘制文字方法
        """
        self.label.setText(text)
        self.label.move(self.x, self.y)
        self.label.show()


# 程序运行入口
if __name__ == "__main__":
    app = QApplication(sys.argv)
    pbw = PlaneBigWar()
    sys.exit(app.exec())


版权声明:本文为u011245132原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。