pygame实现飞机大战-第二版代码分析

2023-11-18

目录

一、原代码

第一部分:精灵定义部分

第二部分:游戏运行部分

二、代码分析

(一)代码整体结构

1、定义了main()

2、创建游戏对象

3、启动游戏

(二)局部代码分析

1、精灵的定义

2、主游戏类定义

3、主程序运行

4、细节分析

三、心得体会


pygame的灵魂和核心就是精灵Sprite和组Group,第一版的代码中是用Rect对象的实现的子弹,飞机,敌机的碰撞检测,第二版找到一个以精灵和组为核心的飞机大战游戏。下面对其进行代码分析, 然后对其功能进行添加,增强个人对精灵和组的理解和掌握。.不多说了,上代码。

一、原代码

第一部分:精灵定义部分

代码部分功能添加,有可能影响整体结构的,请原谅!

import random
import pygame
#窗口的设置
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)
# 刷新的帧率
FRAME_PER_SEC = 60
# 创建敌机的定时器常量,自定义事件
CREATE_ENEMY_EVENT = pygame.USEREVENT
# 英雄发射子弹时间,自定义事件
HERO_FIRE_EVENT = pygame.USEREVENT + 1
#同时发射的子弹的个数
NUM_BULLENT_FIRE = 5
# 初始化声音
pygame.mixer.init()


#创建GameSprite精灵
class GameSprite(pygame.sprite.Sprite):
    def __init__(self, image_name, speed1=1,speed2=1):
        #继承父类方法
        super().__init__()

        self.image = pygame.image.load(image_name) #通过图像生成surface对象
        self.rect = self.image.get_rect() #生成rect对象
        self.speed1 = speed1 #设置垂直速度,如果不设置则默认为1
        self.speed2 = speed2#设置水平速度,如果不设置则默认为1,主要是飞机应用

    def update(self):
        # 1.在屏幕的垂直方向下移动
        self.rect.y += self.speed1

class Background(GameSprite):  #基于GameSprite生成精灵
    def __init__(self, is_alt=False):
        # 1.调用父类方法实现精灵的创建
        super().__init__("./image/background.png")
        # 2.判断是否是交替图像,如果是,需要设置初始位置
        if is_alt:
            self.rect.y = -self.rect.height

    def update(self):
        # 1.调用父类的方法实现
        super().update()
        # 2.判断是否飞出屏幕,如果是,需要从精灵组删除敌机
        if self.rect.y >= SCREEN_RECT.height:
            print("飞出屏幕,需要从精灵组删除。。。。")
            self.rect.y = -self.rect.height


class Enemy(GameSprite):
    def __init__(self):
        # 1.调用父类方法,创建敌机精灵,同时指定敌机图片
        super().__init__("./image/enemy0.png")
        # 2.指定敌机的初始随机速度
        self.speed1 = random.randint(1, 3)
        # 3.指定敌机的初始随机位置
        self.rect.bottom = 0
        max_x = SCREEN_RECT.width - self.rect.width
        self.rect.x = random.randint(0, max_x)

    def update(self):
        super().update()
        # 2.判断是否飞出屏幕,如果是,需要从精灵组删除敌机
        if self.rect.y >= SCREEN_RECT.height:
            # print("飞出屏幕,需要从精灵组删除。。。。")
            self.kill()

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


class Hero(GameSprite):
    def __init__(self):
        # 1.调用父类方法,设置image speed
        super().__init__("./image/hero1.png", 0, 0)
        # 2.设置英雄的初始位置
        self.rect.centerx = SCREEN_RECT.centerx
        self.rect.bottom = SCREEN_RECT.bottom - 80

        # 3.创建子弹的精灵组
        self.bullets = pygame.sprite.Group()

    def update(self):
        # 英雄在水平方向运行
        self.rect.x += self.speed2
        # 英雄在垂直方向运行
        self.rect.y -= self.speed1
        # 控制英雄不能离开屏幕
        if self.rect.x < 0:
            self.rect.x = 0
        elif self.rect.y < 0:
            self.rect.y = 0
        elif self.rect.right > SCREEN_RECT.right:
            self.rect.right = SCREEN_RECT.right
        elif self.rect.bottom> SCREEN_RECT.bottom:
            self.rect.bottom = SCREEN_RECT.bottom


    def fire(self,num):  #修改:2022-4-13 添加num参数,并根据数量控制好子弹位置
        # print("发射子弹。。。")
        bullet_num = num  #单独定义同时发射的子弹数可以为后期游戏过程中改变子弹发射数作基础
        for i in range(0,bullet_num):
            # 1.创建子弹精灵
            bullet = Bullet()
            # 2.设置精灵的位置
            #修改:将竖向子弹改成横向子弹
            # bullet.rect.bottom = self.rect.y - i * 20
            bullet.rect.bottom = self.rect.y
            bullet.rect.centerx = self.rect.centerx - 20 * (i - (bullet_num-1)/2)
            # 3.将精灵添加到精灵组
            self.bullets.add(bullet)
        #添加子弹发出的声音
        sound_bullet=SoundSprite("sound/bullet.mp3")
        sound_bullet.play()


class Bullet(GameSprite):
    def __init__(self):
        # 调用父类方法,设置子弹图片,设置初始速度
        super().__init__("./image/bullet.png", -4, 0) #第二和第三个参数是初始速度

    def update(self):
        # 调用父类方法,让子弹沿垂直方向飞行,
        super().update()
        # 判断子弹是否飞出屏幕
        if self.rect.bottom < 0:
            # kill方法把精灵从精灵组删除
            self.kill()

    def __del__(self):
        # print("子弹被销毁。。")
        pass

第二部分:游戏运行部分

import pygame
from plane_sprites import *

Score = 0

class PlaneGame(object):
    """飞机大战主游戏"""

    def __init__(self):
        print("游戏初始化")
        pygame.init()

        # 1.创建游戏的窗口
        self.screen = pygame.display.set_mode(SCREEN_RECT.size)
        # 2.创建游戏的时钟
        self.clock = pygame.time.Clock()
        # 3.调用私有方法,精灵和精灵组的创建
        self.__create_sprites()
        # 4.设置定时器事件  创建敌机
        pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
        pygame.time.set_timer(HERO_FIRE_EVENT, 500)

       

    def __create_sprites(self):
        # enemy = GameSprite("./images/enemy1.png")
        # enemy1 = GameSprite("./images/enemy1.png")
        # enemy_group = pygame.sprite.Group(enemy, enemy1)
        bg1 = Background()
        bg2 = Background(True)
        # lable_score = LabelSprite(self.screen, str(Score), 0, 0, 40)

        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("游戏开始")

        global Score
        while True:
            # 1.设置刷新帧率
            self.clock.tick(FRAME_PER_SEC)
            # 2.事件监听
            self.__event_handler()
            # 3.碰撞检测
            num_shooted=self.__check_collide()
            Score += num_shooted *100

            # 4.更新/绘制精灵组
            self.__update_sprites()
            # 5.更新显示
            pygame.display.update()

    def __event_handler(self):
        for event in pygame.event.get():
            # 判断是否退出游戏
            if event.type == pygame.QUIT:
                print("游戏结束,退出程序!!")
                PlaneGame.__game_over()
            elif event.type == CREATE_ENEMY_EVENT:
                # print("敌机出场。。。")
                # 创建敌机精灵
                enemy = Enemy()
                # 将敌机精灵添加到敌机精灵组
                self.enemy_group.add(enemy)
            elif event.type == HERO_FIRE_EVENT:
                self.hero.fire(NUM_BULLENT_FIRE)
            # elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
            #     print("向右移动。。。")
        # 使用键盘提供的方法获取键盘按键
        keys_pressed = pygame.key.get_pressed()
        # 判断元祖中对应的按键索引值
        if keys_pressed[pygame.K_RIGHT]:
            # print("向右移动。。。")
            self.hero.speed2 = 2
        elif keys_pressed[pygame.K_LEFT]:
            self.hero.speed2 = -2
        else:
            self.hero.speed2 = 0

        if keys_pressed[pygame.K_UP]:
            # print("向右移动。。。")
            self.hero.speed1 = 2
        elif keys_pressed[pygame.K_DOWN]:
            self.hero.speed1 = -2
        else:
            self.hero.speed1 = 0


    def __check_collide(self):
        # 1.子弹摧毁敌机
        shooted=pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
       
        sound_enemydown=SoundSprite("sound/enemy0_down.mp3")
        if len(shooted) > 0:
            sound_enemydown.play()
            # print("碰撞的个数:{0}".format(len(shooted)))

        return len(shooted)

        # 2.敌机撞毁英雄
        enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True)
       
        if len(enemies) > 0:

            self.hero.kill()
            # 结束游戏
            PlaneGame.__game_over()


    def __update_sprites(self):
        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()


if __name__ == '__main__':
    # 创建游戏对象
    game = PlaneGame()
    # 启动游戏
    game.start_game()

 说明:代码己经在原来的基础上做了两处修改:
1、增加了声音和音效

2、将原来的子弹改成了横向的

二、代码分析

(一)代码整体结构

1、定义了main()

我们可以看到,主函数定义一个对象,其中对象的方法是start_game,并运行这个方法。

2、创建游戏对象

 创建游戏对象主要由以下几部分组成:

(1)初始化

(2)生成精灵体和组

(3)启动游戏的内容

(4)检测事件并进行处理

(5)检测组和精灵之间的碰撞

(6)更新显示

(7)退出

3、启动游戏

(1)设置刷新帧率

(2).事件监听

(3)碰撞检测

(4)更新/绘制精灵组

(5)更新显示

(二)局部代码分析

1、精灵的定义

(1)创建GameSprite精灵,父类是pygame.sprite.Sprite
class GameSprite(pygame.sprite.Sprite):
    def __init__(self):
        #继承父类方法
        super().__init__()

        ....

    def update(self):
      ......

 (2)定义Background、Enemy、Hero、Bullet,父类是GameSprite

背景精灵:

class Background(GameSprite): 
    def __init__(self, is_alt=False):
       .....

    def update(self):
        ......

敌机精灵

class Enemy(GameSprite):
    def __init__(self):
       .......

    def update(self):
       ........

英雄精灵

  class Hero(GameSprite):
    def __init__(self):
        .......

    def update(self):..
       .........

    def fire(self,num):  

          ......

子弹精灵2、
class Bullet(GameSprite):
    def __init__(self):
     ..........

    def update(self):
     .............

2、主游戏类定义

(1)初始化游戏

    def __init__(self):
           .......

(2)创建精灵

    def __create_sprites(self):
         ........

(3)开始游戏

    def start_game(self):
        ...........

(4)检测碰撞

    def __check_collide(self):
       .......

(5)精灵更新
    def __update_sprites(self):
       
   (6)游戏结束
    def __game_over():
        

   3、主程序运行

if __name__ == '__main__':
    # 创建游戏对象
    game = PlaneGame()
    # 启动游戏
    game.start_game()

4、细节分析

(1)碰撞检测

        # 1.子弹摧毁敌机
        shooted=pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
        # 2.敌机撞毁英雄
        enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True)
   两种检测方法,一种是组与组的碰撞检测,另一个是个体与组的检测

语法说明:

pygame.sprite.groupcollide()–两个精灵组中所有精灵的碰撞检测

groundcollide(group1,group2,dokill1,dokill2,collided = None)

若dokill1=True:如果group1和group2发生碰撞,group1中的精灵就会被自动销毁

若dokill2=True:如果group1和group2发生碰撞,group2中的精灵就会被自动销毁

pygame.sprite.spritecollide() —判断某个精灵和指定精灵组中的精灵的碰撞


spritecollide(sprite, group, dokill, collided = Noone)

若dokill值为True,则指定精灵组中发生碰撞的精灵会被自动移除

collided参数是用于计算碰撞的回调函数,如果没有指定,则每个精灵必须有一个rect属性

返回值是 精灵组 中跟 精灵 发生碰撞的精灵列表

(2)Group的更新与绑定

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

   语法说明:

Group.draw(surface)

说明:对精灵组中的每一个精灵依次调用surface.blit(),依次将精灵组中的精灵绘制在surface上

Group.update()

说明:对精灵组中的每一个精灵依次调用update()方法,并且update()方法需要自己在自己定义的精灵类中去实现。

(3)其他细节

其他如键盘操作、音乐播放、刷新频率、事件操作等问题,请参考我以前的文章,其中有相应的介绍,相信你能搞清楚整个代码。

三、心得体会

通过对代码段的分析和学习,主要收获有:

1、代码段整体构造合理,充分利用于精灵与组,与Rect对象操作比较起来,少了很多循环,减少了代码的出错率。

2、代码模块化做的很好,易于理解,对于本人下步写代码启发很大。

3、 代码对精灵和组的运用,对个人的启发很大,也很有借鉴意义


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pygame实现飞机大战-第二版代码分析 的相关文章

随机推荐

  • R语言 数据处理(一)

    数据合并 提取及降维处理 install packages dplyr 数据处理包dplyr library dplyr name1 lt c Bob Mary Jane Kim name2 lt c Bob Mary Kim Jane w
  • Animate.css 动画库介绍

    一个漂亮炫酷的网页离不开动画效果的点缀 现在也有很多动画库可供选择 这里我来介绍一个简单好用的动画库Animate css animate css在线效果用一个在线网页演示了所有动画效果 我们只需要在下拉列表中选择一个效果名字 即可查看它的
  • centos7 安装 docker

    文章目录 使用阿里云镜像源安装docker ce 检查内核版本 Docker requires a 64 bit OS and version 3 10 or higher of the Linux kernel uname r 更新yum
  • SSM异常:No qualifying bean of type `CompanyDao` available expected at least 1 bean which qualifies

    1 边写代码边做测试 由于采用聚合架构及Maven的方式创建项目 一个父项目 Project 会有多个子模块 Module 每个子模块都会在resources目录下创建spring文件夹 用于存放spring相应的配置文件 于是测试时遇到了
  • TensorFlow——鸢尾花分类

    准备数据 数据集读入 导入所需模块 import tensorflow as tf from sklearn import datasets from matplotlib import pyplot as plt import numpy
  • 【计算机网络】计算机网络基础

    计算机是人类社会不可或缺的工具 而单独的一台计算机的功能也是有限的 计算机需要和其它的设备相互连接通信形成的计算机网络才能对人类发展带来巨大的影响 目录 计算机网络 通信协议 网络结构 网络边缘 接入网 网络核心 时延和吞吐量 时延 吞吐量
  • wchar_t char wstring string 项目转换

    wchar t char wstring string 项目转换 1 wchart t转wstring wchar t tmpRuleStr 10 0 wstring m tmpRuleStr wstring tmpRuleStr 2 ws
  • Mac安装python3

    可以在命令行中输入 brew install python3 但是出现了error 如下所示 tar Error opening archive Failed to open Users my Library Caches Homebrew
  • springBoot自动装载原理

    springBoot自动装载原理 启动类的核心组合注解 SpringBootApplication 点进去看看 组成 SpringBootApplication注解的三个核心注解 SpringBootConfiguration 继承自 Co
  • 【MySQL】数据库高级查询:修改表、DML语句、DQL语句

    修改表 1 修改表名 ALTER TABLE 旧表名 RENAME TO 新表名 TO为可选参数 使用与否不影响结果 2 添加字段 ALTER TABLE 表名 ADD 字段名 数据类型 属性 3 修改字段 ALTER TABLE 表名 C
  • Spring Bean作用域简介说明

    转自 Spring Bean作用域简介说明 Bean作用域简介 Bean作用域指一个Bean是否为单例模式 还是每次访问新实例 或一个Session一个新实例等方式 那么Bean作用域有哪几种呢 下文将一一道来 作用域 描述 singlet
  • springboot(三)———解决 8/24/2022 6:00类型转换为2022-08-24 06:00:00.0(MM/dd/yyyy H:mm与yyyy-MM-dd HH:mm:ss转换)

    输入 输出 代码 String datastring 8 24 22 6 00 String arrDate datastring split 3 StringBuffer stringBuffer new StringBuffer str
  • 停止和删除Hasplms服务(一)

    使用圣天诺HASP加密的软件都会启动一个叫做Hasplms或Sentinel LDK License Manager的服务项 这个服务是软件运行所必须的 但是在软件卸载后往往这个服务还在运行 重启电脑后也还继续运行 这就非常不厚道了 针对这
  • Gradle介绍1-入门和IDEA整合(Gradle Wrapper)

    1 Gradle 入门 1 1 Gradle 简介 Gradle 是一款Google 推出的基于 JVM 通用灵活的项目构建工具 支持 Maven JCenter 多种第三方仓库 支持传递性依赖管理 废弃了繁杂的xml 文件 转而使用简洁的
  • QGLWidget类的初始化顺序

    这里 我们用一个简单的opengl程序来了解一下QGLWidget类的结构 程序绘制了一个白色的正方形 1 QGLWidget类的定义 cpp view plain copy class Widget public QGLWidget Q
  • 在Linux(Redhat/CentOS)下安装MySQL之yum(rpm)在线安装方式

    在CentOS6之前 yum源默认存在MySQL yum install mysql server mysql client 在CentOS6之后 yum没有MySQL源 意味着我们要自己下载源 下载源 yum install url 安装
  • JMeter压力测试教程(超详细&小白版)

    目录 文章目录 jdk jmeter下载安装 jmeter应用启动 jmeter使用教程 jmeter测试结果分析 一 jdk jmeter下载安装 1 提前下载jdk和jmeter并配置环境 备注 jdk和jmeter版本需对应 否则后期
  • win10系统mysql开发常见问题集锦

    目录 一 mysql56 系统找不到指定文件 二 mysqldump Error Binlogging on server not active 无法导出数据 三 mysql备份导出的数据为空 四 mysql事件不执行 五 修改mysql密
  • python读取文件存到excel中

    用xlwt模块执行代码报下面的错 ValueError column index 256 not an int in range 256 xlwt 模块看源码说最大列只支持255列 所以超过这个值就报错了 改用xlsxwriter模块 im
  • pygame实现飞机大战-第二版代码分析

    目录 一 原代码 第一部分 精灵定义部分 第二部分 游戏运行部分 二 代码分析 一 代码整体结构 1 定义了main 2 创建游戏对象 3 启动游戏 二 局部代码分析 1 精灵的定义 2 主游戏类定义 3 主程序运行 4 细节分析 三 心得