外星人入侵_武装飞船

0 导入Pygame包

不知道为啥我的Pycharm导入包一直会出现问题,最后使用anaconda powershell prompt导入可以成功。

pip install pygame

1开始游戏项目

开发游戏《外星人入侵》手续创建一个空的Pygame窗口,供后面来绘制后续元素,如飞船和外星人。这里还将让这个游戏响应用户输入、设置背景色以及加载飞船图像。

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

alien_invasion.py

import sys
import pygame
from settings import Settings

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()  #初始化背景设置,让Pygame能正确工作
    ai_setting = Settings()
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
    pygame.display.set_caption("Alien Invasion")

    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        for event in pygame.event.get():    #所有键盘和鼠标事件都将促使for循环运行
            if event.type == pygame.QUIT:
                sys.exit()    #调用sys.exit()退出游戏

        #每次循环都重绘屏幕
        screen.fill(ai_setting.bg_color)

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

run.game()

对象screen是一个surface。在pygame中,surface时屏幕的一部分,用于显示游戏元素。在这个游戏中,每个元素(如外星人或飞船)都是一个surface。
在Pygame库中,Surface 对象是一个非常核心的概念,它代表了一个二维的矩形画布,可以在上面绘制图像、图形、文本等内容。每个 Surface 都有自己的像素格式和尺寸,它们可以被用来表示游戏中的任何可见元素,比如背景、角色、物品等。
display.set_mode()返回的suface表示整个窗口

1.2设置背景色

1.3创建设置类

settings.py

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200  #屏幕宽800
        self.screen_height = 800  #屏幕高800
        self.bg_color = (230,230,230) #浅灰色

2添加飞船图像

2.1创建Ship类

ship.py

import pygame

class Ship():
    def __init__(self,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen

        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('image/ship.bmp')
        self.rect = self.image.get_rect()     #2 使用get_rect()获取相应surface的属性rect
        self.screen_rect = screen.get_rect()    #3 将屏幕的矩形存储在self.screen_rect中

        #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx      #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
        self.rect.bottom = self.screen_rect.bottom        #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom

    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)       #5 根据self.rect指定的位置将图像绘制到屏幕上


rect:矩形
get_rect()可获取相应surface的属性rect。Pygame能够像处理矩形(rect对象)一样处理游戏元素。
处理rect对象时,可使用矩形四角和中心的x和y坐标。可通过设置这些值制定矩形的位置。
要将游戏元素居中,可设置相应rect对象的属性center、centerx、centery。
要让游戏元素与屏幕边缘对齐,可使用属性top、bottom、left或者right。
要调整游戏的水平或垂直位置,可使用属性x或y,它们分别是矩形左上角的x和y坐标。
在Pygame中,原点(0,0)位于屏幕左上角,向右下方移动时,坐标值将增大。在1200×800的屏幕上,原点位于左上角,而右下角的坐标为(1200,800)

2.2在屏幕上绘制飞船

更新alien_invasion.py:,使其创建一艘飞船,并调用其方法blitme():

import sys
import pygame
from settings import Settings
from ship import Ship


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()  #初始化背景设置,让Pygame能正确工作
    ai_setting = Settings()
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
    pygame.display.set_caption("Alien Invasion")

    # 更新的部分 创建一首飞船
    ship = Ship(screen)

    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        for event in pygame.event.get():    #所有键盘和鼠标事件都将促使for循环运行
            if event.type == pygame.QUIT:
                sys.exit()    #调用sys.exit()退出游戏

        #每次循环都重绘屏幕
        screen.fill(ai_setting.bg_color)
        ship.blitme()          #确保飞船绘制在屏幕上,并出现在背景前面

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

run_game()

运行结果:
在这里插入图片描述

3重构:模块game_functions

大型项目中需要在添加新代码前重构既有代码。
重构旨在简化既有的代码,是指更容易扩展。
game_functions模块存储运行的函数,通过创建game_functions,可避免alien_invasion.py过长。

3.1函数check_events()

check_events()放在一个名为game_functions的模块中:

import sys
import pygame

def check_events():
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏

那么alien_invasion.py也需要修改:

import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()  #初始化背景设置,让Pygame能正确工作
    ai_setting = Settings()
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
    pygame.display.set_caption("Alien Invasion")

    # 更新的部分 创建一首飞船
    ship = Ship(screen)

    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        gf.check_events()

        #每次循环都重绘屏幕
        screen.fill(ai_setting.bg_color)
        ship.blitme()          #确保飞船绘制在屏幕上,并出现在背景前面

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

run_game()

3.2函数update_screen()

将更新屏幕的代码放在update_screen()函数中。

import sys
import pygame

def check_events():
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏

def update_screen(ai_settings, screen, ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()  # 确保飞船绘制在屏幕上,并出现在背景前面

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

那么alien_invasion.py也需要修改:

import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()  #初始化背景设置,让Pygame能正确工作
    ai_setting = Settings()
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
    pygame.display.set_caption("Alien Invasion")

    # 更新的部分 创建一首飞船
    ship = Ship(screen)

    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        gf.check_events()
        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship)



run_game()

将飞船设置在屏幕最中间:
ship.py

 #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx      #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
        self.rect.centery = self.screen_rect.centery  # 4 将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
        #self.rect.bottom = self.screen_rect.bottom        #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom

在这里插入图片描述

4 驾驶飞船

让玩家能左右移动飞船,即用户按左键或右键时作出响应。

4.1响应按键

事件通过方法pygame.event.get()获取,因此在函数check_events():中,需要指定要检查哪些类型的事件。每次按键都被注册为一个KEYDOWN事件。
检测到KEYDOWN事件时,需要检查按下的是否是特定的键,如果按下右箭头键,增大飞船的rect.centerx值,将飞船向右移动。
keyup与keydown事件的触发响应
①keyup触发场景:
当用户按下键,再次抬起时,被触发
②keydown触发场景:
当用户按下键时,立刻被触发

game_functions代码:

def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移动飞船
                ship.rect.centerx += 1
            elif event.key == pygame.K_LEFT:
                # 向左移动飞船
                ship.rect.centerx -= 1

但是上面代码在右键按住不动的时候没有效果。
那么alien_invasion.py也需要修改:

   while True:
        #此次更新   监视键盘和鼠标事件
        gf.check_events(ship)
        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship)

4.2允许不断移动

游戏希望玩家按住右箭头不放的时候,飞船不断向右移动,直到玩家松开为止。
因此让游戏检测pygame.KEYUP事件,以便玩家松开箭头时能知道这一点,然后结合使用KEYUP和KEYDOWN事件,以及一个名为moving_right的标志实现持续移动。
飞船不动时,标志moving_right将为False,用户按下键盘右键时,标志设置为True,而用户松开时,将标志重新设置为False。
飞船的属性都由Ship类控制,因此将此类添加一个名为moving_right的属性和一个名为update()的方法。方法update()检查标志moving_right的状态,如过标志位True,调整飞船的位置。
ship.py:

import pygame

class Ship():
    def __init__(self,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen

        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()     #2 使用get_rect()获取相应surface的属性rect
        self.screen_rect = screen.get_rect()    #3 将屏幕的矩形存储在self.screen_rect中

        #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx      #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
        #self.rect.centery = self.screen_rect.centery  #  将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
        self.rect.bottom = self.screen_rect.bottom        #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom

        # 新加  移动标志
        self.moving_right = False
        self.moving_left = False
# 新加
    def update(self):
        """根据移动标志调整飞船位置"""
        if self.moving_right:
            self.rect.centerx += 1
        elif self.moving_left:
            self.rect.centerx -= 1


    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)       #5 根据self.rect指定的位置将图像绘制到屏幕上

接下来修改check_events(ship)方法,使玩家按下右按键时将moving_right设置为True,并在玩家松开右按键时设置为False。
game_functions代码:

def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                # 向右移动飞船
                ship.moving_right = True
            elif event.key == pygame.K_LEFT:
                # 向左移动飞船
                ship.moving_left = True
        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

最后,修改alien_invasion.py中的while循环,以便每次执行循环时都调用飞船的方法update()

    while True:
        #监视键盘和鼠标事件
        gf.check_events(ship)
        ship.update()
        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship)

现在运行alien_invasion.py就会发现按住右箭头,飞船将不断往右移动,知道松开为止,左箭头同理。

4.3调整飞船的速度

首先在settings.py中设置飞船的速度:

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200  #屏幕宽1200
        self.screen_height = 800  #屏幕高800
        #self.bg_color = (0, 0, 255) #蓝色
        self.bg_color = (230, 230, 230)  # 浅灰色
        self.ship_speed_factor = 1.5     #设置飞船速度为1.5

然后改变ship.pyupdate()方法:

import pygame

class Ship():
    def __init__(self, ai_settings, screen):     #1
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings      #2

        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()     # 使用get_rect()获取相应surface的属性rect
        self.screen_rect = screen.get_rect()    # 将屏幕的矩形存储在self.screen_rect中

        #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx      # 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
        #self.rect.centery = self.screen_rect.centery  #  将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
        self.rect.bottom = self.screen_rect.bottom        #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom

        # 在飞船的属性center中加入小数
        self.center = float(self.rect.centerx) #3

        #移动标志
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根据移动标志调整飞船位置"""
        if self.moving_right:
            self.center += self.ai_settings.ship_speed_factor    #4
        elif self.moving_left:
            self.center -= self.ai_settings.ship_speed_factor
        #根据self.center更新rect对象
        self.rect.centerx = self.center          #5

在1处,在__init__列表中增加了形参ai_settings,让飞船能够获取其速度。在2处,将形参ai_settings存储属性中,以便在update()中使用。
建议调整飞船位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一个能够存储小数值的变量中。使用小数设置rect的属性。但rect将只存储这个值得整数部分,为了准确地存储飞船的位置,定义一个可以存储小数值的新属性self.center(3处)。并在update中更新self.center(见4)。更新后,再根据它控制飞船位置的self.rect.centerx(见5)self.rect.centerx将只存储飞船的整数部分,但对于显示飞船而言问题不大。
alien_invasion.py中创建Ship实例时,需要传入实参ai_settings:

    #  创建飞船
    ship = Ship(ai_setting, screen)

4.4限制飞船的活动范围

当前,若玩家一致按住箭头不妨,飞船将移动到屏幕外。下面修复这个问题,让飞船移动到屏幕边缘后停止移动。
为此,修改Ship类中的方法update()

    def update(self):
        """根据移动标志调整飞船位置"""
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        elif self.moving_left and self.rect.left > self.screen_rect.left:
            self.center -= self.ai_settings.ship_speed_factor
        #根据self.center更新rect对象
        self.rect.centerx = self.center

4.6重构check_events()

将响应按键按下和响应按键松开设置为新函数。

def check_keydown_events(event, ship):
    """响应按键按下"""
    if event.key == pygame.K_RIGHT:
        # 向右移动飞船
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        # 向左移动飞船
        ship.moving_left = True

def check_keyup_events(event, ship):
    """响应按键松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False



def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ship)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)

5 射击

添加射击功能,编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕边缘后消失。

5.1添加子弹设置

更新setting.py,在方法__init__()末尾存储新类Bullet所需的值。

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200  #屏幕宽1200
        self.screen_height = 800  #屏幕高800
        #self.bg_color = (0, 0, 255) #蓝色
        self.bg_color = (230, 230, 230)  # 浅灰色
        #飞船设置
        self.ship_speed_factor = 1.5     #设置飞船速度为1.5
        #子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_hight = 15
        self.bullet_color = 60, 60, 60

5.2创建Bullet类

bullet.py前半部分:

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""

    def __init__(self, ai_settings, screen, ship):
        """在飞船所处的位置创建一个子弹对象"""
        super().__init__()
        self.screen = screen

        # 在(0,0)处创建一个表示子弹的矩形,在设置正确的位置
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_hight)         #1
        self.rect.centerx = ship.centerx      #2
        self.rect.top = ship.top    #3

        #存储用小数表示的子弹位置
        self.y = float(self.rect.y)   #4

        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

Bullet类继承了从模块pygame.sprite中导入的Sprite类。通过使用精灵,可将游戏中相关元组编组,进而同时操作编组中的所有元素。
1处创建了子弹的属性rect。子弹并非基于图像的,因此必须使用pygame.Rect()类从空白开始创建一个矩形。

Rect(left, top, width, height) 

在这里插入图片描述
下面是bullet.py的后半部分——方法update()和draw_bullet():

        def update(self):
            """向上移动子弹"""
            #更新表示子弹位置的小数值
            self.y -= self.speed_factor
            #更新表示子弹位置的rect的位置
            self.rect.y = self.y
            
        def draw_bullet(self):
            """在屏幕上绘制子弹"""
            pygame.draw.rect(self.screen,self.color, self.rect)

需要绘制子弹时,调用draw_bullet()

5.3将子弹存储在编组中

首先在alien_invasion.py中创建一个编组(group),用于存储所有有效的子弹,以便能够管理发射出去的子弹。这个编组将是pygame.sprite.Group类中的一个实例。pygame.sprite.Group类似于列表,但提供了有助于开发游戏的格外功能。
在主循环中使用这个编组在屏幕上绘制子弹,已更新每颗子弹的位置。

import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf
from pygame.sprite import Group


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()  #初始化背景设置,让Pygame能正确工作
    ai_setting = Settings()
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
    pygame.display.set_caption("Alien Invasion")

    #  创建飞船
    ship = Ship(ai_setting, screen)

    #创建一组用于存储子弹的编组
    bullets = Group()    #创建一个Group实例

    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        gf.check_events(ai_setting,screen, ship, bullets)
        ship.update()
        bullets.update()
        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship)



run_game()

5.4开火

game_funcrion.py修改:

import sys
import pygame
from ship import Ship
from bullet import Bullet

def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键按下"""
    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:
        #创建一颗子弹,用将其加入到编组bullets中
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

def check_keyup_events(event, ship):
    """响应按键松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False



def check_events(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def update_screen(ai_settings, screen, ship, bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    #在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()  # 确保飞船绘制在屏幕上,并出现在背景前面

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

在这里插入图片描述

5.5删除已消失的子弹

当前子弹抵达屏幕后消失是哪位Pygame无法再屏幕外面绘制它们,这些子弹实际上仍然存在,它们的y坐标为负数,且越来越小,会持续小航内存和处理能力。
需要删除已经消失的子弹
alien_invasion.py

 bullets.update()
        #删除已经消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)
            print(len(bullets))
        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship, bullets)

print(len(bullets))语句用于检查当前还有多少子弹。

5.6限制子弹的数量

settings.py:

#子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_hight = 15
        self.bullet_color = 60, 60, 60
        self.bullet_allowed = 3         #允许子弹的最大数量

修改game_function.py中的check_keydown_events:

def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键按下"""
    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:
        #创建一颗子弹,用将其加入到编组bullets中
        if len(bullets) < ai_settings.bullet_allowed:
            new_bullet = Bullet(ai_settings, screen, ship)
            bullets.add(new_bullet)

5.7创建函数update_bullets()

使主函数变简单,在game_function.py中增加update_bullets():

def update_bullets(bullets):
    bullets.update()
    # 删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

alien_invasion.py:

 while True:
        #监视键盘和鼠标事件
        gf.check_events(ai_setting, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)

        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship, bullets)

5.8创建函数fire_bullet()

将发射子弹的代码移到一个独立的函数中,这样在check_keydown_events中只需要使用一行代码发射子弹。

def fire_bullet(bullets, ai_settings, screen, ship):
    # 创建一颗子弹,用将其加入到编组bullets中
    if len(bullets) < ai_settings.bullet_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)
def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键按下"""
    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:
        #创建一颗子弹,用将其加入到编组bullets中
        fire_bullet(bullets, ai_settings, screen, ship)   

6回顾(整体代码在这里)

6.1 alien_invasion.py

主文件alien_invasion.py包括一些列整个游戏都需要封装的对象:存储在ai_setting中的设置,存储在screen中的主显示surface以及一个飞船实例、游戏的主循环,这是一个调用check_eventsship.update()update_screen()的while循环。

import sys
import pygame
from settings import Settings
from ship import Ship
import game_function as gf
from pygame.sprite import Group


def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()  #初始化背景设置,让Pygame能正确工作
    ai_setting = Settings()
    screen = pygame.display.set_mode((ai_setting.screen_width, ai_setting.screen_height))  #创建一个名为scree,宽1200,高800像素的游戏窗口,display.set_mode返回surface表示整个游戏窗口
    pygame.display.set_caption("Alien Invasion")

    #  创建飞船
    ship = Ship(ai_setting, screen)

    #创建一组用于存储子弹的编组
    bullets = Group()    #创建一个Group实例

    #开始游戏的主循环
    while True:
        #监视键盘和鼠标事件
        gf.check_events(ai_setting, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)

        #更新屏幕上的图像,并切换到新屏幕
        gf.update_screen(ai_setting, screen, ship, bullets)



run_game()


6.2 settings.py

文件settings.py包含Settings类,这个类只包含方法__init__(),它初始化控制游戏外观和飞船速度、子弹的属性。

class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200  #屏幕宽1200
        self.screen_height = 800  #屏幕高800
        #self.bg_color = (0, 0, 255) #蓝色
        self.bg_color = (230, 230, 230)  # 浅灰色
        #飞船设置
        self.ship_speed_factor = 1.5     #设置飞船速度为1.5
        #子弹设置
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_hight = 15
        self.bullet_color = 60, 60, 60
        self.bullet_allowed = 3         #允许子弹的最大数量

6.3 game_function.py

文件game_function.py包含一系列函数,游戏的大部分工作由他们完成。函数check_events()检测相关的事件,如按键和松开,并使用辅助函数check_keydown_events()check_keyup_events()处理这些事件。这些函数管理飞船的移动,以及子弹的发射,模块game_function.py还包含函数update_screen(),用于在每次执行主循环时重绘屏幕。

import sys
import pygame
from ship import Ship
from bullet import Bullet

def update_bullets(bullets):
    bullets.update()
    # 删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

def fire_bullet(bullets, ai_settings, screen, ship):
    # 创建一颗子弹,用将其加入到编组bullets中
    if len(bullets) < ai_settings.bullet_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键按下"""
    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:
        #创建一颗子弹,用将其加入到编组bullets中
        fire_bullet(bullets, ai_settings, screen, ship)

def check_keyup_events(event, ship):
    """响应按键松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False



def check_events(ai_settings, screen, ship, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():  # 所有键盘和鼠标事件都将促使for循环运行
        if event.type == pygame.QUIT:
            sys.exit()  # 调用sys.exit()退出游戏
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screen, ship, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def update_screen(ai_settings, screen, ship, bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    #在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    ship.blitme()  # 确保飞船绘制在屏幕上,并出现在背景前面

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

6.4 ship.py

文件ship.py包含Ship类,这个类包含__init__()方法、管理飞船位置的方法update()和在屏幕上绘制飞船的方法blitme()。表示飞船的图像存储在文件夹images下的文件ship.bmp中。

import pygame

class Ship():
    def __init__(self, ai_settings, screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings

        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')
        self.rect = self.image.get_rect()     #2 使用get_rect()获取相应surface的属性rect
        self.screen_rect = screen.get_rect()    #3 将屏幕的矩形存储在self.screen_rect中

        #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx      #4 将飞船中心的x坐标(self.rect.centerx)设置为表示屏幕的矩形的属性centerx
        #self.rect.centery = self.screen_rect.centery  #  将飞船中心的y坐标(self.rect.centery)设置为表示屏幕的矩形的属性centery
        self.rect.bottom = self.screen_rect.bottom        #将飞船下边缘的y坐标设置为表示屏幕的矩形的属性bottom

        # 在飞船的属性center中加入小数
        self.center = float(self.rect.centerx)

        #移动标志
        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根据移动标志调整飞船位置"""
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        elif self.moving_left and self.rect.left > self.screen_rect.left:
            self.center -= self.ai_settings.ship_speed_factor
        #根据self.center更新rect对象
        self.rect.centerx = self.center


    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)       #5 根据self.rect指定的位置将图像绘制到屏幕上




6.5 bullet.py

创建子弹以及子弹更新和绘制子弹的函数。

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""

    def __init__(self, ai_settings, screen, ship):
        """在飞船所处的位置创建一个子弹对象"""
        super().__init__()
        self.screen = screen

        # 在(0,0)处创建一个表示子弹的矩形,在设置正确的位置
        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_hight)
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top

        #存储用小数表示的子弹位置
        self.y = float(self.rect.y)

        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor

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

    def draw_bullet(self):
        """在屏幕上绘制子弹"""
        pygame.draw.rect(self.screen,self.color, self.rect)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值