Pyhthon实践报告——飞机游戏的开发


前言

通过对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.心得体会

  • 这个项目的游戏算法相对较为简单,明确各方面步骤与操作,一步步分析与添加精灵与消除精灵,注重界面的调节等可以加快设计过程。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值