Python笔记22——飞机大战(下)

Posted dhhtrh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python笔记22——飞机大战(下)相关的知识,希望对你有一定的参考价值。

飞机大战

敌机出场

1 使用定时器添加敌机

  1. 游戏启动后,每隔1秒会出现一架敌机
  2. 每架敌机向屏幕下方飞行,飞行速度各不相同
  3. 每架敌机出现的水平位置也不尽相同
  4. 当敌机从屏幕下方飞出,不会再飞回到屏幕中

1.1 定时器

  • pygame中可以使用pygame.time.set_timer()来添加定时器
  • 所谓定时器,就是每隔一段时间,去执行一些动作
set_timer(eventid,milliseconds)-> None
  • set_timer可以创建一个事件
  • 可以在游戏循环事件监听方法中捕获到该事件
  • 第1个参数事件代号需要基于常量pygame. USEREVENT 来指定
    • USEREVENT 是一个整数,再增加的事件可以使用USEREVENT + 1指定,依次类推…
  • 第2个参数是事件触发间隔的毫秒值

定时器事件的监听

  • 通过 pygame.event.get()可以获取当前时刻所有的事件列表
  • 遍历列表并且判断event.type是否等于eventid ,如果相等,表示定时器事件发生

1.2 定义并监听创建敌机事件

pygame定时器使用套路非常固定:

  1. 定义定时器常量——eventid
  2. 初始化方法中,调用set_timer方法设置定时器事件
  3. 游戏循环中,监听定时器事件

1) 定义事件

  • plane_sprites.py的顶部定义事件常量
#敌机出现事件
CREATE_ENENY_EVENT = pygame.USEREVENT

2 设计Enemy类

  1. 游戏启动后,每隔1秒会出现一架敌机
  2. 每架敌机向屏幕下方飞行,飞行速度各不相同
  3. 每架敌机出现的水平位置也不尽相同
  4. 当敌机从屏幕下方飞出,不会再飞回到屏幕中
GameSprite image rect speed _init__(self, image_name, speed=1) update(self) Background init__(self) update(self) Enemy _init__(self) update(self)
  • 初始化方法
    • 指定敌机图片
    • 随机敌机的初始位置和初始速度
  • 重写**update()**方法
    • 判断是否飞出屏幕,如果是,从精灵组删除

2.1 敌机类的准备

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

    def __init__(self):
        # 继承父类
        super().__init__("./images/me1.png")
        # 指定敌机速度
        # 随机敌机位置

    def update(self):
        # 继承父类
        super().update()
        # 销毁没用的精灵
        if self.rect.y >= SCREEN_RECT.height:
            print("delete")

2.2创建敌机

步骤

  1. _ create_sprites,添加敌机精灵组
  • 敌机是定时被创建的,因此在初始化方法中,不需要创建敌机
  1. _ event_handler,创建敌机,并且添加到精灵组
  • 调用精灵组add方法可以向精灵组添加精灵
  1. _ update_sprites ,让敌机精灵组调用updatedraw方法
精灵 image记录图像数据 rect记录在屏幕上的位置 update(*args) kill() 精灵组 init___(self,*精灵) add(*sprites) sprites() update(*args) draw(Surface)
    def __event_hanlder(self):
        # 捕获用户的事件并监听
        for event in pygame.event.get():
            # 判断是否是退出事件
            if event.type == pygame.QUIT:
                PlayGame.__game_over()
            elif event.type == CREATE_ENEMY_EVENT:
                print("enemy")
                # 创建敌机精灵
                enemy = Enemy()
                # 添加到精灵组
                self.enemy_group.add(enemy)

2.3 随机敌机位置和速度

1) 导入模块

  • 在导入模块时,建议按照以下顺序导入

1 官方标准模块导入
2 第三方模块导入
3 应用程序模块导入

  • 修改plane_sprites.py增加random的导入
import random

2)随机位置
使用pygame.Rect提供的bottom属性,在指定敌机初始位置时,会比较方便

  • bottom = y + height
  • y = bottom - height
    def __init__(self):
        # 继承父类
        super().__init__("./images/enemy1.png")
        # 指定敌机速度 1-3
        self.speed = random.randint(1, 3)
        # 随机敌机位置
        self.rect.bottom = 0
        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0, max_x)

2.4 移出屏幕销毁敌机

  • 敌机移出屏幕之后,如果没有撞到英雄,敌机的历史使命已经终结
  • 需要从敌机组删除,否则会造成内存浪费

检测敌机被销毁

  • _del _ 内置方法会在对象被销毁前调用,在开发中,可以用于判断对象是否被销毁
def __del__(self):
	print("敌机挂了%5"% self.rect)
  • 判断敌机是否飞出屏幕,如果是,调用kill()方法从所有组中删除
    def update(self):
        # 继承父类
        super().update()
        # 销毁没用的精灵
        if self.rect.y >= SCREEN_RECT.height:
            self.kill()

    def __del__(self):
        print("die")

英雄登场

1 设计英雄和子弹类

英雄需求

  1. 游戏启动后,英雄出现在屏幕的水平中间位置,距离屏幕底部120像素
  2. 英雄每隔0.5秒发射一次子弹,每次连发三枚子弹
  3. 英雄默认不会移动,需要通过左/右方向键,控制英雄在水平方向移动

子弹需求

  1. 子弹英雄的正上方发射沿直线上方飞行
  2. 飞出屏幕后,需要从精灵组中删除
GameSprite image rect speed _init__(self, image_name, speed=1) update(self) Background init__(self) update(self) Enemy _init__(self) update(self) Bullet init__(self) update(self) Hero bullets _init_(self) update(self) fire(self)

Hero——英雄

  • 初始化方法
    • 指定英雄图片
    • 初始速度=0——英雄默认静止不动
    • 定义bullets子弹精灵组保存子弹精灵
  • 重写**update()**方法
    • 英雄需要水平移动
    • 并且需要保证不能移出屏幕
  • 增加bullets属性,记录所有子弹精灵
  • 增加fire方法,用于发射子弹

Bullet——子弹

  • 初始化方法
    • 指定子弹图片
    • 初始速度=-2——子弹需要向上方飞行
  • 重写**update()**方法
    • 判断是否飞出屏幕,如果是,从精灵组删除

2 创建英雄

2.1 准备英雄类

  • plane_spkites新建Hero
  • 重写初始化方法,直接指定图片名称,并且将初始速度设置为0
  • 设置英雄的初始位置
Rect x, y, left, top, bottom,right, center,centerx, centery, size, width,height
  • centerx = ×+ 0.5* width
  • centery = y + 0.5 * height
  • bottom = y + height
class Hero(GameSprite):
    """英雄精灵"""

    def __init__(self):
        # 调用父类
        super().__init__("./images/me1.png")
        # 设置初始位置
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 120

2.2 绘制英雄

  1. _create_sprites,添加英雄精灵英雄精灵组
  • 后续要针对英雄碰撞检测以及发射子弹
  • 所以英雄需要单独定义成属性
    2.在_update_sprites,让英雄精灵组调用updatedraw方法

代码实现

  • 修改_create_sprites方法如下:
    def __create_sprites(self):
        # 创建背景精灵
        bg1 = Background()
        bg2 = Background(True)

        self.bg_group = pygame.sprite.Group(bg1, bg2)
        # 创建敌机精灵组
        self.enemy_group = pygame.sprite.Group()

        # 创建英雄的精灵和精灵组
        self.hero = Hero()
        self.hero_group = pygame.sprite.Group(self.hero)

3 移动英雄位置

pygame中针对键盘按键的捕获,有两种方式

  • 第一种方式判断event.type == pygame.KEYDOWN
  • 第二种方式
    1. 首先使用pygame. key.get_pressed()返回所有按键元组
    2. 通过键盘常量,判断元组中某一个键是否被按下——如果被按下,对应数值为1

区别

  • 第一种方式
elif event.type == pygame.KEYDOwN and event.key == pygame.K_RIGHT:

Python——飞机大战(day10)

目录

一、面向过程实现

二、面向对象


plane pro需求的描述:

存在四个对象:
我方飞机、敌方飞机、我方子弹、敌方子弹
功能:

背景音乐的添加

我方飞机可以移动【根据按键来控制的】
敌方飞机也可以移动【随机的自动移动】

双方飞机都可以发送子弹

步骤:
1.创建一个窗口
2.创建一个我方飞机 根据方向键左右的移动
3.给我方飞机添加发射子弹的功能【按下空格键去发送】
4.创建一个敌人飞机
5.敌人飞机可以自由的移动
6.敌人飞机可以自动的发射子弹

在安装pygame模块的时候尤其要注意一下:

如果在pychram中安装不成功  如下错误:
   EOFError: EOF when reading a line

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in C:\\Users\\Administrator\\AppData\\Local\\Temp\\pip-install-392v1at0\\pygame\\

此时我们就可以换种思路:

1:确保在系统层面的python环境里面 已经安装了pygame[pip install pygame] 一般都可以安装成功
2: 我们就可以把 已经安装好的 pygame 模块的 文件夹拷贝到 pycharm 所创建项目中的venv虚拟环境里面【E:\\PythonPro\\PlaneDemo\\venv\\Lib\\site-packages】

soure path:C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python38-32\\Lib\\site-packages
dst path: 你的项目路径/venv\\Lib\\site-packages
 

一、面向过程实现

1、首先设置进行窗口、背景设置

import pygame # 引用pygame模块
from pygame.locals import  *
import time

def main():

#----------------------------- 窗口、背景的基本设置 -------------------------

    # 首先创建一个窗口,用来显示内容
    screen = pygame.display.set_mode((350,500)) # 设置宽、高:350,500
    # 创建一个背景图片对象
    background = pygame.image.load('./feiji/background.png') # 当前路径下的
    # 设置一个标题
    pygame.display.set_caption('阶段总结-飞机大战')
    # 添加背景音乐
    pygame.mixer.init() # 初始化这个函数
    pygame.mixer.music.load('./feiji/background.mp3')
    pygame.mixer.music.set_volume((0.2)) # 0-1之间设置音量
    pygame.mixer.music.play(-1) # 设置循环次数,-1表示无限循环
    #函数原型:pygame.key.set_repeat(delay, interval)

2、载入己方和地方飞机

# 载入玩家的飞机图片
    hero = pygame.image.load('./feiji/hero.png')
    # 初始化玩家的位置:
    x, y = 155, 450 # 玩家飞机hero大小:40×50;(155, 450)表示在窗口最下方居中显示的位置

3、循环更新显示界面

    # 设定要显示的内容
    while True:
        screen.blit(background, (0, 0)) # 显示背景图片,(对象,居中显示),(0,0)表示从窗口的(0,0)坐标开始显示
        screen.blit(hero, (x, y)) # 显示玩家飞机
        # 获取键盘事件
        eventlist = pygame.event.get()
        for event in eventlist:
            if event.type == QUIT:
                print('退出')
                exit()
                pass
            elif event.type == KEYDOWN: # 是否按下了键盘
                if event.key == K_a or event.key == K_LEFT: # 按下了左键
                    print('left')
                    if x > 0:
                        x -= 5
                    elif x < 0:
                        x = 0
                        pass

                elif event.key == K_d or event.key == K_RIGHT: # 按下了右键
                    print('right')
                    if x < 310:
                        x += 5
                    elif x > 310:
                        x = 310
                        pass

                elif event.key == K_SPACE:
                    print('K_SPACE')
        # 更新显示内容
        pygame.display.update()
    pass

if __name__ == '__main__':
    main()

到这里发现,可以控制飞机移动,但是每按下一次键盘只能移动一次,好像不符合正常游戏要求

所以添加

pygame.key.set_repeat(10, 15) # 放到初始化中,这两个参数都是以ms为单位的

 函数即可实现,按下连续移动

只需要将这段代码放到初始化中即可!(ps:)

代码的具体意义:
在pygame中对按键的连续检测是默认失能的,调用上述函数便可以使能按键的连续检测。按键的连续检测使能后,当按键按下时,将会以delay的延时去触发第一次的KEYDOWN事件,之后则会以interval的延时去触发接下来的KEYDOWN事件。通俗的讲,第一个参数影响着按键的灵敏度,第二个参数影响着按键的移动时间间隔。即:以上述(10,15)来说,每次按下键盘触发KEYDOWN事件后,延时10ms后再次检测是否有还在按下,若在10ms内有按下,则之后以15ms的延时连续触发接下来的KEYDOWN。

二、面向对象

import pygame # 引用pygame模块
from pygame.locals import  *
import time
import random
# ---------------------------------- 面对对象 -------------------------------------

'''
1:实现飞机的显示,并且可以控制飞机的移动【面向对象】
'''
# 抽象出来一个飞机的基类
class BasePlane(object):
    def __init__(self, screen, imagePath):
        '''
        初始化基类函数
        :param screen: 主窗体对象
        :param imageName: 加载的图片
        '''
        self.screen = screen
        self.image = pygame.image.load(imagePath)
        self.bulletlist = [] #存储所有的子弹
        pass
    def display(self):
        self.screen.blit(self.image, (self.x, self.y))
        # 完善子弹的展示逻辑,删除越界的子弹,释放内存
        needDelItemList = []
        for item in self.bulletlist:
            if item.judge():
                needDelItemList.append(item)
                pass
            pass
        # 重新遍历一遍
        for i in needDelItemList:
            self.bulletlist.remove(i)
            pass
        for bullet in self.bulletlist:
            bullet.display()  # 显示子弹的位置
            bullet.move()  # 让这个子弹移动,下次再显示的就会看到子弹在修改后的位置
        pass
    pass

class CommonBullet(object):
    '''
    公共的子弹类
    '''
    def __init__(self, x, y, screen, bullettype):
        self.type = bullettype
        self.screen = screen
        if self.type == "hero":
            self.x += 20
            self.y -= 20
            self.imagePath = './feiji/bullet.png'
        elif self.type == "enemy":
            self.x = x
            self.y += 10
            self.imagePath = './feiji/bullet1.png'
        self.image = pygame.image.load(self.imagePath)
    def move(self):
        '''
        子弹的移动
        :return:
        '''
        if self.type == 'hero':
            self.y -= 2
        elif self.type == 'enemy':
            self.y += 2
    def display(self):
        self.screen.blit(self.image,(self.x, self.y))
    pass

class HeroPlane(BasePlane):
    def __init__(self, screen):
        '''
        初始化函数
        :param screen: 主窗体对象
        '''

        super().__init__(screen, './feiji/hero.png') # 调用父类的构造方法
        # 飞机的默认位置
        self.x = 155
        self.y = 450
        pass
    def moveleft(self):
        '''
        左移动
        :return:
        '''
        if self.x > 0:
            self.x -= 5
        elif self.x <= 0:
            self.x = 0
        pass
    def moveright(self):
        '''
        右移动
        :return:
        '''
        if self.x < 310:
            self.x += 5
        elif self.x >= 310:
            self.x = 310
        pass
    def moveup(self):
        '''
        上移动
        :return:
        '''
        if self.y > 0:
            self.y -= 5
        elif self.y <= 0:
            self.y = 0
        pass
    def movedown(self):
        '''
        下移动
        :return:
        '''
        if self.y < 450:
            self.y += 5
        elif self.y >= 450:
            self.y = 450
        pass
    # 发射子弹
    def ShotBullet(self):
        # 创建一个新的子弹对象
        newbullet = CommonBullet(self.x, self.y, self.screen, 'hero')
        self.bulletlist.append(newbullet)
        pass

'''
创建敌机类
'''
class EnemyPlane(BasePlane):
    def __init__(self, screen):
        super().__init__(screen, './feiji/enemy0.png')
        # 默认设置一个方向
        self.direction = 'right'
        # 飞机的默认位置
        self.x = 0
        self.y = 0
        pass
    def shotenemybullet(self):
        '''
        敌机随机发射
        :return:
        '''
        num = random.randint(1,50)
        if num == 3:
            newbullet = CommonBullet(self.x, self.y, self.screen, 'enemy')
            self.bulletlist.append(newbullet)
        pass
    def move(self):
        '''
        敌机移动 随机进行
        :return:
        '''
        if self.direction == 'right':
            self.x += 2
        elif self.direction == 'left':
            self.x -= 2
        if self.x > 330:
            self.direction = 'left'
        elif self.x < 0:
            self.direction = 'right'
    pass

def key_control(HeroObj):
    '''
    定义普通的函数,用来实现键盘的检测
    :param HeroObj:可控制检测的对象
    :return:
    '''

    # 获取键盘事件
    eventlist = pygame.event.get()
    # pygame.key.set_repeat(10, 15)
    pygame.key.set_repeat(200, 15)
    # pygame.key.set_repeat(200, 200)
    for event in eventlist:
        if event.type == QUIT:
            print('退出')
            exit()
            pass
        elif event.type == KEYDOWN:  # 是否按下了键盘
            if event.key == K_a or event.key == K_LEFT:  # 按下了左键
                print('left')
                HeroObj.moveleft() # 调用函数实现左移动
                pass
            elif event.key == K_d or event.key == K_RIGHT:  # 按下了右键
                print('right')
                HeroObj.moveright()  # 调用函数实现右移动
                pass
            elif event.key == K_w or event.key == K_UP:  # 按下了右键
                print('up')
                HeroObj.moveup()  # 调用函数实现上移动
                pass
            elif event.key == K_s or event.key == K_DOWN:  # 按下了右键
                print('down')
                HeroObj.movedown()  # 调用函数实现下移动
                pass
            elif event.key == K_SPACE:
                print('K_SPACE')
                HeroObj.ShotBullet()


def main():
#----------------------------- 窗口、背景的基本设置 -------------------------
    # 首先创建一个窗口,用来显示内容
    screen = pygame.display.set_mode((350,500)) # 设置宽、高:350,500
    # 创建一个背景图片对象
    background = pygame.image.load('./feiji/background.png') # 当前路径下的
    # 设置一个标题
    pygame.display.set_caption('阶段总结-飞机大战')
    # 添加背景音乐
    pygame.mixer.init() # 初始化这个函数
    pygame.mixer.music.load('./feiji/background.mp3')
    pygame.mixer.music.set_volume((0.2)) # 0-1之间设置音量
    pygame.mixer.music.play(-1) # 设置循环次数,-1表示无限循环
    #函数原型:pygame.key.set_repeat(delay, interval)

    # 创建一个飞机对象
    hero = HeroPlane(screen)
    # 创建一个敌人
    enemyplane = EnemyPlane(screen)

    # 设定要显示的内容
    while True:
        screen.blit(background, (0, 0)) # 显示背景图片,(对象,居中显示),(0,0)表示从窗口的(0,0)坐标开始显示
        hero.display()
        enemyplane.display() # 显示敌机
        enemyplane.move()  # 敌机移动
        enemyplane.shotenemybullet() # 敌机随机发射子弹
        # 获取键盘事件
        key_control(hero)
        # 更新显示内容
        pygame.display.update()
        # time.sleep(1)
        pygame.time.Clock().tick(15) # 每秒发生15次
    pass

if __name__ == '__main__':
    main()


  

以上是关于Python笔记22——飞机大战(下)的主要内容,如果未能解决你的问题,请参考以下文章

python小游戏——飞机大战代码开源

python3飞机大战

如何用用python写飞机大战?

怎么用python学飞机大战?

小甲鱼python基础教程飞机大战源码及素材

Python——飞机大战(day10)