项目规划

​ 开发项目时,做好规划后再动手编写项目其实很重要。固化可确保你不偏离轨道,从而提高项目成功的可能性。
在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。

安装pygame

​ pygame是一组强大而又去的模块,可用于管理图形,动画乃至声音,让你能够更轻松地开发复杂的游戏。通过使用Pygame来处理再屏幕上绘制图像等任务,你不用考虑众多烦琐而艰难的编码工作,而是将重点放在程序的高级逻辑上。

###再windows系统中安装pip
python get-pip.py
###安装pygame
python -m pip install --user pygame-1.9.2a0-cp35-none-win32.whl

开始游戏项目

​ 现在来开始开发游戏《外星人入侵》。
​ 首先创建一个空的Pygame窗口,供后面用来绘制游戏元素。如飞船和外星人。
​ 还要红色字游戏响应用户输入,设置背景色以及加载飞船图像

创建Pygame窗口以及响应用户输入

​ 首先,要创建一个空的Pygame窗口。使用Pygame编写的游戏的基本结构

"""alien_dem"""
###sys调用系统的参数和功能
import sys
import pygame


def run_game():
    """初始化游戏并创建一个屏幕对象"""
    ##初始化背景设置
    pygame.init()
    ##定义一个名为screen宽800高600的游戏窗口,screen是一个surface(表面),surface是屏幕的一部分,用于显示游戏对象,再这个游戏中外星人,飞船都是surface
    screen = pygame.display.set_mode((800,600))
    ##显示窗口的解释说明
    pygame.display.set_caption("Alien Invasion")
    """开始游戏的循环,这个游戏由一个循环语句来控制,死循环保证这个窗口一直再显示"""
    while True:
        """监视键盘和鼠标事件,pygame.event是用来管理事件的,按键和移动键盘也是事件,也就是说一旦有事件发送就会执行这个for循环
        """
        for event in pygame.event.get():
            ##如果点击关闭窗口even.type的类型就为QUIT,则退出 pygaem.QUIT是退出事件,退出代码为256
            if event.type == pygame.QUIT:
                ##退出
                sys.exit()
        """让最近绘制的屏幕可见"""
        ##这里就是你每执行一个while循环这个就会刷新屏幕,把旧的屏幕擦去,然后显示新的
        pygame.display.flip()

##调用这个函数
run_game()

###执行
##执行后就会有一个黑色的显示屏

设置背景色

​ pygame默认创建的是一个黑色的屏幕,这太乏味了。羡慕来讲背景设置为其他颜色

##alien_demo.py
        .....
        ##设置显示背景颜色
        bg_color = (230, 230, 230)
        screen.fill(bg_color)

        ##设置显示背景颜色
        """再Pygame中颜色使用RGB来表现的,这种颜色由红色,蓝色,绿色来组成,取值范围都是0-255"""
        bg_color = (230, 230, 230)
        """给屏幕填充色"""
        screen.fill(bg_color)


##调用这个函数
run_game()
###执行
你会发现原来是黑色的背景变成白色的了

创建设置类

​ 每次给游戏添加新功能是,通常也要引入一些新的设置
​ 就比如你相改一些游戏中的某些设置--屏幕吧,你不需要直接打开本代码去修改,你将设置全部存储再一个地方,你要修改的时只需要修改那个设置的文件了,调用起来也比较方便。
​ 这里我把显示屏幕的长宽高和背景颜色添加到了alien_setting_demo.py中并在ailen_demo.py中导入

###alien_setting_demo.py
"""定义一个设置类"""
class alien_settings():
    """外星人游戏中的设置参数"""
    def __init__(self):
        self.high = 600
        self.width = 800
        self.bg_color= (230,230,230)
##alien_demo.py
import sys
import pygame
from alien_setting_demo import alien_settings


def run_game():
    """初始化游戏并创建一个屏幕对象"""
    ##初始化背景设置
    pygame.init()
    """创建类的实列,创建设置的实列"""
    settings = alien_settings()
    screen = pygame.display.set_mode((settings.width, settings.high))
    ##显示窗口的解释说明
    pygame.display.set_caption("Alien Invasion")
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                ##退出
                sys.exit()
        ##设置显示背景颜色
        screen.fill(settings.bg_color)

        """让最近绘制的屏幕可见"""
        pygame.display.flip()


##调用这个函数
run_game()

###执行
和上面一样的效果

添加飞船图标

首先我们先再文件中创建一个image目标以便于添加图片
再把飞船图添加到该文件夹中,以便后面调用

创建ship(飞船)类

​ 创建一个ship类,它负责管理飞船的大部分1行为

##ship.py
import pygame

class ship():
    def __init__(self,screen):
        """初始化飞船并设置位置"""
        self.screen = screen
        """加载图像并获取其外接矩形 返回飞船的的surface并存储到self.image中"""
        self.image = pygame.image.load('../alien/image/飞船.png')
        """调整图片大小"""
        self.image = pygame.transform.scale(self.image, (30, 40))
        """get_rect()可以让Pygame读取到图像的矩形属性,Pygame之所以效率高是因为它可以像处理矩形那样处理图像,矩形是一个特别简单的形状"""
        self.rect = self.image.get_rect()
        """获取到屏幕的矩形属性"""
        self.screen_rect = screen.get_rect()

        """每搜飞船放在屏幕底部的中心,
            要将游戏元素居中,可以设置相应rect对象的属性center(中央),centerx(x轴中央),centery(y轴中央)。要让游戏元素与屏幕边缘对齐,可是使用top、bottom、left、right;要调整游戏元素水平或垂直位置,可使用属性x和y。
        """
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

    def print_ship(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)

在屏幕上绘制飞船

###alien_demo.py
....
from ship import ship

def run_game():
....
    screen = pygame.display.set_mode((settings.width, settings.high))
    ##显示窗口的解释说明
    pygame.display.set_caption("Alien Invasion")

    """创建一个飞船"""
    ship_demo = ship(screen)
  """开始游戏的循环,这个游戏由一个循环语句来控制,死循环保证这个窗口一直再显示"""
    while True:
        .....
        screen.fill(settings.bg_color)
        ##打印出飞船到屏幕
        ship_demo.print_ship()
        pygame.display.flip()


##调用这个函数
run_game()



##执行

image-20211110101331066

重构:模块game_functions

​ 在大型项目中,经常需要在添加新代码前重构即现有代码。重构旨在简化既有代码的结果后,是其更容易扩展。

函数check_event

​ 把检查事件放在这个函数模块里面

##game_functions.py
import pygame
import sys


def check_events():
    for event in pygame.event.get():
        ##如果点击关闭窗口,则退出 pygaem.QUIT是退出事件
        if event.type == pygame.QUIT:
            ##退出
            sys.exit()

函数update_screen()

​ 我们把更新屏幕函数放在这个模块里面

##game_functions.py
def update_screen(settings,screen,ship_demo):
    ##设置显示背景颜色
    """再Pygame中颜色使用RGB来表现的,这种颜色由红色,蓝色,绿色来组成,取值范围都是0-255"""
    """给屏幕填充色"""
    screen.fill(settings.bg_color)
    ship_demo.print_ship()
    """让最近绘制的屏幕可见"""
    ##这里就是你每执行一个while循环这个就会刷新屏幕,把新的屏幕擦去
    pygame.display.flip()
##alien_demo.py
###sys调用系统的参数和功能
import sys
import pygame
from alien_setting_demo import alien_settings
from ship import ship
import game_functions


def run_game():
    """初始化游戏并创建一个屏幕对象"""
    ##初始化背景设置
    pygame.init()
    """创建类的实列,创建设置的实列"""
    settings = alien_settings()
    ##定义一个名为screen宽800高600的游戏窗口,screen是一个surface(表面),surface是屏幕的一部分,用于显示游戏对象,再这个游戏中外星人,飞船都是surface
    screen = pygame.display.set_mode((settings.width, settings.high))
    ##显示窗口的解释说明
    pygame.display.set_caption("Alien Invasion")

    """创建一个飞船"""
    ship_demo = ship(screen)
    """开始游戏的循环,这个游戏由一个循环语句来控制,死循环保证这个窗口一直再显示"""
    while True:
        game_functions.check_events()
        game_functions.update_screen(settings, screen, ship_demo)


##调用这个函数
run_game()

12-1蓝色天空:创建一个背景为蓝色的Pygame窗口

##bule_sky.py
import pygame 
import sys


class settings():
    def __init__(self):
        self.high = 600
        self.width = 1000
        self.bg_color = (0,0,250)


def bule_sky():
    ##初始化
    pygame.init()
    setting = settings()
    screen = pygame.display.set_mode((setting.width,setting.high))
    pygame.display.set_caption("蓝色的背景")
    while True:
        for even in pygame.event.get():
            if even.type == pygame.QUIT:
                sys.exit()
        screen.fill(setting.bg_color)
        pygame.display.flip()


bule_sky()

12-2游戏角色:找一幅你喜欢的游戏角色位图图像或将一幅图像转换为位图。创建一个类,将该角色绘制到屏幕中央,并将该图像的背景色设置为屏幕背景色,或将屏幕背景色设置为该图像的背景色。

##tiger_doll_setting.py
"""基本设置,屏幕,背景色"""
class settings():
    def __init__(self):
        self.width = 800
        self.high = 600
        self.bg_color = (255, 255, 255)
##tiger_doll_image.py
import pygame


class tiger_doll_image():
    def __init__(self,screen):
        self.screen = screen
        ##获取图像
        self.image = pygame.image.load('C:\\Users\\tanchang\Desktop\python\code\\alien\image\虎年娃娃-01.png')
        ##调整图像大小
        self.image = pygame.transform.scale(self.image,(400,400))
        ##调整
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()

        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

    def prin_image(self):
        self.screen.blit(self.image,self.rect)
##tiger_doll.py
import pygame
import sys
from tiger_doll_setting import settings
from tiger_doll_image import tiger_doll_image


def tiger_doll():
    pygame.init()
    setting = settings()
    screen = pygame.display.set_mode((setting.width, setting.high))
    pygame.display.set_caption("虎娃娃")
    image_demo = tiger_doll_image(screen)

    while True:
        for even in pygame.event.get():
            if even.type == pygame.QUIT:
                sys.exit()

        screen.fill(setting.bg_color)
        image_demo.prin_image()
        pygame.display.flip()


tiger_doll()

驾驶飞船

​ 只显示一个图片是不行的,必须要控制飞船前后左右移动
​ 所以我们要编写代码控制飞船左右移动

按键响应

​ 每当用户按键时,都将在pygame中注册一个事件。事件都是通过Pygame.event.get()获取的,因此在函数check_event()中,我们需要指定要检查哪些事件的类型。
​ 每次按键都是被注册为一个KEYDOWN事件
​ 检测到KEYDOWEN事件是,就要判断是否按下了特定的键

##game_functions.py
import pygame
import sys


def check_events(ship):
    """监视键盘和鼠标事件,pygame.event是用来管理事件的,按键和移动键盘也是事件,也就是说一旦有事件发送就会执行这个for循环"""
    for event in pygame.event.get():
        ##如果点击关闭窗口,则退出 pygaem.QUIT是退出事件
        if event.type == pygame.QUIT:
            ##退出
            sys.exit()
            ##判断是否按下键盘
        elif event.type == pygame.KEYDOWN:
            ##判断按下的键是否是方向键右键
            if event.key == pygame.K_RIGHT:
                ##向右移动飞船,横坐标+1
                ship.rect.centerx += 1
##alien_demo.py
import pygame
from alien_setting_demo import alien_settings
from ship import ship
import game_functions


def run_game():
    .....
    while True:
        .....
        ##调用
        game_functions.check_events(ship_demo)
        game_functions.update_screen(settings, screen, ship_demo)

run_game()


##执行后按下右键就会发现飞船可以动了

允许不断移动

​ 玩家按住右箭头不放,飞船就可以不断移动,直到玩家松开为止

##ship.py
import pygame


class ship():
    ....
    def __init__(self,screen):
        ##移动标志,,这只是一个变量
        self.moving_right = False

    def update(self):
        ##如果moving_right为True就向右移动
        if self.moving_right:
            self.rect.centerx += 1

    def print_ship(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)
###game_functions.py
import pygame
import sys


def check_events(ship):
    """监视键盘和鼠标事件,pygame.event是用来管理事件的,按键和移动键盘也是事件,也就是说一旦有事件发送就会执行这个for循环"""
    for event in pygame.event.get():
        ....
        elif event.type == pygame.KEYDOWN:
            ##判断按下的键是否是方向键右键,如果是按下右键moving_right就为True就会向右移动
             if event.key == pygame.K_RIGHT:
                ##向右移动飞船
                ship.moving_right = True
            ##如果不是按下右键moving_right就为Flase就不会会向右移动
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
.....
##aline_demo.py
import pygame
from alien_setting_demo import alien_settings
from ship import ship
import game_functions


def run_game():
    pygame.init()
    settings = alien_settings()
    screen = pygame.display.set_mode((settings.width, settings.high))
    pygame.display.set_caption("Alien Invasion")
    ship_demo = ship(screen)
    while True:
        game_functions.check_events(ship_demo)
        ship_demo.update()
        game_functions.update_screen(settings, screen, ship_demo)

左右移动

##ship.py
import pygame


class ship():
    def __init__(self,screen):
        ....
        ##移动标志
        self.moving_right = False
        self.moving_left = False
    def update(self):
        ##如果moving_right为True就向右移动
        if self.moving_right:
            self.rect.centerx += 1
        ##如果moving_left为True就向左移动
        if self.moving_left:
            self.rect.centerx -= 1
##game_functions.py
import pygame
import sys


def check_events(ship):
    """监视键盘和鼠标事件,pygame.event是用来管理事件的,按键和移动键盘也是事件,也就是说一旦有事件发送就会执行这个for循环"""
    for event in pygame.event.get():
        ##如果点击关闭窗口,则退出 pygaem.QUIT是退出事件
        if event.type == pygame.QUIT:
               ##退出
            sys.exit()
            ##判断是否按下键盘
        elif event.type == pygame.KEYDOWN:
            ##判断按下的键是否是方向键右键,如果是按下右键moving_right就为True就会向右移动
            if event.key == pygame.K_RIGHT:
                ##向右移动飞船
                ship.moving_right = True
            elif event.key == pygame.K_LEFT:
                ##向左移动飞船
                ship.moving_left = True
            ##如果不是按下右键moving_right就为Flase就不会会向右移动
            ##如果不是按下左键moving_right就为Flase就不会会向左移动
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
            elif event.key == pygame.K_LEFT:
                ship.moving_left = False
##执行aline_demo.py

调整飞船的速度

​ 当每次执行while循环时,飞船最多移动1像素,但是我们可以在设置类中添加移动属性,用于控制飞船的移动速度。

##alien_settings_demo.py
"""定义一个设置类"""


class alien_settings():
    """外星人游戏中的设置参数"""

    def __init__(self):
        ....
        ##将速度设置为小数值,可以在游戏后期更好的控制飞船的移动,只要这个值大于一他就会移动速度比以前更快
        self.ship_speed = 1.5
##ship.py
import pygame


class ship:
    ###这里多添加了一个形参settings
    def __init__(self, screen, settings):
        """初始化飞船并设置位置"""
        self.screen = screen
        ##导入设置
        self.setting = settings
        ##存储小数值
        self.center = float(self.rect.centerx)
    def update(self):
        ##如果moving_right为True就向右移动
        """注意这里是self.center"""
        if self.moving_right:
            self.center += self.setting.ship_speed
        ##如果moving_left为True就向左移动
        if self.moving_left:
            self.center -= self.setting.ship_speed
        ##根据self.center更新rect对象 因为self.rect.centerx存储着飞船的x坐标的初始值,但是不能存储小数,只能存储整数
        self.rect.centerx = self.center
##alien_demo.py
import pygame
from alien_setting_demo import alien_settings
from ship import ship
import game_functions


def run_game():
    ....
    """创建类的实列,创建设置的实列"""
    settings = alien_settings()
    ....
    ##传入参数,这里要加有个设置
    ship_demo = ship(screen,settings)

限制飞船移动范围

当玩家一直按住箭头左右移动就会使飞船在屏幕上消失的无影无踪,需要在update()中的if语句中在加个条件

##ship.py
    def update(self):
        ##如果moving_right为True就向右移动
        ##self.screen_rect.right是800 这个屏幕最大宽度也就是800,向右移动时要小于这个宽度
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.setting.ship_speed
        ##如果moving_left为True就向左移动
        ##self.screen_rect.left是0
        if self.moving_left and self.rect.left > 0:
            self.center -= self.setting.ship_speed

        #根据
        self.rect.centerx = self.center

重构check_event()

##game_functions.py
import pygame
import sys


def check_events_keyup(event,ship):
    ##如果不是按下右键moving_right就为Flase就不会会向右移动
    ##如果不是按下左键moving_right就为Flase就不会会向左移动
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False

def check_events_keydown(event,ship):
    ##判断按下的键是否是方向键右键,如果是按下右键moving_right就为True就会向右移动
    if event.key == pygame.K_RIGHT:
        ##向右移动飞船
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ##向左移动飞船
        ship.moving_left = True

def check_events(ship):
    """监视键盘和鼠标事件,pygame.event是用来管理事件的,按键和移动键盘也是事件,也就是说一旦有事件发送就会执行这个for循环"""
    for event in pygame.event.get():
        ##如果点击关闭窗口,则退出 pygaem.QUIT是退出事件
        if event.type == pygame.QUIT:
               ##退出
            sys.exit()
            ##判断是否按下键盘
        elif event.type == pygame.KEYDOWN:
            check_events_keydown(event,ship)

        elif event.type == pygame.KEYUP:
            check_events_keyup(event,ship)

射击

​ 下面需要玩家按下空隔键飞船就发射子弹(小矩形)的代码,子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失

添加子弹设置

​ 首先,更新ailen_setting_demo.py,在初始化中存储子弹所需要的值

class alien_settings():
    """外星人游戏中的设置参数"""

    def __init__(self):
         ##子弹设置
            #速度
        self.bullet_speed_factor = 1
            #宽
        self.bullet_width = 3
            #高
        self.bullet_high = 10
            #颜色
        self.bullet_color = (60, 60, 60)

创建Bullet类

import pygame
##导入pygame.sprite中的Sprite模块
from  pygame.sprite import Sprite

class bullet(Sprite):
    def __init__(self,settinge,screen,ship):
        ##把bullet子类和父类Sprite关联起来
        super().__init__()
        self.screen = screen
        ##子弹不是基于图形的,因此我们要使用pygame.Rect()类从空白处创建预估矩形,创建这个类的实列是需要提供x,y的坐标,表面在哪里创建,后面的就是表示这个矩形的宽和高
        self.rect = pygame.Rect(0,0,settinge.bullet_width , settinge.bullet_high)
        ##子弹必须得从飞船中央的顶端发出
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top
        ##子弹的y坐标设置为小数值,以便可以微调子弹的速度
        self.y = float(self.rect.y)
        print(self.y)
        self.color =  settinge.bullet_color
        self.speed_factor = settinge.bullet_speed_factor

    def update(self):
        ##移动子弹,向上移动
        self.y -= self.speed_factor
        ##更新表示子弹的位置
        self.rect.y = self.y

    def prit_bullet(self):
        ##pygame.draw.rect()是绘制矩形的函数
        pygame.draw.rect(self.screen,self.color,self.rect)

pygame.draw

pygame.draw.rect:    绘制矩形
pygame.draw.polygon:  绘制任意边数的多边形
pygame.draw.circle:  绘制圆
pygame.draw.ellipse:  在矩形内绘制椭圆
pygame.draw.arc:     绘制圆弧(或者椭圆的一部分)
pygame.draw.line:    绘制直线(线段)
pygame.draw.lines:  从一个点列表中连续绘制直线段
pygame.draw.aaline:  绘制一根平滑的线(反锯齿)
pygame.draw.aalines:  绘制一系列平滑的线

将子弹存储到编组中

​ 现在编写让玩家没按一次空隔就发射一个子弹,我们将有效子弹存储到一个编组中,
​ pygame.sprite.Group类的一个实列,pygame.sprite.Group类似于列表,但提供了又助于开发游戏的额外功能

##alien_demo.py
...
from pygame.sprite import Group


def run_game():
    """定义一个编组 可以查看game_function.py 中的check_events_keydown,"""
    bullets = Group()
    """开始游戏的循环,这个游戏由一个循环语句来控制,死循环保证这个窗口一直再显示"""
    while True:
        ##game_fuctions,在这个里面需要设置按空格时来处理bullets
        game_functions.check_events(ship_demo,settings, screen,bullets)
        ship_demo.update()
        ##子弹向上移动,bullets.py,当你调用update时编组将自动对其中的每个精灵调用update()
        bullets.update()
        game_functions.update_screen(ship_demo,settings, screen,bullets)

开火

​ 在game_function,我们需要对KEYDWON进行操作,当玩家按一次空格就会发送一颗子弹,还需要修改update_screen(),确保在调用filp()前在屏幕上重绘每颗子弹

##game_functions.py
import pygame
import sys
from bullet import bullet

def check_events_keydown(event,ship,settinge,screen,bullets):
    ##判断按下的键是否是方向键右键,如果是按下右键moving_right就为True就会向右移动
    if event.key == pygame.K_RIGHT:
        ##向右移动飞船
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ##向左移动飞船
        ship.moving_left = True
        ##如果按下空额就添加一个新的子弹
    elif event.key == pygame.K_SPACE:
        ##创建新子弹bullet.py
        new_bullet = bullet(settinge,screen,ship)
        ##存储到编组中我们在aline_demo.py中创建了一个编组
        bullets.add(new_bullet)

def check_events(ship,settinge,screen,bullets):
    """监视键盘和鼠标事件,pygame.event是用来管理事件的,按键和移动键盘也是事件,也就是说一旦有事件发送就会执行这个for循环"""
    for event in pygame.event.get():
         ......
        elif event.type == pygame.KEYDOWN:
            check_events_keydown(event,ship,settinge,screen,bullets)

def update_screen(ship,settings, screen,bullets):
    ....
    ###bullets.sprites()返回一个列表这个列表包含子弹,我们来遍历这个列表来获取子弹,然后一直不停的刷新子弹位置然后一直不停的打印
    for bullet in bullets.sprites():
        ##打印子弹
        bullet.prit_bullet()
    """让最近绘制的屏幕可见"""
    ##这里就是你每执行一个while循环这个就会刷新屏幕,把新的屏幕擦去
    pygame.display.flip()

删除消失的子弹

​ 现在屏幕上的子弹并不会消失,看不见了是因为pygame无法在屏幕外面绘制它,这些子弹依旧还在但y轴坐标会越来越小,当你发射子弹过多时就会占用过多的内存

##alien_demo.py
import pygame
from alien_setting_demo import alien_settings
from ship import ship
import game_functions
from pygame.sprite import Group


def run_game():
    ....
    """定义一个编组 可以查看game_function.py 中的check_events_keydown,"""
    bullets = Group()

    """开始游戏的循环,这个游戏由一个循环语句来控制,死循环保证这个窗口一直再显示"""
    while True:
         ##子弹向上移动,bullets.py
        bullets.update()
        ###在for循环中不应该在列表或编组中直接删除条目内,如果直接删除本体而不用副本的画,python在每次删除本体第一个元素后面的元素都会向前移一位,相应的,索引值也会发生改变
        for bullet in bullets.copy():
        ##子弹的最底部y轴位置小于0
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)        ##用于测试,测试好了之后可以把这条删除,如果你留下这条语句,你的运行效率会大大降低,因为输出到终端打印的时间比绘制图像的时间还要久
         print(len(bullets))

限制子弹数

​ 可以设置子弹数量,以便玩家可以有效射击

##alien_settings_demo.py
class alien_settings():
    """外星人游戏中的设置参数"""
    def __init__(self):
        ....
        self.bullet_num = 3
##game_functions.py
import pygame
import sys
from bullet import bullet

def check_events_keydown(event,ship,settinge,screen,bullets):
        ##如果按下空额就添加一个新的子弹
    elif event.key == pygame.K_SPACE:
        ##创建新子弹bullet.py
        ##先检查一下编组内的子弹数量,看看它是否小于默认设置的子弹数量,如果小于默认设置的子弹数量就可以继续添加子弹
        if len(bullets) < settinge.bullet_num:
            new_bullet = bullet(settinge,screen,ship)
            ##存储到编组中我们在aline_demo.py中创建了一个编组
            bullets.add(new_bullet)

重构

##game_functions.py
我们可以把更新子弹和删除子弹,整合到一个模块函数中让主程序alien_demo.py尽量简单
def bullet_update(bullets):
    ##子弹向上移动,bullets.py
    bullets.update()
    ##让超过屏幕的子弹消失
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
##alen_demo.py
import pygame
from alien_setting_demo import alien_settings
from ship import ship
import game_functions
from pygame.sprite import Group


def run_game():
    ....
        """定义一个编组 可以查看game_function.py 中的check_events_keydown,"""
    bullets = Group()
    while True:
        ....
        game_functions.bullet_update(bullets)
        ....
##game_functions.py
def fire_bullet(ship, settinge, screen, bullets):
    ##创建新子弹bullet.py
    ##先检查一下编组内的子弹数量,看看它是否小于默认设置的子弹数量,如果小于默认设置的子弹数量就可以继续添加子弹
    if len(bullets) < settinge.bullet_num:
        new_bullet = bullet(settinge, screen, ship)
        ##存储到编组中我们在aline_demo.py中创建了一个编组
        bullets.add(new_bullet)
....

def check_events_keydown(event, ship, settinge, screen, bullets):
        elif event.key == pygame.K_SPACE:
            fire_bullet(ship, settinge, screen, bullets)

12-5侧面射击:编写一个游戏,将一艘飞船放在屏幕左边,并允许玩家上下移动飞船。在玩家按空格键时,让飞船发射一颗在屏幕中向右穿行的子弹,并在子弹离开屏幕而消失后将其删除。

##game_man.py 游戏主程序
##game_setting.py 存储程序的一些设置
##game_fun12_5.py 存储程序while内部组成的函数
##ship_man.py 飞船
##game_bullets.py 子弹的程序
##game_man.py
import pygame
from game_setting import setting
import game_fun12_5
from ship_man import Ship
from pygame.sprite import Group

def run_game():
    """初始化游戏"""
    pygame.init()
    settings = setting()
    screen = pygame.display.set_mode((settings.dispaly_width,settings.dispaly_high))
    pygame.display.set_caption ("game")
    ##创建ship实列
    ship = Ship(screen,settings)
    bullet = Group()
    while True:
        ##检测事件
        game_fun12_5.check_even(ship,screen,bullet,settings)
        # ##更新飞船位置
        # ship.update_ship()
        ##更新飞船位置并检擦飞船的y轴坐标
        game_fun12_5.bullet_udpate(bullet,settings)
        ##打印
        game_fun12_5.update_screen(ship,screen,bullet,settings)



run_game()
##game_setting.py
class setting:
    """游戏设置"""
    def __init__(self):
        ##屏幕设置
        self.dispaly_width = 1200
        self.dispaly_high = 600
        self.bg_color= (70,100,80)
        ##飞船移动速度
        self.sheep_ship = 1
        ##子弹设置
        self.bullet_high = 3
        self.bullet_width = 10
        self.bullet_color = (60,60,60)
        self.bullet_sheep = 0.5
        self.bullet_num = 6
##game_fun12_5.py
import pygame
import sys
from game_bullets import bullet


def bullet_udpate(bullets, setting):
    """更新子弹位置并检擦子弹x轴坐标地点,大于屏幕就会被删"""
    bullets.update()
    for bullet in bullets.copy():
        if bullet.rect.right >= setting.dispaly_width:
            bullets.remove(bullet)
    print(len(bullets))


def fire_bullet(ship, screen, bullets, setting):
    """添加子弹,如果子弹数大于设定的子弹数就不能添加子弹"""
    if len(bullets) < setting.bullet_num:
        new_bullet = bullet(ship, screen, setting)
        bullets.add(new_bullet)


def check_even_keydown(even, ship, screen, bullets, setting):
    """检擦是否按下按键"""
    if even.key == pygame.K_DOWN:
        ship.moving_down = True
    elif even.key == pygame.K_UP:
        ship.moving_up = True
    elif even.key == pygame.K_SPACE:
        fire_bullet(ship, screen, bullets, setting)


def check_even_keyup(even, ship):
    """检查是否松开"""
    if even.key == pygame.K_DOWN:
        ship.moving_down = False
    elif even.key == pygame.K_UP:
        ship.moving_up = False


def check_even(ship, screen, bullets, setting):
    """汇总"""
    for even in pygame.event.get():
        if even.type == pygame.QUIT:
            sys.exit()
        elif even.type == pygame.KEYDOWN:
            check_even_keydown(even, ship, screen, bullets, setting)
        elif even.type == pygame.KEYUP:
            check_even_keyup(even, ship)


def update_screen(ship, screen, bullets, setting):
    ##加上背景色
    screen.fill(setting.bg_color)
    ##更新飞船位置
    ship.update_ship()
    ##打印飞船
    ship.prit_ship()
    ##打印子弹
    for bulle in bullets.sprites():
        bulle.prin_bullet()
    ##打印最新的屏幕
    pygame.display.flip()
##ship_man.py
import pygame


class Ship:
    def __init__(self,screen,setting):
        ##屏幕
        self.screen = screen
        self.settings = setting
        self.image = pygame.image.load('C:\\Users\\tanchang\Desktop\python\code\\alien\image\飞船.png')
        self.image = pygame.transform.scale(self.image,(30,40))
        ##旋转飞船
        self.image = pygame.transform.rotate(self.image,270)

        ##设置飞船的矩形和位置
        print(self)
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        self.rect.centery = self.screen_rect.centery
        self.rect.left = self.screen_rect.left

        self.center = float(self.rect.centery)

        ##移动标识,False为不移动,True为移动
        self.moving_up = False
        self.moving_down = False

    def update_ship(self):
        """设置飞船的移动和移动范围"""
        if self.moving_up and self.rect.top > self.screen_rect.top:
            self.center -= self.settings.sheep_ship
        elif self.moving_down and self.rect.bottom < self.screen_rect.bottom:
            self.center += self.settings.sheep_ship
        self.rect.centery = self.center


    def prit_ship(self):
        self.screen.blit(self.image,self.rect)
##game_bullets.py
import pygame
from pygame.sprite import Sprite


class bullet(Sprite):
    def __init__(self, ship, screen, setting):
        ##子类和父类关联
        super().__init__()

        self.screen = screen
        ##画出矩形
        self.rect = pygame.Rect(0, 0, setting.bullet_width, setting.bullet_high)
        self.rect.right = ship.rect.right
        self.rect.centery = ship.rect.centery
        self.x = float(self.rect.x)
        # print(self.x)
        self.color = setting.bullet_color
        self.sheep = setting.bullet_sheep


    def update(self):
        self.x += self.sheep
        self.rect.x = self.x
        # print(self.rect.x)

    def prin_bullet(self):
        pygame.draw.rect(self.screen,self.color,self.rect)