Python-PyGame实现飞机大战游戏(2)
一、使用精灵组创建敌机
1、简要说明
下面任务为创建敌机,由于敌机较多且运行较为统一,因此使用精灵组创建敌机群。若没有接触过精灵组,可见博客:
python-pygame精灵组使用示例(https://blog.csdn.net/lyz21/article/details/96482389)
2、实现
新建Enemy.py,创建类Enemy并继承pygame.sprite.Sprite:
"""
敌人飞机类
"""
import pygame
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
# 继承精灵类
class Enemy(pygame.sprite.Sprite):
def __init__(self, image, position): # 构造方法
pygame.sprite.Sprite.__init__(self) # 使用父类构造方法
self.image = image # 图片
self.rect = self.image.get_rect() # 获取rect对象
self.rect.topleft = position # 位置
self.speed = 1 # 速度
# 更新
def update(self):
self.rect.top = self.rect.top + self.speed # 向下飞
if self.rect.top > SCREEN_HEIGHT: # 越界,销毁
self.kill()
在主函数中导入Enemy:
from Enemy import Enemy
加载敌机图片并创建敌机组:
'载入敌机'
# 敌机图片
enemy_image = pygame.image.load('resources/images/enemy.png')
# 敌机组
enemy_group = pygame.sprite.Group()
主循环中添加并绘制敌机:
# 根据计数添加敌机
if tick % 60 == 0:
enemy_group.add(
Enemy(
enemy_image, (randint(0, SCREEN_WIDTH - 50), -49) # 50,49是飞机的宽高
)
)
# 更新敌机组
enemy_group.update()
# 将敌机组显示到界面
enemy_group.draw(screen)
3、运行展示
二、玩家飞机发射子弹
1、简要说明
玩家发射的子弹与敌机较为相似,即数量多且运行模式较为相同,因此也使用精灵组创建。
2、实现
新建子弹类,为了方便直接建在Hero类中了。子弹类与敌机类非常相似,不再赘述。
# 子弹类
class Bullet(pygame.sprite.Sprite):
def __init__(self, image, position):
pygame.sprite.Sprite.__init__(self)
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = position
self.speed = 1
def update(self):
self.rect.top = self.rect.top - self.speed
if self.rect.top <= -self.rect.height:
self.kill()
玩家类中增加子弹组属性
# 子弹精灵组
self.bullet_group = pygame.sprite.Group()
并新增射击方法,每次调用射击方法就往精灵组里面添加一个子弹类
# 射击方法
def shoot(self, bullet_img):
self.bullet_group.add(
Bullet(bullet_img, self.rect.midtop)
)
主方法中载入子弹图片
# 载入子弹图片
bullet_img = pygame.image.load('resources/images/bullet.png')
调用射击方法
# 调用射击方法
if tick % 60 == 0:
hero.shoot(bullet_img)
更新子弹
# 更新子弹
hero.bullet_group.update()
并将子弹绘制出来
# 绘制子弹
hero.bullet_group.draw(screen)
3、运行展示
三、飞机坠毁
1、简要说明
完成飞机坠毁功能主要是需要进行碰撞检测,如果检测到两个精灵有碰撞,则进行处理(比如销毁其中一个或两个),碰撞检测在其他语言中会比较麻烦,但python中的pygame模块已经对其进行很好的封装,只需调用相应方法即可。
2、实现
(1)检测敌机与子弹碰撞
子弹与敌机都是group,因此使用group与group之间的检测碰撞,后面两个布尔类型分别指是否移除两个group内相碰撞的精灵
'''敌机与子弹碰撞处理'''
# 碰撞检测(groupcollide()方法为检测group与group之间的碰撞,后面两个True分别代表是否kill掉相碰撞的两个精灵)
pygame.sprite.groupcollide(enemy_group, hero.bullet_group, True, True)
(2)检测敌机与玩家碰撞
玩家只有一个,而敌机是一个group,因此采用sprite与group之间的碰撞检测,后面一个布尔类型指是否移除碰撞的组内的精灵
'''敌机与玩家碰撞处理'''
# 碰撞检测(参数:sprite,group,后面的True是指kill掉碰撞的group内的精灵)
hero_down = pygame.sprite.spritecollide(hero, enemy_group, True)
# 如果hero_down长度大于0,则证明玩家发生碰撞,游戏结束
if len(hero_down) > 0:
break
3、运行展示
四、总结
1、说明
到此为止,飞机大战基本功能:包括击落飞机以及玩家死亡已经完成。但很明显,里面很多地方太过于简陋,因此下面将是对其进行完善。
2、当前目录
3、当前代码
import pygame
from sys import exit
from Hero import Hero
from Enemy import Enemy
from random import randint
# 计数,根据计数做一些定时操作
tick = 0
# 设置按键状态,用字典存储。字典中存储的键是前后左右四个按键值,后面数字对相应该方向移动距离
offset = {pygame.K_LEFT: 0, pygame.K_RIGHT: 0, pygame.K_UP: 0, pygame.K_DOWN: 0}
# 窗口分辨率
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
# 载入背景图片
background_img = pygame.image.load('resources/images/background.png')
'载入玩家战斗机'
# 玩家战斗机图片
player_plane_image = pygame.image.load('resources/images/feiji.png')
# 取出不带火焰的飞机部分
player_plane_imageA = player_plane_image.subsurface(pygame.Rect(0, 0, 102, 89))
# 取出带火焰的飞机部分
player_plane_imageB = player_plane_image.subsurface(pygame.Rect(0, 0, 102, 109))
# 用列表存储图像
player_plane_images = []
player_plane_images.append(player_plane_imageA)
player_plane_images.append(player_plane_imageB)
# 玩家飞机出现位置
hero_position = [200, 400]
# 创建玩家
hero = Hero(player_plane_image, hero_position)
# 载入子弹图片
bullet_img = pygame.image.load('resources/images/bullet.png')
'载入敌机'
# 敌机图片
enemy_image = pygame.image.load('resources/images/enemy.png')
# 敌机组
enemy_group = pygame.sprite.Group()
'''初始化界面'''
# 初始化pygame
pygame.init()
# 初始化窗口
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
# 设置窗口标题
pygame.display.set_caption('飞机大战')
'''事件循环'''
while True:
# 绘制背景
screen.blit(background_img, (0, 0))
'''玩家飞机相关'''
# 根据计数切换飞机图片
if tick % 41 == 0:
hero.image = player_plane_images[tick % 2]
# 调用射击方法
if tick % 60 == 0:
hero.shoot(bullet_img)
# 更新子弹
hero.bullet_group.update()
# 绘制子弹
hero.bullet_group.draw(screen)
# 绘制玩家飞机
screen.blit(hero.image, hero.rect)
'''敌机相关'''
# 根据计数添加敌机
if tick % 60 == 0:
enemy_group.add(
Enemy(
enemy_image, (randint(0, SCREEN_WIDTH - 50), -49) # 50,49是飞机的宽高
)
)
# 更新敌机组
enemy_group.update()
# 将敌机组显示到界面
enemy_group.draw(screen)
'''敌机与子弹碰撞处理'''
# 碰撞检测(groupcollide()方法为检测group与group之间的碰撞,后面两个True分别代表是否kill掉相碰撞的两个精灵)
pygame.sprite.groupcollide(enemy_group, hero.bullet_group, True, True)
'''敌机与玩家碰撞处理'''
# 碰撞检测(参数:sprite,group,后面的True是指kill掉碰撞的group内的精灵)
hero_down = pygame.sprite.spritecollide(hero, enemy_group, True)
# 如果hero_down长度大于0,则证明玩家发生碰撞,游戏结束
if len(hero_down) > 0:
break
# 更新计数
tick = tick + 1
# 更新屏幕
pygame.display.update()
# 处理按键
for event in pygame.event.get():
# 游戏退出
if event.type == pygame.QUIT:
pygame.quit()
exit()
# 按键按下
if event.type == pygame.KEYDOWN:
# 是否在offset字典中,即按下的是否是前后左右四个键
if event.key in offset:
# 在的话,将该方向的移动值设置为相应的值
offset[event.key] = hero.speed
# 按键松开
if event.type == pygame.KEYUP:
# 是否在offset字典中,即按下的是否是前后左右四个键
if event.key in offset:
# 在的话,将该方向的移动值归0
offset[event.key] = 0
# 调用移动方法
hero.move(offset)
"""
玩家飞机类
"""
import pygame
# 窗口分辨率
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
# 子弹类
class Bullet(pygame.sprite.Sprite):
def __init__(self, image, position):
pygame.sprite.Sprite.__init__(self)
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = position
self.speed = 1
def update(self):
self.rect.top = self.rect.top - self.speed
if self.rect.top <= -self.rect.height:
self.kill()
# 英雄飞机类
class Hero():
def __init__(self, hero_surface, hero_init_position):
self.image = hero_surface
self.rect = self.image.get_rect() # get_rect返回一个矩形rect对象
self.rect.topleft = hero_init_position # 矩形位置(定左上角的位置)
self.speed = 2
# 子弹精灵组
self.bullet_group = pygame.sprite.Group()
def move(self, offset):
x = self.rect.left + offset[pygame.K_RIGHT] - offset[pygame.K_LEFT]
y = self.rect.top + offset[pygame.K_DOWN] - offset[pygame.K_UP]
# 若最左边坐标小于0,即出左边界,设为0
if x < 0:
self.rect.left = 0
# 若最左边坐标大于界面宽度-飞机宽度,即出右边界,设为右边界值
elif x > SCREEN_WIDTH - self.rect.width:
self.rect.left = SCREEN_WIDTH - self.rect.width
# 其他即为正常情况
else:
self.rect.left = x
# 若最上边坐标小于0,即出上边界,设为0
if y < 0:
self.rect.top = 0
# 若最上边坐标大于界面高度-飞机高度,即出下边界,设为下边界值
elif y > SCREEN_HEIGHT - self.rect.height:
self.rect.top = SCREEN_HEIGHT - self.rect.height
# 其他视为正常情况
else:
self.rect.top = y
# 射击方法
def shoot(self, bullet_img):
self.bullet_group.add(
Bullet(bullet_img, self.rect.midtop)
)
"""
敌人飞机类
"""
import pygame
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
# 继承精灵类
class Enemy(pygame.sprite.Sprite):
def __init__(self, image, position): # 构造方法
pygame.sprite.Sprite.__init__(self) # 使用父类构造方法
self.image = image # 图片
self.rect = self.image.get_rect() # 获取rect对象
self.rect.topleft = position # 位置
self.speed = 1 # 速度
# 更新
def update(self):
self.rect.top = self.rect.top + self.speed # 向下飞
if self.rect.top > SCREEN_HEIGHT: # 越界,销毁
self.kill()