当前位置: Python基础教程 > 12-pygame飞机大战 > 阅读正文

pygame 飞机大战 碰撞检测

2021.6.30.   994 次   5752字

本节内容, 主要介绍了”发射子弹”, 以及”碰撞检测”

发射子弹逻辑

发射子弹, 是hero的一个方法, 在hero中, 新增一个 fire 方法, 发射子弹时

  • 每隔0.5秒发射一次, 一次发射3颗
  • 子弹往上移动, 碰到敌机后消失, 或移出屏幕外消失

添加定时器常量

HERO_FIRE_EVENT = pg.USEREVENT + 1  # 发射子弹计时器

实现hero的fire方法

    def fire(self):
        print("发射子弹")

在main.py中的init方法添加定时器

        # 5.添加子弹定时器
        pg.time.set_timer(HERO_FIRE_EVENT, 500)

在游戏循环中, 监听子弹事件

            elif event.type == HERO_FIRE_EVENT:
                self.hero.fire()

完成上述代代码, 基本发射逻辑已经搭建完毕, 接下来我们需要搭建子弹类, 并使用子弹图片

搭建子弹类

在plane_sprites模块中, 新增Bullet类, 代码如下

class Bullet(GameSprite):
    """子弹精灵"""

    def __init__(self):
        # 调用父类方法, 设置子弹图片, 以及速度
        super().__init__("./images/bullet1.png", -2)  # 子弹的y减少, 故负数

    def update(self):
        # 调用父类方法, 让子弹垂直飞行
        super().update()
        # 判断子弹是否飞出屏幕
        if self.rect.bottom < 0:
            self.kill()

    def __del__(self):
        print("子弹销毁")

发射子弹实现

在hero的init方法中创建子弹精灵组

        # 创建子弹精灵组
        self.bullets = pg.sprite.Group()

在main.py的重绘方法中, 添加绘制子弹方法

        # 更新子弹
        self.hero.bullets.update()
        self.hero.bullets.draw(self.screen)

在hero的fire方法中, 完善发射子弹方法

# 创建子弹精灵
bullet = Bullet()
# 设置子弹精灵的位置
bullet.rect.bottom = self.rect.y - 20
bullet.rect.centerx = self.rect.centerx
# 添加子弹精灵到精灵组
self.bullets.add(bullet)

一次发射3颗子弹

使用for in循环, 创建子弹精灵, 设置子弹精灵的位置, 更新后的fire方法

    def fire(self):
        print("发射子弹")
        # 创建子弹精灵
        for i in (0, 1, 2):

            bullet = Bullet()
            # 设置子弹精灵的位置
            bullet.rect.bottom = self.rect.y - i * 20
            bullet.rect.centerx = self.rect.centerx
            # 添加子弹精灵到精灵组
            self.bullets.add(bullet)

子弹摧毁敌机

pygame提供了非常方便的碰撞检测方法

1.精灵组碰撞检测

groupcollide(group, group2, dokill1, dokill2, collied = None) -> Sprite_dict

如果将 dokill 设置为 True, 则发生碰撞的精灵将被自动移除

collied参数是计算碰撞的回调函数, 如果没有指定, 每一个精灵应该有rect属性

2.精灵碰撞检测

spritecollide(sprite, group, dokill, collied = None) -> Sprite_List

这个函数, 是某个精灵, 与另一个精灵组中的精灵碰撞检测方法

子弹组与敌机组碰撞检测, 应该使用的是第一个函数, 我们在main.py中找到碰撞检测方法, 代码如下

        pg.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)

已机撞毁

我们使用第二个碰撞检测的方法, 在碰撞检测方法中添加代码

        # 英雄撞毁
        enemies = pg.sprite.spritecollide(self.hero, self.enemy_group, True)
        if len(enemies) > 0:
            # 让英雄牺牲
            self.hero.kill()
            # 游戏结束
            PlaneGame.__game_over()

代码小结

到此, 代码基本完成, 后续功能请自行添加

plane_sprites模块

import random
import pygame as pg

SCREEN_RECT = pg.Rect(0, 0, 480, 700)  # 窗体属性
FRAME_PER_SEC = 60  # 刷新帧率
CREATE_ENEMY_EVENT = pg.USEREVENT  # 创建敌机计时器
HERO_FIRE_EVENT = pg.USEREVENT + 1  # 发射子弹计时器


class GameSprite(pg.sprite.Sprite):
    """飞机大战游戏精灵"""

    def __init__(self, image_name, speed=1):
        # 调用父类的初始化方法
        super().__init__()

        # 定义对象的属性
        self.image = pg.image.load(image_name)
        self.rect = self.image.get_rect()
        self.speed = speed

    def update(self):
        # 在屏幕的垂直方向上移动
        self.rect.y += self.speed


class Background(GameSprite):
    """游戏背景精灵"""

    def __init__(self, is_alt=False):

        # 1. 调用父类方法实现精灵的创建(image/rect/speed)
        super().__init__("./images/background.png")

        # 2. 判断是否是交替图像,如果是,需要设置初始位置
        if is_alt:
            self.rect.y = -self.rect.height

    def update(self):

        # 1. 调用父类的方法实现
        super().update()

        # 2. 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方
        if self.rect.y >= SCREEN_RECT.height:
            self.rect.y = -self.rect.height


class Enemy(GameSprite):
    """敌机精灵"""

    def __init__(self):
        # 1. 调用父类方法, 创建敌机精灵, 同时指定敌机图片
        super().__init__("./images/enemy1.png")

        # 2. 指定敌机的初始随机速度
        self.speed = random.randint(1, 3)
        # 3. 指定敌机的初始随机位置(x随机)
        self.rect.bottom = 0  # 指定y

        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0, max_x)

    def update(self):
        # 1. 调用父类方法, 保持垂直方向飞行
        super().update()
        # 2. 判断是否飞出屏幕, 飞出则删除
        if self.rect.y >= SCREEN_RECT.height:
            print("飞出屏幕, 需要从精灵组中删除")
            self.kill()  # 使用kill从精灵组中删除

    def __del__(self):
        print("敌机销毁" + str(self.rect))


class Hero(GameSprite):
    """英雄精灵"""

    def __init__(self):
        # 1.调用父类方法, 设置image
        super().__init__("./images/me1.png", 0)  # 设置速度为0, 不移动

        # 设置初始位置
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 120

        # 创建子弹精灵组
        self.bullets = pg.sprite.Group()

    def update(self):

        self.rect.x += self.speed
        # 控制边界
        if self.rect.x < 0:
            self.rect.x = 0

        if self.rect.right > SCREEN_RECT.right:  # right属性, 为x+width
            self.rect.right = SCREEN_RECT.right

    def fire(self):
        print("发射子弹")
        # 创建子弹精灵
        for i in (0, 1, 2):

            bullet = Bullet()
            # 设置子弹精灵的位置
            bullet.rect.bottom = self.rect.y - i * 20
            bullet.rect.centerx = self.rect.centerx
            # 添加子弹精灵到精灵组
            self.bullets.add(bullet)

class Bullet(GameSprite):
    """子弹精灵"""

    def __init__(self):
        # 调用父类方法, 设置子弹图片, 以及速度
        super().__init__("./images/bullet1.png", -2)  # 子弹的y减少, 故负数


    def update(self):
        # 调用父类方法, 让子弹垂直飞行
        super().update()
        # 判断子弹是否飞出屏幕
        if self.rect.bottom < 0:
            self.kill()

    def __del__(self):
        print("子弹销毁")

main.py代码

import pygame as pg  # 导入pygame模块
from plane_sprites import *


class PlaneGame(object):
    """飞机大战主程序"""

    def __init__(self):
        print("游戏初始化")
        pg.init()
        # 1. 创建游戏的窗口
        self.screen = pg.display.set_mode(SCREEN_RECT.size)
        # 2. 创建游戏的时钟
        self.clock = pg.time.Clock()
        # 3. 调用私有方法,精灵和精灵组的创建
        self.__create_sprites()
        # 4. 添加敌机
        pg.time.set_timer(CREATE_ENEMY_EVENT, 1000)
        # 5.添加子弹定时器
        pg.time.set_timer(HERO_FIRE_EVENT, 500)

    def __create_sprites(self):  # 创建精灵组
        # 创建背景精灵和精灵组
        bg1 = Background()
        bg2 = Background(True)

        self.back_group = pg.sprite.Group(bg1, bg2)

        self.enemy_group = pg.sprite.Group()

        # 创建英雄的精灵和精灵组
        self.hero = Hero()  # 保存到self中, 因为后续需要对它增加子弹
        self.hero_group = pg.sprite.Group(self.hero)

    def start_game(self):
        print("游戏开始...")

        while True:
            # 1. 设置刷新帧率
            self.clock.tick(FRAME_PER_SEC)
            # 2. 事件监听
            self.__event_handler()
            # 3. 碰撞检测
            self.__check_collide()
            # 4. 更新/绘制精灵组
            self.__update_sprites()
            # 5. 更新显示
            pg.display.update()

    def __event_handler(self):

        for event in pg.event.get():

            # 判断是否退出游戏
            if event.type == pg.QUIT:
                PlaneGame.__game_over()
            elif event.type == CREATE_ENEMY_EVENT:
                print("敌机出场...")
                enemy = Enemy()
                self.enemy_group.add(enemy)
            elif event.type == HERO_FIRE_EVENT:
                self.hero.fire()
            # elif event.type == pg.KEYDOWN and event.key == pg.K_RIGHT:
            #     print("向右移动")
        keys_pressed = pg.key.get_pressed()
        if keys_pressed[pg.K_RIGHT]:
            self.hero.speed = 2
        elif keys_pressed[pg.K_LEFT]:
            self.hero.speed = -2
        else:
            self.hero.speed = 0

    def __check_collide(self):  # 碰撞检测
        pg.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
        # 英雄撞毁
        enemies = pg.sprite.spritecollide(self.hero, self.enemy_group, True)
        if len(enemies) > 0:
            # 让英雄牺牲
            self.hero.kill()
            # 游戏结束
            PlaneGame.__game_over()

    def __update_sprites(self):

        # 更新背景精灵
        self.back_group.update()
        self.back_group.draw(self.screen)

        # 更新敌机精灵
        self.enemy_group.update()
        self.enemy_group.draw(self.screen)

        # 更新英雄精灵
        self.hero_group.update()
        self.hero_group.draw(self.screen)

        # 更新子弹
        self.hero.bullets.update()
        self.hero.bullets.draw(self.screen)

    @staticmethod
    def __game_over():
        print("游戏结束")

        pg.quit()
        exit()


if __name__ == "__main__":
    # 创建游戏对象
    game = PlaneGame()
    # 开始游戏
    game.start_game()

本篇完,还有疑问?

加入QQ交流群:11500065636 IT 技术交流群