python 编程-从入门到实践:项目一 外星人入侵

  • Post author:
  • Post category:python


一、武装飞船

1.1 安装Pygame

1、在Windows系统中检查是否安装了pip

C:\Users\Administrator.DESKTOP-SM7MMFT>python -m pip --version

pip 19.0.3 from D:\Python37\lib\site-packages\pip (python 3.7)

2、在Windows系统中安装pygame

下载地址

https://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame

下载合适的文件后,如果是.exe文件,就运行它。

如果该文件的扩展名为.whl,就将他复制到项目文件夹中。在打开一个命令窗口,切换到该文件所在的文件夹,并使用pip来运行它:

python -m pip install --user pygame-1.9.6-cp37-cp37m-win_amd64.whl

1.2 开始游戏项目

1.2.1 创建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()


遇到问题:

安装pygame成功看到提示:uccessfully installed pygame-1.9.6。Python自带IDE导入pygame验证导入成功,但是在pycharm中导入失败。

原因:pygame默认安装在C盘,pycharm未安装在C盘,需要把pygame安装路径下的以下两个文件夹拷贝到pycharm项目下:pygame pygame-1.9.6.dist-info

D:\pywork>python -m pip install --user pygame-1.9.6-cp37-cp37m-win_amd64.whl
Requirement already satisfied: pygame==1.9.6 from file:///D:/pywork/pygam

c:\users\administrator.desktop-sm7mmft\appdata\roaming\python\python37\site-packages (1.9.6) —》D:\alien_invasion\venv\Lib\site-packages

再次在pycharm中导入,导入成功

1.2.2 设置背景颜色

bg_color

1.2.3 创建设置类

Settings的类用于将所有设置存储在一个地方

修改alien_invasion.py文件

1.3 添加飞船图像

下载图片

https://pixabay.com/

本书配套资源

https://www.ituring.com.cn/book/1861

在项目中新建一个images文件夹用于存放图片

1.3.1 创建ship类

ship类负责管理飞船的大部分行为

1.3.2 在屏幕上绘制飞船

更新alien_invasion.py 调用ship类里的blitme()函数

1.4 重构:模块game_functions

1.4.1 函数check_events()

首先把管理事件的代码移到一个名为check_events()的函数中,以简化run_game()并隔离事件管理系统循环。

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

1.4.2 函数update_screen()

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

1.5 驾驶飞船

1.5.1 响应按键

1.5.2 允许不断移动

1.5.3 左右移动

1.5.4 调整飞船的速度

1.5.5 限制飞船的活动范围

1.5.6重构check_events()

1.6 射击

1.6.1 添加子弹设置

更新settings.py

1.6.2 创建Bullet类

1.6.3 将子弹存储到编组中

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

1.6.4 开火

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

1.6.5 删除已消失的子弹

检测条件:表示子弹的rect的bottom属性为零,它表明子弹已穿过屏幕顶端:‘

1.6.6 限制子弹数量

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

在game_functions.py的check_keydown_events()中,我们在创建新子弹前检查未消失的子弹是是否小于该设置。

1.6.7 创建函数update_bullets()

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

修改修改alien_invasion.py文件调用update_bullets()函数

1.6.8 创建函数fire_bullet()

将发射子弹移到独立一个函数fire_bullet(),这样,在check_keydown_events()中只需使用一行代码来发射子弹,让elif代码块变的非常简单。

二、外星人

2.1 回顾项目

1、研究既有代码,确定实现新功能前是否要进行重构。对混乱或低效的代码进行清理

2、在屏幕的左上角添加一个外星人,并指定合适的边距

3、根据第一个外星人的边距和屏幕尺寸计算屏幕上课容纳多少个外星人。我们将编写一下循环来创建一系列外星人,这些外星人填满了屏幕的上半部分

4、让外星人群向两边和下方移动,直到外星人被全部击落,有外星人撞到飞船,或有外星人抵达屏幕底端。如果整群外星人都被击落,我们将再创建一群外星人。如果有外星人撞到了飞船或抵达屏幕底端,我们将销毁飞船并再创建一群外星人。

5.限制玩家可用飞船数量,配给的飞船用完之后,游戏结束。

每次运行这个游戏是,都必须要鼠标来关闭他,现添加一个退出游戏的快捷键Q

2.2 创建第一个外星人

将外星人图片保存到images文件夹中

2.2.1 创建Alien类

2.2.2 创建Alien实例

在alien_invasion.py中导入Alien类,并进入值while循环前创建了一个Alien实例。没有修改外星人的位置,因此该while循环没有任何任何新东西,但修改对update_screen()的调用,传递了一个外星人实例。

2.2.3 让外星人出现在屏幕上

在update_screen()中调用其方法blitme()

2.3 创建一群外星人

2.3.1 确定一行可容纳多少个外星人

可用于放置外星人的水平空间为屏幕宽度减去外星人宽度的两倍:

available_space_x = ai_settings.screen_width - (2 * alien_width)

可容纳外星人数为可用空间除以外星人宽度的两倍:

number_aliens_x = available_space_x / (2 * alien_width)

2.3.2 创建多行外星人

为创建一行外星人,首先在alien_invasion.py中创建一个名为aliens的空编组,用于存储全部外星人,再调用game_functions.py中创建外星人群的函数。

2.3.3 创建外星人群

在game_functions.py导入Alien类,并在末尾加新函数creat_fleet()


遇到问题

AttributeError:'Alien' object has no attribute 'add_internal'

原因:找到的原因是,精灵组里面只能添加精灵,不能添加其他的非精灵的类,这也是我在这里犯的错。将需要添加到精灵组的类,需要继承Sprite类,犯错代码块如下:

解决:alien.py导入并继承Sprite

from pygame.sprite import Sprite

class Alien(

Sprite

):

def __init__(self, ai_settings, screen):

”初始化外星人并设置其初始位置”’


Sprite.__init__(self) # 新添加的代码

self.screen = screen

self.ai_settings = ai_settings

# 加载外星人图像,并设置其rect属性

self.image = pygame.image.load(‘images/alien.bmp’)

self.rect = self.image.get_rect()

# 每个外星人最初都在屏幕左上角附近

self.rect.x = self.rect.width

self.rect.y = self.rect.height

2.3.4 重构create_fleet()

2.3.5 添加行

计算可用垂直空间:将屏蔽高度减去第一行外星人的上边距(外星人高度)、飞船的高度以及最初外星人高度加上外星人间距(外星人高度的两倍)

available_space_y = ai_setting.screen_height - 3 *alien_height - ship_height

在飞船上方留出一定的空白区域,给玩家留出射杀外星人的时间

number_rows = available_space_y / (2 * alien_height)

知道可容纳多少行后,便可重复执行创建一行外星人的代码

2.4 让外星人群移动

让外星人群在屏幕上向右移动,撞到屏幕边缘后下移一定距离,再沿相反方向移动。

2.4.1 向右移动外星人

调用alien.py中的方法update(),且对外星人群中的每个外星人都调用他。首先添加一个控制外星人速度的设置:

然后,添加一个控制外星人速度的设置。

2.4.2 创建表示外星人移动方向是设置

用self.fleet_direction = 1 表示移动方向,鉴于只有两个可能的方向,1表示右移,-1表示向左移动。并在外星人群改变方向时在这两个值之间转换。另外,鉴于向右移动时需要增大每个外星人的x坐标,而向左移动时需要减小每个外星人的x坐标,使用数字来表示方向更合理。

2.4.3 检查外星人是否撞到了屏幕边缘

在alien.py中编写一个方法check_edges()来检查是否有外星人撞到了屏幕边缘,还需修改update(),以让每个外星人都沿着正确的方向移动

2.4.4 向下移动外星人群并改变移动方向

有外星人到达屏幕边缘时,需要将整群外星人下移,并改变他们的移动方向。对game_functions.py做重大修改,检查是否有外星人到达了左边缘或右边缘编写check_fleet_edges()和change_fleet_direction(),并对update_aliens()进行修改。

2.5 射杀外星人

2.5.1 检测子弹与外星人的碰撞

方法sprite.groupcollide()将每个子弹的rect同每个外星人的rect进行比较,并返回一个字典,其中包含发生了碰撞的子弹和外星人。在这个字典中,每个键都是一颗子弹,而相应的值都是被击中的外星人

2.5.2 为测试创建的子弹

2.5.3 生成新的外星人群

首先需要检查编组aliens是否为空。如果为空,就调用create_fleet()。我们将在update_bullets()中执行这种检查,因为外星人都是在这里被消灭的。

2.5.4 提高子弹速度

2.5.5 重构update_bullets()

创建一个新函数check_bullet_alien_collisions(),以检测子弹和外星人之间的碰撞,以及在整群外星人都被消灭干净时采取相应的措施。

2.6 结束游戏

如果玩家没能在足够短的时间内将整群外星人都消灭干净, 且有外星人撞到了飞船, 飞船将被摧毁。 与此同时, 我们还限制了可供玩家使用的飞船数, 而有外星人抵达屏幕底端时, 飞船也将被摧毁。 玩家用光了飞船后, 游戏便结束。

2.6.1 检测外星人和飞船碰撞

我们首先检查外星人和飞船之间的碰撞,以便外星人撞上飞船时我们能够作出合适的响应。我们在更新每个外星人的位置后立即检测外星人和飞船之间的距离。

将ship传递给update_aliens():

2.6.2 响应外星人和飞船碰撞

外星人与飞船发生碰撞时,不销毁ship实例并创建一个新的ship实例,而是通过跟踪跟踪游戏的统计信息来记录飞船被撞了多少次(跟踪有助于计分)

下面来编写一个用于跟踪游戏统计信息的新类——GameStats , 并将其保存为文件game_stats.py:

当前只有一项统计信息——ships_left , 其值在游戏运行期间将不断变化。 一开始玩家拥有的飞船数存储在settings.py的ship_limit 中:

我们还需对alien_invasion.py做些修改, 以创建一个GameStats 实例。

有外星人撞到飞船时, 我们将余下的飞船数减1, 创建一群新的外星人, 并将飞船重新放置到屏幕底端中央(我们还将让游戏暂停一段时间, 让玩家在新外星人群出现前注意到发生了碰撞, 并将重新创建外星人群)。将实现这些功能的大部分代码放到函数ship_hit() 中

新方法center_ship() , 请将其添加到ship.py的末尾。为让飞船居中, 我们将飞船的属性center 设置为屏幕中心的x 坐标, 而该坐标是通过属性screen_rect 获得的。


注意

我们根本没有创建多艘飞船, 在整个游戏运行期间, 我们都只创建了一个飞船实例, 并在该飞船被撞到时将其居中。 统计信息ships_left 让我们知道飞船是否用完。

请运行这个游戏, 射杀几个外星人, 并让一个外星人撞到飞船。 游戏暂停后, 将出现一群新的外星人, 而飞船将在屏幕底端居中。

2.6.3 有外星人到达屏幕底端

如果有外星人到达屏幕底端, 我们将像有外星人撞到飞船那样作出响应。 请添加一个执行这项任务的新函数, 并将其命名为update_aliens()

2.6.4 游戏结束

现在这个游戏看起来更完整了, 但它永远都不会结束, 只是ships_left 不断变成更小的负数。 下面在GameStats 中添加一个作为标志的属性game_active , 以便在玩家的飞船用完后结束游戏。

在game_stats.py中先设置游戏刚启动时处于活动状态

在ship_hit() 中添加代码, 在玩家的飞船都用完后将game_active 设置为False

2.6.5 确定应运行游戏的哪部分

在alien_invasion.py中, 我们需要确定游戏的哪些部分在任何情况下都应运行, 哪些部分仅在游戏处于活动状态时才运行:

在主循环中, 在任何情况下都需要调用check_events() , 即便游戏处于非活动状态时亦如此。 例如, 我们需要知道玩家是否按了Q键以退出游戏, 或单击关闭窗口的按钮。我们还需要不断更新屏幕, 以便在等待玩家是否选择开始新游戏时能够修改屏幕。 其他的函数仅在游戏处于活动状态时才需要调用, 因为游戏处于非活动状态时, 我们不用更新游戏元素的位置。

三、记分

在本章中, 我们将结束游戏《外星人入侵》 的开发。 我们将添加一个Play按钮, 用于根据需要启动游戏以及在游戏结束后重启游戏。 我们还将修改这个游戏, 使其在玩家的等级提高时加快节奏 并实现一个记分系统。

3.1 添加Play按钮

在本节中, 我们将添加一个Play按钮, 它在游戏开始前出现, 并在游戏结束后再次出现, 让玩家能够开始新游戏。

当前, 这个游戏在玩家运行alien_invasion.py时就开始了。 下面让游戏一开始处于非活动状态, 并提示玩家单击Play按钮来开始游戏。

3.1.1 创建Button类

由于Pygame没有内置创建按钮的方法, 我们创建一个Button 类, 用于创建带标签的实心矩形。 你可以在游戏中使用这些代码来创建任何按钮。

3.1.2 在屏幕上绘制按钮

我们将使用Button 类来创建一个Play按钮。 鉴于只需要一个Play按钮, 我们直接在alien_invasion.py中创建它。

接下来, 修改update_screen() , 以便在游戏处于非活动状态时显示Play按钮。

3.1.3 开始游戏

为在玩家单击Play按钮时开始新游戏, 需在game_functions.py中添加如下代码, 以监视与这个按钮相关的鼠标事件

至此, 你应该能够开始这个游戏了。 游戏结束时, game_active 应为False , 并重新显示Play按钮。

3.1.4 重置游戏

前面编写的代码只处理了玩家第一次单击Play按钮的情况, 而没有处理游戏结束的情况, 因为没有重置导致游戏结束的条件。

为在玩家每次单击Play按钮时都重置游戏, 需要重置统计信息、 删除现有的外星人和子弹、 创建一群新的外星人, 并让飞船居中

3.1.5 将Play按钮切换到非活动状态

当前, Play按钮存在一个问题, 那就是即便Play按钮不可见, 玩家单击其原来所在的区域时, 游戏依然会作出响应。 游戏开始后, 如果玩家不小心单击了Play按钮原来所处的区域, 游戏将重新开始!为修复这个问题, 可让游戏仅在game_active 为False 时才开始。

3.1.6 隐藏光标

为让玩家能够开始游戏, 我们要让光标可见, 但游戏开始后, 光标只会添乱。 为修复这种问题, 我们在游戏处于活动状态时让光标不可见。

通过向set_visible() 传递False , 让Pygame在光标位于游戏窗口内时将其隐藏起来。

游戏结束后, 我们将重新显示光标, 让玩家能够单击Play按钮来开始新游戏。

在ship_hit() 中, 我们在游戏进入非活动状态后, 立即让光标可见。 关注这样的细节让游戏显得更专业, 也让玩家能够专注于玩游戏而不是费力搞明白用户界面

3.2 提高等级

每当玩家将屏幕上的外星人都消灭干净后, 加快游戏的节奏, 让游戏玩起来更难。

3.2.1 修改速度设置

我们首先重新组织Settings 类, 将游戏设置划分成静态的和动态的两组。 对于随着游戏进行而变化的设置, 我们还确保它们在开始新游戏时被重置。

通过修改速度设置ship_speed_factor 、 alien_speed_factor 和bullet_speed_factor 的值, 足以加快整个游戏的节奏!

3.2.2 重置速度

每当玩家开始新游戏时, 我们都需要将发生了变化的设置重置为初始值, 否则新游戏开始时, 速度设置将是前一次游戏增加了的值。

3.3 记分

下面来实现一个记分系统, 以实时地跟踪玩家的得分, 并显示最高得分、 当前等级和余下的飞船数。

得分是游戏的一项统计信息, 因此我们在GameStats 中添加一个score 属性。

3.3.1 显示得分

为在屏幕上显示得分, 我们首先创建一个新类Scoreboard 。 就当前而言, 这个类只显示当前得分, 但后面我们也将使用它来显示最高得分、 等级和余下的飞船数。 下面是这个类的前半部分, 它被保存为文件scoreboard.py

最后, 我们创建方法show_score() , 用于显示渲染好的得分图像

3.3.2 创建记分牌

为显示得分, 我们在alien_invasion.py中创建一个Scoreboard 实例

为显示得分, 修改update_screen()

3.3.3 在外星人被消灭时更新得分

为在屏幕上实时地显示得分, 每当有外星人被击中时, 我们都更新stats.score 的值, 再调用prep_score() 更新得分图像。 但在此之前, 我们需要指定玩家每击落一个外星人都将得到多少个点。

随着游戏的进行, 我们将提高每个外星人值的点数。 为确保每次开始新游戏时这个值都会被重置, 我们在initialize_dynamic_settings() 中设置它。在check_bullet_alien_collisions() 中, 每当有外星人被击落时, 都更新得分。

我们需要修改update_bullets() , 确保在函数之间传递合适的实参。

还需要修改主while 循环中调用update_bullets() 的代码。

3.3.4 将消灭的每个外星人的点数都计入得分

当前, 我们的代码可能遗漏了一些被消灭的外星人。 例如, 如果在一次循环中有两颗子弹射中了外星人, 或者因子弹更宽而同时击中了多个外星人, 玩家将只能得到一个被消灭的外星人的点数。 为修复这种问题, 我们来调整检测子弹和外星人碰撞的方式。

在check_bullet_alien_collisions() 中, 与外星人碰撞的子弹都是字典collisions 中的一个键; 而与每颗子弹相关的值都是一个列表, 其中包含该子弹撞到的外星人。 我们遍历字典collisions , 确保将消灭的每个外星人的点数都记入得分。

3.3.5 提高点数

玩家每提高一个等级, 游戏都变得更难, 因此处于较高的等级时, 外星人的点数应更高。

3.3.6 将得分圆整

大多数街机风格的射击游戏都将得分显示为10的整数倍, 下面让我们的记分系统遵循这个原则。 我们还将设置得分的格式, 在大数字中添加用逗号表示的千位分隔符。 在Scoreboard 中执行这种修改。

函数round() 通常让小数精确到小数点后多少位, 其中小数位数是由第二个实参指定的。 然而, 如果将第二个实参指定为负数, round() 将圆整到最近的10、 100、 1000等整数倍。

注意 在Python 2.7中, round() 总是返回一个小数值, 因此我们使用int() 来确保报告的得分为整数。 如果你使用的是Python 3, 可省略对int() 的调用。

3.3.7 最高得分

每个玩家都想超过游戏的最高得分记录。 下面来跟踪并显示最高得分, 给玩家提供要超越的目标。 我们将最高得分存储在GameStats 中

3.3.8 显示等级

为在游戏中显示玩家的等级, 首先需要在GameStats 中添加一个表示当前等级的属性。 为确保每次开始新游戏时都重置等级, 在reset_stats() 中初始化它。

3.3.9 显示余下的飞船数

最后, 我们来显示玩家还有多少艘飞船, 但使用图形而不是数字。 为此, 我们在屏幕左上角绘制飞船图像来指出还余下多少艘飞船。



版权声明:本文为weixin_44156173原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。