Python笔记22——飞机大战(下)
Posted dhhtrh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python笔记22——飞机大战(下)相关的知识,希望对你有一定的参考价值。
飞机大战
敌机出场
1 使用定时器添加敌机
- 游戏启动后,每隔1秒会出现一架敌机
- 每架敌机向屏幕下方飞行,飞行速度各不相同
- 每架敌机出现的水平位置也不尽相同
- 当敌机从屏幕下方飞出,不会再飞回到屏幕中
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的定时器使用套路非常固定:
- 定义定时器常量——eventid
- 在初始化方法中,调用set_timer方法设置定时器事件
- 在游戏循环中,监听定时器事件
1) 定义事件
- 在plane_sprites.py的顶部定义事件常量
#敌机出现事件
CREATE_ENENY_EVENT = pygame.USEREVENT
2 设计Enemy类
- 游戏启动后,每隔1秒会出现一架敌机
- 每架敌机向屏幕下方飞行,飞行速度各不相同
- 每架敌机出现的水平位置也不尽相同
- 当敌机从屏幕下方飞出,不会再飞回到屏幕中
- 初始化方法
- 指定敌机图片
- 随机敌机的初始位置和初始速度
- 重写**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创建敌机
步骤
- 在_ create_sprites,添加敌机精灵组
- 敌机是定时被创建的,因此在初始化方法中,不需要创建敌机
- 在_ event_handler,创建敌机,并且添加到精灵组
- 调用精灵组的add方法可以向精灵组添加精灵
- 在 _ update_sprites ,让敌机精灵组调用update和draw方法
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 设计英雄和子弹类
英雄需求
- 游戏启动后,英雄出现在屏幕的水平中间位置,距离屏幕底部120像素
- 英雄每隔0.5秒发射一次子弹,每次连发三枚子弹
- 英雄默认不会移动,需要通过左/右方向键,控制英雄在水平方向移动
子弹需求
- 子弹从英雄的正上方发射沿直线向上方飞行
- 飞出屏幕后,需要从精灵组中删除
Hero——英雄
- 初始化方法
- 指定英雄图片
- 初始速度=0——英雄默认静止不动
- 定义bullets子弹精灵组保存子弹精灵
- 重写**update()**方法
- 英雄需要水平移动
- 并且需要保证不能移出屏幕
- 增加bullets属性,记录所有子弹精灵
- 增加fire方法,用于发射子弹
Bullet——子弹
- 初始化方法
- 指定子弹图片
- 初始速度=-2——子弹需要向上方飞行
- 重写**update()**方法
- 判断是否飞出屏幕,如果是,从精灵组删除
2 创建英雄
2.1 准备英雄类
- 在 plane_spkites新建Hero类
- 重写初始化方法,直接指定图片名称,并且将初始速度设置为0
- 设置英雄的初始位置
- 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 绘制英雄
- 在_create_sprites,添加英雄精灵和英雄精灵组
- 后续要针对英雄做碰撞检测以及发射子弹
- 所以英雄需要单独定义成属性
2.在_update_sprites,让英雄精灵组调用update和draw方法
代码实现
- 修改_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——飞机大战(下)的主要内容,如果未能解决你的问题,请参考以下文章