python飞机大战的项目实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/rusi__/article/details/95811544

前言

  代码中我保留了所有最开始学习时做的笔记,很多地方显得很稚嫩,也许还存在错误,但也是我过去的见证,哈哈。

  下面进入正题~~~(需要相关资料文件的可以给我留言,我看到会回复的。)

结构

  代码部分是分为两个部分:plane_main文件和plane_sprite文件。main文件是程序实现过程的体现(比如:游戏初始化,设置帧率,时间监听,碰撞检测等等…),而sprites则是定义各种“精灵类”(比如:子弹类,战斗机类,小飞机类,背景类等…)

  编写代码的过程,基本上按照python的PE8规则书写,但是由于采用的编辑器是sublime,所以有的地方还是存在小问题,这里就不一一修改了(如果觉得实在不好的话,可以使用pycharm一键搞定格式问题。。。)

代码

plane_main

import pygame
from plane_sprites import *
class PlaneGame(object):
    '''飞机大战主游戏'''
    def __init__(self):
        print("游戏初始化")
        # 1-1.创建游戏窗口(最好不要把窗口的数值写死)最好使用常量:
        # python是纯动态的语言,是没有真正意义上的常量的,所以是通过命名的约定来命名的:字母都应该大写,单词与单词之间用下划线连接
        self.screen=pygame.display.set_mode(SEREEN_RECT.size)
        # 1-2 创建游戏的时钟
        self.clock=pygame.time.Clock()
        # 1-3 调用私有方法
        self.__create_sprites()
        # 4-1 设置定时器事件(事件监听常量,定时器以毫秒为单位的(下面实现了一秒出现一次定时器事件,事件常量为CREATE_ENEMY_EVENT))
        pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)

        pygame.time.set_timer(HERO_FIRE_EVENT,500) # 发射子弹

    def __create_sprites(self):
        # 3-1 创建背景精灵和精灵组
        bg1 = Background()
        bg2 = Background(is_alt=True)
        # bg2.rect.y=-bg2.rect.height
        self.back_group=pygame.sprite.Group(bg1,bg2)
        # 创建敌机的精灵组
        self.enemy_group=pygame.sprite.Group()
        # 创建英雄的精灵和精灵组
        # 因为需要在别的地方用到英雄的精灵,所以定义成了属性(方便调用)
        self.hero = Hero()
        self.hero_group=pygame.sprite.Group(self.hero)

    def start_game(self):
        print('游戏开始')
        while True:
            # 2-1 设置刷新帧率
            self.clock.tick(FRAME_PER_SEC)
            # 2-2 事件监听
            self.__event_handler()
            # 2-3 碰撞检测
            self.__check_collide()
            # 2-4 更新/绘制精灵组
            self.__update_sprites()
            # 2-5 更新显示
            pygame.display.update()

    def __event_handler(self):
        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type==pygame.QUIT:
                PlaneGame.__game_over() # 使用类名调用静态方法
            elif event.type==CREATE_ENEMY_EVENT:
                # print("敌机出场%s" % CREATE_ENEMY_EVENT)
                # 创建敌机精灵
                enemy=Enemy()
                # 将敌机精灵添加到敌机精灵组(精灵组对象所具有的方法add )
                self.enemy_group.add(enemy)
            elif event.type==HERO_FIRE_EVENT:
                self.hero.fire()
            # 如果KEYDOWN成立,就表明用户按下了某一个键,后面是判断具体是哪一个按键
            # elif event.type==pygame.KEYDOWN and event.key==pygame.K_RIGHT:
            #     print('向右移动...%s/%s'% (pygame.KEYDOWN,pygame.K_RIGHT))
        # 使用键盘提供的方法,获取键盘按键,返回按键元祖(这个方法对比上面的一个方法的好处就是使按下按键后队像不会停止运动)
        keys_pressed=pygame.key.get_pressed()
        # print(keys_pressed)
        # 判断元祖中对应的按键索引对应的值 一旦为1就说明对应的按键被按下了
        if keys_pressed[pygame.K_RIGHT]:
            self.hero.speed=2
        elif keys_pressed[pygame.K_LEFT]:
            self.hero.speed=-2
        else:
            self.hero.speed=0


    def __check_collide(self):
        # 子弹摧毁敌机(pygame自带的碰撞检测方法两个精灵组之间的检测)
        pygame.sprite.groupcollide(self.hero.bullets,self.enemy_group,True,True)
        # 敌机撞毁英雄(pygame自带的碰撞检测方法一个精灵和一个精灵组之间的检测)
        enemies=pygame.sprite.spritecollide(self.hero,self.enemy_group,True)  # 当检测到碰撞的时候会返回一个跟英雄碰撞的敌机精灵的列表
        # 判断列表是否有内容
        if len(enemies)>0:
            # 让英雄牺牲
            self.hero.kill()
            # 结束游戏
            PlaneGame.__game_over()
    def __update_sprites(self):
        # 下面的这俩update方法不是同一个方法,是各自对象对应的类中的方法
        self.back_group.update()
        self.back_group.draw(self.screen)

        self.enemy_group.update()
        self.enemy_group.draw(self.screen)

        self.hero_group.update()
        self.hero_group.draw(self.screen)

        self.hero.bullets.update()
        self.hero.bullets.draw(self.screen)

    @staticmethod # 因为没有使用到对象的属性和类属性所以应该定义为静态方法
    def __game_over():
        print("游戏结束")
        pygame.quit()
        exit()
# main tab生成的
# 有了以下这些代码,那么此句之后的代码只有以脚本的形式运行才会被执行,导入到别的模块中是不会被执行的
if __name__ == '__main__':
    # 判断原理:如果导入到别的模块中了,__name__便是文件名而不是__main__了。
    # print(__name__)
    pygame.init()
    # print(pygame.init()) init()在做的,其实就是检查,电脑上一些需要的硬件调用接口、基础功能是否有问题。如果有,他会在程序运行之前就反馈给你,方便你进行排查和规避。
    # 创建游戏对象
    game=PlaneGame()
    # 启动游戏
    game.start_game()

plane_sprite

# 导入模块的顺序—1官方标准模块2第三方模块3应用程序模块 比如:pygame是第三方模块,random是标准模块
import random
import pygame
# 定义屏幕大小的常量
SEREEN_RECT=pygame.Rect(0,0,480,700)
# 刷新的帧率
FRAME_PER_SEC=60
# 创建敌机的定时器常量 USEREVENT是pygame自带的用户事件常量
CREATE_ENEMY_EVENT=pygame.USEREVENT
# 创建英雄发射子弹事件的定时器
HERO_FIRE_EVENT=pygame.USEREVENT+1
# Sprite是pygame的精灵类
class GameSprite(pygame.sprite.Sprite):
    '''飞机大战游戏精灵'''
    # 可能我们有一个类继承的基类是pygame提供的类,而这个类的__init__初始化方法中已经写入部分代码了,
    # 所以为了避免程序出错我们应该在重写我们自己的类的初始化方法之前用super()这个方法调用一下父类的__init__方法。
    # ps:super()这个方法的作用就是当子类和父类重名的时候使用的。
    def __init__(self,image_name,speed=1):
        """
        :param image_name:图片路径及名字
        :param speed:经历移动速度
        """
        # 调用父类的初始化方法(调用方法需要一个对象,而super()便是这个对象)
        super().__init__()
        # 定义对象属性,将指定名称的图像加载在图像属性中
        self.image=pygame.image.load(image_name)
        # #绘制图片后的变量可以调用get_rect()方法,返回一个pygame.Rect(0,0,图像宽,图像高)的对象.(get_rect()无智能提示)--x值在后续代码设置
        self.rect=self.image.get_rect()
        self.speed=speed

    # 直接重写父类的方法不重写的话默认是静止的精灵(循环由游戏循环代码部分的“时钟”对象实现)
    def update(self, *args):
        # 屏幕的垂直方向上移动
        self.rect.y+=self.speed


class Background(GameSprite):
    '''游戏背景精灵'''
    def __init__(self,is_alt=False):
        # 2-1 调用父类方法,实现精灵的创建(image/rect/speed)
        super().__init__("./images/background.png")
        # 2-2 判断是否交替图像
        if is_alt:
            self.rect.y=-self.rect.height
    def update(self, *args):
        # 1-1.调用父类的方法实现
        super().update()
        # 1-2 判断是否移出屏幕,如果移出屏幕,将图像设置到屏幕的上方
        if self.rect.y>=SEREEN_RECT.height:
            self.rect.y=-self.rect.height


class Enemy(GameSprite):
    '''敌机精灵'''
    def __init__(self):
        # 1-1 调用父类方法,创建敌机精灵。同时指定敌机图片
        super().__init__("./images/enemy1.png")
        # 1-2 指定敌机的随机初始速度
        self.speed=random.randint(1,3)
        # 1-3 指定敌机的初始随机位置
        # bottom属性是使精灵的初始位置从屏幕正上方一点一点出来(参考 y=bottom-height)
        self.rect.bottom=0
        max_x=SEREEN_RECT.width-self.rect.width
        self.rect.x=random.randint(0,max_x)

    def update(self, *args):
        # 1-1 调用父类方法,保持垂直方向的飞行
        super().update()
        # 1-2 判断是否飞出屏幕,如果是要从精灵组删除敌机
        if self.rect.y >= SEREEN_RECT.height:
            # print("飞出屏幕。需要从精灵组删除")
            self.kill()
    # __del__内置方法,在精灵或者别的东西被销毁的时候,就会被调用了

    def __del__(self):
        # print("敌机挂了%s" % self.rect)
        pass

class Hero(GameSprite):
    '''英雄精灵'''
    def __init__(self):
        # 调用父类方法,设置image&speed(这里的0,相当于重写了父类的update方法)
        super().__init__("./images/me1.png",0)
        # 设置英雄的初始位置(ceterx可以理解为对象水平居中的属性bottom等于y+对象的高)
        self.rect.centerx=SEREEN_RECT.centerx
        self.rect.bottom=SEREEN_RECT.bottom-120
        # 创建子弹的精灵组(这是英雄精灵的子弹精灵)
        self.bullets=pygame.sprite.Group()
    def update(self, *args):
        # 英雄在水平方向上移动(重写之后,上面的参数0可以去掉了)
        self.rect.x+=self.speed
        # 判断英雄是否在水平位置上是否移除屏幕
        if self.rect.x<0:
            self.rect.x=0
        # right =x+width
        elif self.rect.right>SEREEN_RECT.right:
            self.rect.right=SEREEN_RECT.right
    def fire(self):
        # 拓展:以bottom为基础设计实现一次发射三枚子弹
        for i in (0,1,2):
            # 创建子弹精灵
            bullet=Bullet()
            # 设置精灵位置(bottom无智能提示)
            bullet.rect.bottom=self.rect.y-i*20
            bullet.rect.centerx=self.rect.centerx
            # 将精灵添加到精灵组
            self.bullets.add(bullet)

class Bullet(GameSprite):
    '''子弹精灵'''
    def __init__(self):
        # 调用父类方法,设置子弹图片和初始速度
        super().__init__("./images/bullet1.png",-2)
    def update(self, *args):
        # 调用父类方法
        super().update()
        # 判断子弹是否飞出屏幕
        if self.rect.bottom<0:
            self.kill()
    def __del__(self):
        # print("子弹被销毁")
        pass

效果如图所示:

  项目中所需要的images便不再上传了,如有需要留言即可。
在这里插入图片描述

结语

  写了好多的新浪博客了,第一次在这写博客有点诚惶诚恐。

猜你喜欢

转载自blog.csdn.net/rusi__/article/details/95811544