当前位置:   article > 正文

Python项目--外星人入侵--武装飞船_项目外星人入侵python代码(中文注释)

项目外星人入侵python代码(中文注释)

武装飞船

开始游戏项目

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

首先,我们创建一个空的Pygame窗口。使用Pygame编写的游戏的基本结构如下:

#alien_invasion.py
import sys
import pygame
def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen = pygame.display.set_mode((1200, 800))#注意这里是元组
    pygame.display.set_caption("Alien Invasion")
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 让最近绘制的屏幕可见
        pygame.display.flip()


run_game()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

如果此时运行这些代码,你将看到一个空的Pygame窗口。

设置背景色

Pygame默认创建一个黑色屏幕,这太乏味了。下面来将背景设置为另一种颜色:
#alien_invasion.py

--snip--
    pygame.display.set_caption("Alien Invasion")
    bg_color = (230, 230, 230)
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 每次循环时都重绘屏幕
        screen.fill(bg_color)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

创建设置类

下面是最初的Settings 类:

#settings.py
class Settings():
    def __init__(self):
        """初始化游戏设置"""
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

为创建Settings 实例并使用它来访问设置,将alien_invasion.py修改成下面这样:
#alien_invasion.py
from settings import Settings
--snip--
    ai_settings = Settings()
    screen = pygame.display.set_mode(ai_settings.screen_width, ai_settings.screen_height)
    pygame.display.set_caption("Alien Invasion")
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

添加飞船图像

创建Ship 类

选择用于表示飞船的图像后,需要将其显示到屏幕上。我们将创建一个名为ship 的模块,其中包含Ship 类,它负责管理飞船的大部分行为。

#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()
        self.screen_rect = screen.get_rect()
        # 将每艘飞船放在屏幕底部
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image, self.rect)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

首先,我们导入了模块pygame 。Ship 的方法__init__() 接受两个参数:引用self 和screen ,其中后者指定了要将飞船绘制到什么地方。为加载图像,我们调用 了pygame.image.load() 。这个函数返回一个表示飞船的surface,而我们将这个surface存储到了self.image 中。
要将游戏元素居中,可设置相应rect 对象的属性center 、centerx 或centery 。要让游戏元素与屏幕边缘对齐,可使用属性top 、bottom 、left 或right ;要调整游 戏元素的水平或垂直位置,可使用属性x 和y ,它们分别是相应矩形左上角的 x 和 y 坐标。
最后,我们定义了方法blitme() ,它根据self.rect 指定的位置将图像绘制到屏幕上。

在屏幕上绘制飞船

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

#alien_invasion.py
from ship import Ship
--snip--
    pygame.display.set_caption("Alien Invasion")
    # 创建一艘飞船
    ship = Ship(screen)
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
        # 让最近绘制的屏幕可见
        pygame.display.flip()
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

重构:模块game_functions

函数check_events()

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

#game_functions.py
import sys
import pygame
def check_events():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

下面来修改alien_invasion.py,使其导入模块game_functions ,并将事件循环替换为对函数check_events() 的调用:

#alien_invasion.py
import game_functions as gf
--snip--
    while True:
        # 监视键盘和鼠标事件
        gf.check_events()
        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在主程序文件中,不再需要直接导入sys ,因为当前只在模块game_functions 中使用了它。出于简化的目的,我们给导入的模块game_functions 指定了别名gf 。

函数update_screen()

为进一步简化run_game() ,下面将更新屏幕的代码移到一个名为update_screen() 的函数中,并将这个函数放在模块game_functions.py 中:

#game_functions.py
--snip--
def update_screen(ai_settings, screen, ship):
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    pygame.display.flip()

新函数update_screen() 包含三个形参:ai_settings 、screen 和ship 。现在需要将alien_invasion.py的while 循环中更新屏幕的代码替换为对函数update_screen() 的调用:
#alien_invasion.py
--snip--
    while True:
        # 监视键盘和鼠标事件
        gf.check_events()
        # 每次循环时都重绘屏幕
        gf.update_screen(ai_settings, screen, ship)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这两个函数让while 循环更简单,并让后续开发更容易:在模块game_functions 而不是run_game() 中完成大部分工作。

驾驶飞船

响应按键

每当用户按键时,都将在Pygame中注册一个事件。事件都是通过方法pygame.event.get() 获取的,因此在函数check_events() 中,我们需要指定要检查哪些类型的事 件。每次按键都被注册为一个KEYDOWN 事件。

#game_functions.py
--snip--
def check_events(ship):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                #向右移动飞船
                ship.rect.centerx +=1
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在alien_invasion.py中,我们需要更新调用的check_events() 代码,将ship 作为实参传递给它:

#alien_invasion.py
--snip--
    while True:
        # 监视键盘和鼠标事件
        gf.check_events(ship)
        # 每次循环时都重绘屏幕
        gf.update_screen()
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果现在运行alien_invasion.py,则每按右箭头键一次,飞船都将向右移动1像素。这是一个开端,但并非控制飞船的高效方式。下面来改进控制方式,允许持续移动。

允许不断移动

#ship.py
--snip--
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        self.moving_right = False
    def update(self):
        if self.moving_right:
            self.rect.centerx +=1
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在方法__init__() 中,我们添加了属性self.moving_right ,并将其初始值设置为False 。接下来,我们添加了方法update() ,它在前述标志为True 时向 右移动飞船。 下面来修改check_events() ,使其在玩家按下右箭头键时将moving_right 设置为True ,并在玩家松开时将moving_right 设置为False :

#game_functions.py
--snip--
def check_events(ship):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit(0)
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                #向右移动飞创
                ship.moving_right = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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

#alien_invasion.py
--snip--
    while True:
        # 监视键盘和鼠标事件
        gf.check_events(ship)
        ship.update()
        # 每次循环时都重绘屏幕
        gf.update_screen(ai_settings, screen, ship)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

左右移动

飞船能够不断地向右移动后,添加向左移动的逻辑很容易。我们将再次修改Ship 类和函数check_events() 。下面显示了对Ship 类的方法__init__() 和update() 所做 的相关修改:

#ship.py
--snip--
        self.moving_right = False
        self.moving_left = False
    def update(self):
        if self.moving_right:
            self.rect.centerx +=1
        if self.moving_left:
            self.rect.centerx -=1
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们还需对check_events() 作两方面的调整:

#game_functions.py
--snip--
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = True
            if event.key == pygame.K_LEFT:
                ship.moving_left = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False
            if event.key == pygame.K_LEFT:
                ship.moving_left = False
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

调整飞船的速度

当前,每次执行while 循环时,飞船最多移动1像素,但我们可以在Settings 类中添加属性ship_speed_factor ,用于控制飞船的速度。我们将根据这个属性决定飞船在每次循环时最多移动多少距离。

#settings.py
class Settings():
    def __init__(self):
        """初始化游戏设置"""
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)
        self.ship_speed_factor = 1.5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

通过将速度设置指定为小数值,可在后面加快游戏的节奏时更细致地控制飞船的速度。然而,rect 的centerx 等属性只能存储整数值,因此我们需要对Ship 类做些修改:

#ship.py
class Ship():
    def __init__(self, ai_settings, screen):
        """初始化飞船并设置初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings
--snip--
        self.center = float(self.rect.centerx)
        self.moving_right = False
        self.moving_left = False
    def update(self):
        if self.moving_right:
            self.center +=self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.rect.centerx -=self.ai_settings.ship_speed_factor
        # 根据self.center更新rect对象
        self.rect.centerx = self.center
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

鉴于现在调整飞船的位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一个能够存储小数值的变量中。可以使用小数来 设置rect 的属性,但rect 将只存储这个值的整数部分。为准确地存储飞船的位置,我们定义了一个可存储小数值的新属性self.center。我们使用函数float() 将self.rect.centerx 的值转换为小数,并将结果存储到self.center 中。 现在在update() 中调整飞船的位置时,将self.center 的值增加或减去ai_settings.ship_speed_factor 的值。更新self.center 后,我们再根据它来 更新控制飞船位置的self.rect.centerx 。self.rect.centerx 将只存储self.center 的整数部分,但对显示飞船而言,这问题不大。
在alien_invasion.py中创建Ship 实例时,需要传入实参ai_settings :

#alien_invasion.py
--snip--
    # 创建一艘飞船
    ship = Ship(ai_settings,screen)
    # 开始游戏的主循环
    while True:
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

限制飞船的活动范围

当前,如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面,消失得无影无踪。下面来修复这种问题,让飞船到达屏幕边缘后停止移动。为此,我们将修改Ship 类的方 法update() :

#ship.py
--snip--
    def update(self):
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center +=self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:
            self.center -=self.ai_settings.ship_speed_factor
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

重构check_events()

随着游戏开发的进行,函数check_events() 将越来越长,我们将其部分代码放在两个函数中:一个处理KEYDOWN 事件,另一个处理KEYUP 事件:

#game_functions.py
--snip--
def check_events(ship):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit(0)
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ship)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)
def check_keydown_events(event, ship):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    if 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
    if event.key == pygame.K_LEFT:
        ship.moving_left = False
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

射击

添加子弹设置

首先,更新settings.py

#settings.py
class Settings():
    def __init__(self):
        --snip--
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

创建Bullet 类

下面来创建存储Bullet 类的文件bullet.py,其前半部分如下:

#bullet.py
import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """创建一个子弹类"""
    def __init__(self, ai_settings, screen, ship):
        super().__init__()
        self.screen = screen
        self.rect = (0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
        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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Bullet 类继承了我们从模块pygame.sprite 中导入的Sprite 类。通过使用精灵,可将游戏中相关的元素编组,进而同时操作编组中的所有元素。
下面是bullet.py的第二部分——方法update() 和draw_bullet() :

#bullet.py
def update(self):
        """更新子弹位置"""
        self.y -= self.speed_factor#将子弹速度存为小数,方便后期更改,因为self.rect.y无法存储小数
        self.rect.y = self.y
    def draw_bullet(self):
        """绘制子弹"""
        pygame.draw.rect(self.screen, self.color, self.rect)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

将子弹存储到编组中

定义Bullet 类和必要的设置后,就可以编写代码了,在玩家每次按空格键时都射出一发子弹。首先,我们将在alien_invasion.py中创建一个编组(group),用于存储所有有效的子 弹,以便能够管理发射出去的所有子弹。这个编组将是pygame.sprite.Group 类的一个实例;pygame.sprite.Group 类类似于列表,但提供了有助于开发游戏的额外功 能。在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新每颗子弹的位置:

#alien_invasion.py
--snip--
from pygame.sprite import Group
def run_game():
--snip--
    bullets = Group()
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        bullets.update()
        # 每次循环时都重绘屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

当你对编组调用update() 时,编组将自动对其中的每个精灵调用update() ,因此代码行bullets.update() 将为编组bullets 中的每颗子弹调用bullet.update() 。

开火

在game_functions.py中,我们需要修改check_keydown_events() ,以便在玩家按空格键时发射一颗子弹。我们无需修改check_keyup_events() ,因为玩家松开空格键 时什么都不会发生。我们还需修改update_screen() ,确保在调用flip() 前在屏幕上重绘每颗子弹。
#game_functions.py

import sys
import pygame
from bullet import Bullet
--snip--
def check_keydown_events(event, ai_settings, screen, ship, bullets):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    if event.key == pygame.K_LEFT:
        ship.moving_left = True
    if event.key == pygame.K_SPACE:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)
--snip--
def update_screen(ai_settings, screen, ship, bullets):
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    for bullet in bullets.sprites():#疑问:可不可以写成bullets.draw_bullet()
        bullet.draw_bullet()
    pygame.display.flip()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

编组bulltes 传递给了check_keydown_events()。玩家按空格键时,创建一颗新子弹(一个名为new_bullet 的Bullet 实例),并使用方法add() 将其加入 到编组bullets 中;代码bullets.add(new_bullet) 将新子弹存储到编组bullets 中。

删除已消失的子弹

#alien_invasion.py
--snip--
def run_game():
    --snip--
        #删除已消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)
        # 每次循环时都重绘屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
--snip--
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

限制子弹数量

首先,在settings.py中存储所允许的最大子弹数:

#settings.py
self.bullets_allowed = 3

这将未消失的子弹数限制为3颗。在game_functions.py的check_keydown_events() 中,我们在创建新子弹前检查未消失的子弹数是否小于该设置:
#game_functions.py
if event.key == pygame.K_SPACE:
        if len(bullets) < ai_settings.bullets_allowed:
            new_bullet = Bullet(ai_settings, screen, ship)
            bullets.add(new_bullet)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

创建函数update_bullets()

编写并检查子弹管理代码后,可将其移到模块game_functions 中,以让主程序文件alien_invasion.py尽可能简单。我们创建一个名为update_bullets() 的新函数,并将其添 加到game_functions.py的末尾:

#game_functions.py
def update_bullets(bullets):
    #更新子弹位置
    bullets.update()
    #删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

alien_invasion.py中的while 循环又变得很简单了:

#alien_invasion.py
while True:
        # 监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, ship, bullets)
        ship.update()
        gf.update_bullets(bullets)
        # 每次循环时都重绘屏幕
        gf.update_screen(ai_settings, screen, ship, bullets)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

创建函数fire_bullet()

下面将发射子弹的代码移到一个独立的函数中,这样,在check_keydown_events() 中只需使用一行代码来发射子弹

#game_functions.py
--snip--
def check_keydown_events(event, ai_settings, screen, ship, bullets):
    --snip--
    if event.key == pygame.K_SPACE:
        fire_bullet(ai_settings, screen, ship, bullets)
--snip--
def fire_bullet(ai_settings, screen, ship, bullets):
    if len(bullets) < ai_settings.bullets_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/123324
推荐阅读
相关标签
  

闽ICP备14008679号