前言
通过对pygame的应用实现飞机游戏
一、项目概述
1.1项目目标和主要内容
- 1)实现飞机游戏基本功能,背景实现,飞机图标,飞机的上下左右移动,发射子弹攻击陨石。
- 2)实现对陨石的攻击,陨石撞击飞机,并拥有爆炸效果。
1.2项目的主要功能
- 按照飞机游戏的格式,创建程序,实现飞机的行动设计,射击设计,敌人的设计等。
二、项目实现
1.创建constant文件来存储常量
代码如下:
import pygame
import os
# 定义游戏帧率
GAME_FRAME = 60
# 定义游戏窗口的大小
SCREEN_RECT = pygame.Rect(0, 0, 480, 600)
# 定义间距
MARGIN = 20
PLANE_MARGIN=15
# 自定义创建敌方陨石的事件
EVENT_CREATE_STONE = pygame.USEREVENT
EVENT_CREATE_PLANE = pygame.USEREVENT + 1
# 定义所有陨石的文件名
# , 'meteorBrown_small1.png', 'meteorBrown_small2.png'
STONE_LIST = ['meteorBrown_big1.png', 'meteorBrown_big2.png', 'meteorBrown_med1.png', 'meteorBrown_med3.png', 'meteorBrown_small1.png', 'meteorBrown_small2.png', 'meteorBrown_tiny1.png']
# 图片所在的文件夹路径
IMAGE_FOLDER = os.path.join(os.path.dirname(__file__), 'img')
# 准备爆炸精灵字典
exp_animation = {'regular': [],'sonic': []}
for i in range(9):
# 加载regular动画
file_name_re = f'regularExplosion0{i}.png'
exp_1 = pygame.image.load(os.path.join(IMAGE_FOLDER, file_name_re))
exp_animation['regular'].append(exp_1)
# 加载sonic动画
file_name_so = f'sonicExplosion0{i}.png'
exp_2 = pygame.image.load(os.path.join(IMAGE_FOLDER, file_name_so))
exp_animation['sonic'].append(exp_2)
2.创建game_sprite文件来存储与实现游戏精灵
代码如下(示例):
from pygame import key
from constant import *
import random
# 精灵的分析:
# 玩家飞机 玩家飞机的子弹 敌方飞机 敌方飞机的子弹 背景 爆炸效果
# 继承关系
class GameSprite(pygame.sprite.Sprite):
"""游戏精灵的基类"""
def __init__(self, img_src, speed=1):
# 调用父类的初始化方法
super().__init__()
# 通过路径(图片)加载精灵
self.image = pygame.image.load(img_src)
# 获取image的大小
self.rect = self.image.get_rect()
self.speed = speed
def update(self):
"""更新精灵的方法(自动被调用的)"""
super().update()
# 默认所有精灵都是向下移动
self.rect.y += self.speed
class Background(GameSprite):
def __init__(self, isFirst=True):
super().__init__(img_src='./img/bg.png')
if not isFirst:
# 如果你不是第一张图片
# 此时设置第二张背景图的位置
self.rect.y = -self.rect.height
def update(self):
super().update()
# 判断当背景移动到windwow外的时候,改变移出屏幕的那张背景的位置
if self.rect.y > SCREEN_RECT.height:
self.rect.y = -self.rect.height
class Player(GameSprite):
def __init__(self):
super().__init__(img_src='./img/player.png')
# 是否存活
self.islive = True
# 改变加载的图像的尺寸
self.image = pygame.transform.scale(self.image, (64, 64))
# 获取精灵的大小
self.rect = self.image.get_rect()
# 给飞机设置初始位置
# 设置飞机水平居中
self.rect.centerx = SCREEN_RECT.width * .5
self.rect.bottom = SCREEN_RECT.height - MARGIN
# 给玩家飞机定义初始的水平速度
self.speedx = 0
self.speedy = 0
# 定义子弹精灵组
self.bullet_group = pygame.sprite.Group()
def update(self):
# 初始化水平速度
self.speedx = 0
self.speedy = 0
# 获取用户的按键情况
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -4
if keystate[pygame.K_RIGHT]:
self.speedx = 4
if keystate[pygame.K_UP]:
self.speedy = -4
if keystate[pygame.K_DOWN]:
self.speedy = 4
# 改变自身x
self.rect.x += self.speedx
self.rect.y += self.speedy
# 如何设置飞机的边界(确保飞机飞不出屏幕外)
# 左边
if self.rect.x < 0:
self.rect.x = 0
# 右边
if self.rect.x > SCREEN_RECT.width - self.rect.width:
self.rect.right = SCREEN_RECT.right
# 上边
if self.rect.y < 0:
self.rect.top = 0
# 下边
if self.rect.y > SCREEN_RECT.height - self.rect.height:
self.rect.bottom = SCREEN_RECT.bottom
def fire(self):
# 创建子弹
b = Bullet()
# 初始化子弹的位置
b.rect.bottom = self.rect.y - MARGIN
b.rect.centerx = self.rect.centerx
# 将子弹加入到子弹精灵组
self.bullet_group.add(b)
class Bullet(GameSprite):
def __init__(self):
super().__init__(img_src='./img/bullet.png', speed=-5)
def update(self):
super().update()
# 如果子弹飞出屏幕外,处理掉这个精灵
if self.rect.bottom < 0:
self.kill()
class Enemy(GameSprite):
def __init__(self, stone_name):
#print(stone_name)
super().__init__(img_src=f'./img/{stone_name}')
# 设置石头的随机速度
self.speedy = random.randint(1, 2)
self.speedx = random.randint(-1, 1)
# 设置石头的初始位置
self.rect.bottom = -10
self.rect.x = random.randint(0, SCREEN_RECT.width - self.rect.width)
def update(self):
super().update()
# 叠加随机x速度
self.rect.x += self.speedx
self.rect.y += self.speedy
# 判断陨石飞出边界
if self.rect.y >= SCREEN_RECT.height or \
self.rect.left < -2 * MARGIN or \
self.rect.right > SCREEN_RECT.width + 2 * MARGIN:
self.kill()
def __del__(self):
pass
class Explosion(pygame.sprite.Sprite):
def __init__(self, pos, anim_key):
super().__init__()
self.key = anim_key
# 展示第一张
self.image = exp_animation[self.key][0]
self.rect = self.image.get_rect()
# 给第一张图像初始化位置
self.rect.center = pos
# 索引
self.index = 0
self.frame_rate = 3
self.last_time = pygame.time.get_ticks()
def update(self):
# 1. 获取当前帧的时间
now_time = pygame.time.get_ticks()
if now_time - self.last_time > self.frame_rate:
# 记录上一帧的时间
self.last_time = now_time
# 控制索引递增
self.index += 1
if self.index == len(exp_animation[self.key]):
self.kill()
else:
# 如果能来到这里,就开始切换下一张图片
# 记录位置
cur_pos = self.rect.center
# 取出图像
self.image = exp_animation[self.key][self.index]
self.rect = self.image.get_rect()
self.rect.center = cur_pos
class Lives(GameSprite):
def __init__(self,isSecond=False,isThird=False):
#当前条命存在
self.alive=True
super().__init__(img_src='./img/player.png')
# 改变加载的图像的尺寸
self.image = pygame.transform.scale(self.image, (25, 19))
# 获取精灵的大小
self.rect = self.image.get_rect()
# 给飞机设置初始位置
# 设置飞机为右上角
self.rect.centerx = SCREEN_RECT.right - PLANE_MARGIN
self.rect.top = MARGIN
if isSecond==True:
self.rect.centerx = SCREEN_RECT.right - 3*PLANE_MARGIN
if isThird==True:
self.rect.centerx = SCREEN_RECT.right - 5*PLANE_MARGIN
def update(self):
super().update()
self.speed=0
3.创建game_manager文件实现具体的游戏逻辑
from game_sprite import *
class GameManager:
def __init__(self):
"""游戏初始化阶段"""
pygame.init()
# 1. 设置游戏的窗口
self.window = pygame.display.set_mode(SCREEN_RECT.size)
# 2. 创建精灵
self.create_sprite()
# 3. 设置循环条件
self.isRunning = True
# 4. 设置游戏帧率
# 4.1 创建时钟对象
self.clock = pygame.time.Clock()
self.clock.tick(GAME_FRAME)
# 5. 设置定时器事件
pygame.time.set_timer(EVENT_CREATE_STONE, 1000)
def create_sprite(self):
"""创建游戏精灵"""
# 1. 创建背景精灵类对象
self.b1 = Background()
self.b2 = Background(isFirst=False)
# 2. 创建背景精灵组
self.bg_group = pygame.sprite.Group(self.b1, self.b2)
# 3. 创建玩家精灵
self.player = Player()
self.player_group = pygame.sprite.Group(self.player)
# 4. 创建陨石精灵组
self.stone_group = pygame.sprite.Group()
# 5. 创建爆炸精灵组
self.exp_group = pygame.sprite.Group()
# 6.创建生命精灵组
self.lives1 = Lives()
self.lives2 = Lives(isSecond=True)
self.lives3 = Lives(isThird=True)
self.lives_group = pygame.sprite.Group(self.lives1,self.lives2,self.lives3)
def update_sprites(self):
"""更新精灵"""
# 更新精灵组
self.bg_group.update()
self.bg_group.draw(self.window)
# 更新玩家精灵组
self.player_group.update()
self.player_group.draw(self.window)
# 更新子弹精灵组
self.player.bullet_group.update()
self.player.bullet_group.draw(self.window)
# 更新陨石精灵组
self.stone_group.update()
self.stone_group.draw(self.window)
# 更新爆炸精灵组
self.exp_group.update()
self.exp_group.draw(self.window)
#更新生命精灵组
self.lives_group.update()
self.lives_group.draw(self.window)
def event_handle(self):
"""事件监听"""
for event in pygame.event.get():
# print(event)
if event.type == pygame.QUIT:
self.isRunning = False
self.game_over()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
# 让玩家飞机开火
self.player.fire()
elif event.type == EVENT_CREATE_STONE:
for _ in range(3):
# 获取随机索引
random_index = random.randint(0, len(STONE_LIST) - 1)
# 利用随机出来的索引去列表中取出对应的文件名
file_name = STONE_LIST[random_index]
# 创建陨石
enemy = Enemy(stone_name=file_name)
# 将陨石加入到陨石精灵组中
self.stone_group.add(enemy)
def collide(self):
"""碰撞检测"""
# 检测玩家的子弹精灵组和敌人精灵组之间的碰撞
# groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
hit = pygame.sprite.groupcollide(self.player.bullet_group, self.stone_group, True, True)
for h in hit:
pos = h.rect.center
# 生成爆炸精灵
exp = Explosion(pos, 'regular')
# 爆炸精灵加入到精灵组中
self.exp_group.add(exp)
# 玩家和敌人之间的碰撞检测
#spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
hit1 = pygame.sprite.spritecollide(self.player, self.stone_group, True)
for h in hit1:
pos = h.rect.center
exp = Explosion(pos,'sonic')
self.exp_group.add(exp)
print("you died")
if self.lives3.alive==True:
self.lives3.alive=False
self.lives3.kill()
# -生命
elif self.lives2.alive==True and self.lives3.alive==False:
self.lives2.alive=False
self.lives2.kill()
elif self.lives1.alive==True and self.lives2.alive==False:
self.lives1.alive=False
self.lives1.kill()
self.player.kill()
self.isRunning = False
def gaming(self):
"""游戏阶段"""
while self.isRunning:
# 更新精灵组(绘制)
self.update_sprites()
# 事件监听
self.event_handle()
# 检测碰撞
self.collide()
# 刷新屏幕
pygame.display.update()
self.game_over()
def game_over(self):
"""游戏结束"""
print("GAMEOVER")
pygame.quit()
if __name__ == "__main__":
GameManager().gaming()
总结
1.项目的难点和关键点
- 1)设计飞机初始位置于飞机移动时候保证不飞出界面
- 2)石头与子弹飞出界面时候的销毁释放内存
- 3)图片的获取与使用
2.心得体会
- 这个项目的游戏算法相对较为简单,明确各方面步骤与操作,一步步分析与添加精灵与消除精灵,注重界面的调节等可以加快设计过程。