【Python飞机大战游戏实战+笔记】黑马程序员Python教程项目实战记录【超详细】

  博主在哔哩哔哩上学习了黑马程序员的python教程,并且完成了老师讲的项目实战,为了巩固知识点通过这篇博客来记录一下。

目录

1. 创建python项目+pygame模块下载

1.1 创建项目

1.2 pygame下载

 2.导入模块+创建游戏窗口

2.1导入模块  

2.2 创建游戏窗口

3 添加游戏背景

3.1 创建精灵父类+背景精灵类

3.2 背景精灵类

3.3 展示背景

3.4背景图片动画

3.5设置时钟和帧率

4.敌机出现

4.1单个敌机出现

4.2多个敌机出现

4.2.1敌机定时器事件

4.2.2事件监听【按键退出+监听敌机定时器事件】

4.2.3随机速度和位置

5.飞机出现+键盘操控

5.1飞机出现

5.2 键盘控制飞机水平移动

5.3防止飞机溢出屏幕

 6.飞机发射子弹

6.1 子弹位置逻辑

6.2子弹定时器事件监听

6.3 子弹飞出屏幕销毁

 7.检查碰撞

7.1子弹敌机碰撞

​7.2飞机敌机碰撞

8.资源分享


  首先飞机大战中包含2个python文件,1个images的文件夹。

  plane_main.py就是飞机大战的主程序,plane_sprites则是定义精灵类,方便plane_main.py主程序直接导入。

  博主先画了一个思维导图来滤清思路每一个py文件的每一个类都需要定义哪些内容。

接下来就开始细化整个飞机大战的内容,开始编写代码吧!

1. 创建python项目+pygame模块下载

1.1 创建项目

打开pycharm创建一个新的python项目,我讲它命令为hm_plane,并将游戏中所需要的图片文件夹复制到项目中,并创建plane_main.py和plane_sprites.py这两个python文件。(图片和代码会在本文末尾给出)

1.2 pygame下载

  在项目最下面点击Terminal然后进行pygame安装。(首先需要安装pip,pip安装方法我在另一个博客中有写Matplotlib不显示中文解决办法_不拘于时.的博客-CSDN博客

pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple/

  等待一会后显示成功安装。

 2.导入模块+创建游戏窗口

2.1导入模块  

  想做一个游戏,首先就需要把pygame的模块导入,先给2个py文件进行模块导入。

import pygame
from plane_sprites import *

import pygame

 2.2 创建游戏窗口

先在plane_main中创建一个PlaneGame类,一般游戏的一些内容初始化的时候就应该有,如:游戏窗口、窗口背景等。

  我们先把游戏的窗口展示出来,游戏的窗口使用的是pygame.display.set_mode(),括号里需要填写游戏窗口的大小即矩形元素。游戏窗口的大小是根据背景图的像素决定的,游戏中用到的背景图是480*700,由于这个大小是固定的那我们就在plane_sprites中定义一个静态常量去存储这个矩形元素来直接调用。

pygame.Rect(x,y,width,height),其中x和y是左上角的坐标,向右x增加,向下y增加,width和height就是窗口的宽和高。

SCREEN_RECT = pygame.Rect(0,0,480,700)

  回到主程序中,完善窗口代码,set_mode()括号中需要知道长宽,SCREEN_RECT.size就代表矩形元素中的长宽。

  初始化游戏窗口后还需要展示,那什么时候才会展示呢?就是开始游戏的时候,因此我们还要设置一个开始游戏的方法来更新显示。开始游戏这一方法需要一直执行,所以通过while True来判断。什么时候结束游戏?飞机撞毁、手动退出的时候,这两种情况后续再说,所以暂时开始游戏这一方法是一直运行的。

class PlaneGame(object):

    def __init__(self):
        print("游戏初始化")

        self.screen = pygame.display.set_mode(SCREEN_RECT.size)

    def start_game(self):
        while True:
            pygame.display.update()

  这时,我们还需要在最底部创建几行代码来创建一个属于PlaneGame的对象,要不然又怎么运行start_game呢?

if __name__ == '__main__':

    # 创建游戏对象
    game = PlaneGame()

    # 启动游戏
    game.start_game()

右键运行plane_main就会出现一个黑色的游戏窗口,因为我们此时还没有设置游戏窗口。 

3 添加游戏背景

3.1 创建精灵父类+背景精灵类

在游戏开发中,在游戏中显示的图片,皆可称为精灵。通常,精灵表示游戏中所有运动的部分。

绘制图片有三要素,分别是:图片加载到内存、确定位置、更新图片。

我们在plane_sprites中,先创建一个GameSprite类。由于飞机大战中的图片都是运动的还需要定义一个update方法来修改各个图片的y坐标位置,y+=speed就说明图片会垂直向下运动。

class GameSprite(pygame.sprite.Sprite):



    def __init__(self,image_name,speed =1):
        super().__init__()
        self.image = pygame.image.load(image_name)
        self.rect = self.image.get_rect()
        self.speed = speed

    def update(self):
        self.rect.y +=self.speed

3.2 背景精灵类

 之前创建的GameSprite是一个父类,我们创建背景精灵类,只需要继承GameSprite然后写上对应的图片路径和速度即可。

class Background(GameSprite):

    def __init__(self):

        super().__init__("./images/background.png")

    def update(self):
        super().update()

3.3 展示背景

接下来的问题是,背景精灵类创建完成,我们怎么创建一个背景精灵类的对象又要怎么将背景展示?

要想创建对象就需要在plane_main中的PlaneGame中建立一个私有方法__create_prites来创建背景对象,同时需要把对象添加进背景精灵组。

    def __create_sprite(self):
        bg = Background()
        self.back_group = pygame.sprite.Group(bg)

背景精灵对象创建完成,接下来就是更新,那么我们需要在plane_main中的PlaneGame中建立一个私有方法__update_prites来更新精灵组,并将其画在屏幕上。

    def __update_sprites(self):
        self.back_group.update()
        self.back_group.draw(self.screen)

上述两个私有方法__create_sprites __update_sprites已经创建,但是我们并没有调用,因此需要在PlaneGame初始化方法中调用__create_sprite方法,在__start_game中调用__update_sprites

        self.__create_sprite()

 self.__update_sprites()

 

这时候运行主程序就会发现背景图已经显示出来了,但是随着背景图片移动有背景图案已经消失。

3.4背景图片动画

老师给的案例中整个背景是一直移动的,就出来了一个问题:当图片移出游戏窗口时怎么回到原位继续移动?答案就是通过两个一样的图片一起移动并回到原始位置,产生一种图片一直向下移动的错觉。

再设置一个背景图片精灵bg2,把bg2的y值设为图片高度的负值,这样最开始我们看到的还是bg,但是随着bg移动,我们就会看到移动下来的bg2。当我们看到的完全是bg2时再把bg的y值设为图片高度的负值,一直循环。

那么我们怎么判断背景精灵的位置呢?我们将初始化方法再设置一个变量is_alt,如果is_alt是True,说明精灵位置为-height,再用if判断精灵高度是否需要调整。

class Background(GameSprite):

    def __init__(self,is_alt=False):

        super().__init__("./images/background.png")

        if is_alt:
            self.rect.y = -self.rect.height

    def update(self):

        super().update()

        if self.rect.y >= SCREEN_RECT.height:
            self.rect.y = -self.rect.height

 再到主程序中修改一下__create_sprites,增加bg2并添加进精灵组。将bg2的is_alt设置成True,这样它最初y的位置就是-height。

    def __create_sprite(self):
        bg = Background()
        bg2 = Background(is_alt=True)
        self.back_group = pygame.sprite.Group(bg,bg2)

3.5设置时钟和帧率

但是这样背景动画会看起来特别快,为了让动画看起来更加平滑,所以我们需要创建游戏时钟设置帧率。

我们将每秒帧率设为60,这也是个常量,所以也写在plane_sprites中。

FRAME_PER_SEC = 60

 

然后在主程序的初始化方法中创建游戏时钟,再在__start_game中设置游戏的帧率。

self.clock = pygame.time.Clock()

 

self.clock.tick(FRAME_PER_SEC)

再次运行背景动画就显得平滑许多了!

4.敌机出现

4.1单个敌机出现

飞机大战中,敌机应该同时出现很多个,为了简单操作,我们先让一个敌机从左上角出现并滑行。

首先在plane_sprites中设置敌机的精灵类,重写__init__、update函数。

为了看懂显著看出敌机也是在运动的,我们把其速度设置为和背景图片速度不一样的值,我将其设置为2.

class Enemy(GameSprite):

    def __init__(self):

        super().__init__("./images/enemy1.png",2)

    def update(self):
        super().update()

然后在主程序的__create_sprites中创建敌机和精灵组,在__update_sprites中更新和绘制后,敌机即可显示出来。

enemy = Enemy()
        self.enemy_group = pygame.sprite.Group(enemy)

 

self.enemy_group.update()
        self.enemy_group.draw(self.screen)

 

 运行主程序后就会有一个敌机从左上角出现了!

4.2多个敌机出现

同一事件段出现多个敌机,可以使用定时器事件,当定时器触发时,就生成一个敌机。

4.2.1敌机定时器事件

在plane_sprites中创建敌机定时器常量。

CREATE_ENEMY_EVENT = pygame.USEREVENT

接下来在plane_main的__init__中创建生成敌机的定时器事件,假设每1s生成1个敌机,单位为毫秒,所以最后一个参数是1000.

pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)

4.2.2事件监听【按键退出+监听敌机定时器事件】

那么,怎么判断定时器事件的发生呢?这样就需要进行事件监听。如果监测到了敌机定时器事件,就会创建一个敌机。

在主程序的PlaneGame中定义一个__event_handler类,用来监听各种事件,捕捉事件用到pygame.event.get().

由于事件可能有很多种,所以用for in 进行事件的判断,首先我们来设置点击关闭按钮来关闭程序。关闭程序的操作方法是固定的,所以我们先来定义一个静态方法来封装退出游戏的步骤。

    @staticmethod
    def __game_over():
        pygame.quit()
        exit()

接下来定义__event_handler. 其中,退出按钮的类别是pygame.QUIT

    def __event_handler(self):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.__game_over()

 定义在事件监听了,但是还要调用,所以当__start_game运行时,就应该进行事件监听。

            self.__event_handler()

然后再判断敌机定时器的发生。

 elif event.type == CREATE_ENEMY_EVENT:
                enemy = Enemy()
                self.enemy_group.add(enemy)

注意,这个时候要把之前创建敌机精灵组前的创建一个敌机的代码删掉,因为和上述代码重复了,再把括号中的enemy删掉,因为enemy已经不存在了。

4.2.3随机速度和位置

但是这个时候运行程序会每个一秒在相同的位置产生一辆敌机,这显然是不符合逻辑的,因此我们还需要在设置一下敌机的初始位置和速度。

在plane_sprites中,在Enemy类中修改。为了游戏效果,我们选择随机敌机的位置和速度,需要导入random模块。

import random

 接下来就是在Enemy类中随机初始位置和速度,用到了random.randint(),因为敌机的要在屏幕里出现,所以要设置范围边界。同时,速度也是同理,所以把速度设置为1~3

这里把调用父类的__init__的速度参数删除。

    def __init__(self):

        super().__init__("./images/enemy1.png")
        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0,max_x)
        self.speed = random.randint(1,3)

 但是这个时候敌机的飞出动作很生硬,为了显得更自然,把敌机的y值设为负数,这样在我们看不见的地方开始起飞,再出现就显得平滑许多。

4.2.4飞出屏幕销毁敌机

当敌机被子弹销毁或是飞出屏幕的时候就应该被删除,先来设计当敌机飞出屏幕时如何销毁。

首先就是判断敌机的y的位置,如果大于屏幕长度就应该被销毁,使用kill()杀死,再调用一下__del__。

    def update(self):
        super().update()
        if self.rect.y > SCREEN_RECT.height:
            self.kill()

    def __del__(self):
        print("敌机销毁")

再次运行程序,当敌机飞出屏幕时就会出现“敌机销毁”字样。

5.飞机出现+键盘操控

5.1飞机出现

首先在plane_sprites创建一个Hero类,考虑飞机的位置:他不会主动垂直移动所以速度是0.水平位置应该在屏幕的居中位置,居中位置用到centerx属性。

class Hero(GameSprite):

    def __init__(self):

        super().__init__("./images/me1.png",0)
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 120

    def update(self):

        super().update()

 再和之前敌机、背景一样,在主程序__create_sprites中创建精灵,并在__update_sprites更新。

        self.hero = Hero()
        self.hero_group = pygame.sprite.Group(self.hero)

        self.hero_group.update()
        self.hero_group.draw(self.screen)

 运行程序,飞机就出现了!

 5.2 键盘控制飞机水平移动

这时候就需要用到事件监听了,需要用event.type判断,键盘按的是↑还是↓。

首先定义一个keys_pressed变量用来接收pygame.key.get_pressed()的元组,这个函数是将对应按键的值设为1,其他为0。接下来用if判断是否是左或右并改变对应的x的值。

        keys_pressed = pygame.key.get_pressed()

        if keys_pressed[pygame.K_RIGHT]:
            self.hero.rect.x += 2
        elif keys_pressed[pygame.K_LEFT]:
            self.hero.rect.x += -2

5.3防止飞机溢出屏幕

但是飞机有可能左右移动会移出游戏窗口,所以我们应该对其进行限制。

        if self.rect.x < 0:
            self.rect.x = 0
        elif self.rect.right >SCREEN_RECT.right:
            self.rect.right = SCREEN_RECT.right

 

 这时候运行主程序,按下小键盘的左右移动键就可以移动飞机了!

 6.飞机发射子弹

6.1 子弹位置逻辑

首先在create_sprites创建一个Bullet类,因为子弹是往上飞的所以速度应该为负数。其他有关子弹的代码在Hero类体现,因为飞机要发射子弹,所以在Hero中定义一个fire()方法,并在Hero的__init__中创建子弹精灵组。

class Bullet(GameSprite):

    def __init__(self):
        super().__init__("./images/bullet1.png",-2)

    def update(self):
        super().update()

因为子弹从属于飞机,所以在Hero的__init__中建立子弹精灵组。 

        self.bullets = pygame.sprite.Group()

假设飞机发射子弹时是一下发了3枚子弹,可以用for循环建立bullet()子弹精灵,为了让子弹离飞机不是很近,让子弹的bottom属于离飞机y有一定距离,再把子弹精灵添加进子弹精灵组。

    def fire(self):

        for i in (0,1,2):
            bullet = Bullet()
            bullet.rect.centerx = self.rect.centerx
            bullet.rect.bottom = self.rect.y - i * 20
            self.bullets.add(bullet)

 接下来在plane_main中创建子弹组和在__update_sprites中更新。

        self.bullet_group.update()
        self.bullet_group.draw(self.screen)

6.2子弹定时器事件监听

 这时候还没有完善,因为一直没有调用fire(),我们定义一个发射子弹的定时器来调用fire()

首先在plane_sprites中设置子弹发射定时器事件

HERO_FIRE_EVENT = pygame.USEREVENT + 1

在plane_main中将子弹发射设置为每0.5s一次

        pygame.time.set_timer(HERO_FIRE_EVENT,500)

 在__event_handler中判断是否是子弹发射事件,如果是调用fire()

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

6.3 子弹飞出屏幕销毁

销毁子弹与销毁敌机是一样的道理,在plane_sprites中判断子弹的y值是否飞出屏幕如果是,就销毁。

    def update(self):
        super().update()
        if self.rect.y < 0:
            self.kill()

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

接下来运行程序,子弹就顺利的跟着飞机走了!子弹的逻辑比较复杂,我在再次编写的时候遇到了创建精灵、精灵组、添加精灵的种种问题,希望大家注意。

 7.检查碰撞

在plane_main中定义一个__check_collide方法进行碰撞检测。

7.1子弹敌机碰撞

pygame.sprite.groupcollide(x,y,z,w)的意思分为是当x和y撞到时,x进行z(True销毁),y进行w。

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

7.2飞机敌机碰撞

使用spritecollide时会返回一个数,如果返回是数就说明碰撞,那么就调用之前设置好的__game_over.

        enemies = pygame.sprite.spritecollide(self.hero,self.enemy_group,True)
        if len(enemies):
            self.__game_over()

 

 此时飞机大战的项目就已经全局结束啦!记录一下这么长时间的python学习成果!

8.资源分享

两个py文件和一个images文件夹资源:

链接:https://pan.baidu.com/s/1wEAraFTm8jRjgiHqOX7r2g?pwd=mcps 
提取码:mcps

cmd进入存储位置后输入:python plane_main.py即可玩游戏啦。

猜你喜欢

转载自blog.csdn.net/qq_43604183/article/details/126640430
今日推荐