武装飞船

开始游戏项目

创建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窗口。

设置背景色

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--

创建设置类

下面是最初的Settings 类:

#settings.py
class Settings():def __init__(self):"""初始化游戏设置"""self.screen_width = 1200self.screen_height = 800self.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--

添加飞船图像

创建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.centerxself.rect.bottom = self.screen_rect.bottomdef blitme(self):"""在指定位置绘制飞船"""self.screen.blit(self.image, self.rect)

首先,我们导入了模块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--

重构:模块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()

下面来修改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--

在主程序文件中,不再需要直接导入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--

这两个函数让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--

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

#alien_invasion.py
--snip--while True:# 监视键盘和鼠标事件gf.check_events(ship)# 每次循环时都重绘屏幕gf.update_screen()
--snip--

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

允许不断移动

#ship.py
--snip--self.rect.centerx = self.screen_rect.centerxself.rect.bottom = self.screen_rect.bottomself.moving_right = Falsedef update(self):if self.moving_right:self.rect.centerx +=1
--snip--

在方法__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 = Trueelif event.type == pygame.KEYUP:if event.key == pygame.K_RIGHT:ship.moving_right = False
--snip--

最后,我们需要修改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--

左右移动

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

#ship.py
--snip--self.moving_right = Falseself.moving_left = Falsedef update(self):if self.moving_right:self.rect.centerx +=1if self.moving_left:self.rect.centerx -=1
--snip--

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

#game_functions.py
--snip--elif event.type == pygame.KEYDOWN:if event.key == pygame.K_RIGHT:ship.moving_right = Trueif event.key == pygame.K_LEFT:ship.moving_left = Trueelif event.type == pygame.KEYUP:if event.key == pygame.K_RIGHT:ship.moving_right = Falseif event.key == pygame.K_LEFT:ship.moving_left = False
--snip--

调整飞船的速度

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

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

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

#ship.py
class Ship():def __init__(self, ai_settings, screen):"""初始化飞船并设置初始位置"""self.screen = screenself.ai_settings = ai_settings
--snip--self.center = float(self.rect.centerx)self.moving_right = Falseself.moving_left = Falsedef update(self):if self.moving_right:self.center +=self.ai_settings.ship_speed_factorif self.moving_left:self.rect.centerx -=self.ai_settings.ship_speed_factor# 根据self.center更新rect对象self.rect.centerx = self.center
--snip--

鉴于现在调整飞船的位置时,将增加或减去一个单位为像素的小数值,因此需要将位置存储在一个能够存储小数值的变量中。可以使用小数来 设置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--

限制飞船的活动范围

当前,如果玩家按住箭头键的时间足够长,飞船将移到屏幕外面,消失得无影无踪。下面来修复这种问题,让飞船到达屏幕边缘后停止移动。为此,我们将修改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_factorif self.moving_left and self.rect.left > 0:self.center -=self.ai_settings.ship_speed_factor
--snip--

重构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 = Trueif event.key == pygame.K_LEFT:ship.moving_left = True
def check_keyup_events(event, ship):if event.key == pygame.K_RIGHT:ship.moving_right = Falseif event.key == pygame.K_LEFT:ship.moving_left = False
--snip--

射击

添加子弹设置

首先,更新settings.py

#settings.py
class Settings():def __init__(self):--snip--self.bullet_speed_factor = 1self.bullet_width = 3self.bullet_height = 15self.bullet_color = (60, 60, 60)

创建Bullet 类

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

#bullet.py
import pygame
from pygame.sprite import Spriteclass Bullet(Sprite):"""创建一个子弹类"""def __init__(self, ai_settings, screen, ship):super().__init__()self.screen = screenself.rect = (0, 0, ai_settings.bullet_width, ai_settings.bullet_height)self.rect.centerx = ship.rect.centerxself.rect.top = ship.rect.topself.y = float(self.rect.y)self.color = ai_settings.bullet_colorself.speed_factor = ai_settings.bullet_speed_factor

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.ydef draw_bullet(self):"""绘制子弹"""pygame.draw.rect(self.screen, self.color, self.rect)

将子弹存储到编组中

定义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--

当你对编组调用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 = Trueif event.key == pygame.K_LEFT:ship.moving_left = Trueif 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()

编组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--

限制子弹数量

首先,在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)

创建函数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)

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)

创建函数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)

Python项目--外星人入侵--武装飞船相关推荐

  1. Python项目外星人入侵(一):实现宇宙飞船

    文章目录 Python项目:外星人入侵(一) 安装Pygame 安装pip 安装Pygame 开始游戏项目 创建Pygame窗口以及响应用户输入 设置背景颜色 创建设置类 添加飞船图像 创建ships ...

  2. 怎么运行python外星人入侵_python编程:从入门到实践 外星人入侵项目:武装飞船 代码运行不显示飞船...

    我的飞船去哪了???!!! 照着书上的项目完整的代码运行一下,结果屏幕上没出现飞船,代码都是对的呀,这是咋回事? 屏幕高度问题 class Settings(): """ ...

  3. python编程:从入门到实践 外星人入侵项目:武装飞船 代码运行不显示飞船

    我的飞船去哪了???!!! 照着书上的项目完整的代码运行一下,结果屏幕上没出现飞船,代码都是对的呀,这是咋回事? 屏幕高度问题 class Settings():"""A ...

  4. python项目——外星人入侵游戏

    开发大型项目时,做好规划后再动手编写项目很重要.规划可确保你不偏离轨道,从而提高项目成功的可能性. 下面来编写有关游戏<外星人入侵>的描述,其中虽然没有涵盖这款游戏的所有细节,但能让你清楚 ...

  5. Python项目 外星人入侵游戏

    终于开始入门项目了,使用Pygame包,跟着教程的过程敲打游戏来学习,做了自己的笔记 文末有完整项目代码,或者点这 -->链接 目标 在游戏<外星人入侵>中,玩家控制着一艘最初出现在 ...

  6. 记录第一个 python项目 外星人入侵小游戏

    学习目标:提升自己的编程能力## 标题 *> 可谓是一波三折 ,刚开始看的一本电子书 里面是python2.0的内容,我敲着在3系列的编译器试着2.0的代码可谓好不酸爽 那报错 绝了 学习内容: ...

  7. python随机出现外星人飞船_python外星人入侵游戏如何改变飞船的大小

    Python从入门到实践里的<外星人入侵> Python外星人入侵问题分享助世界上从来不缺少风景,缺少的只是欣赏的心情. Python项目外星人入侵添加爆照效果有没有原码<Pytho ...

  8. python外星人入侵飞船上下移动_Python 项目练习1 外星人入侵(武装飞船)

    Python 项目练习1 外星人入侵(武装飞船) 2020年07月30日 | 萬仟网IT编程 | 我要评论 本项目具体步骤在书本<Python编程:从入门到实践>第十二章中安装pygame ...

  9. python项目一:外星人入侵:(一)武装飞船

    第一章:武装飞船 1.1.规划项目 1.2.安装Pygame 1.3.开始游戏项目 1.3.1.创建pygame窗口以及响应用户输入 1.3.2.设置背景色 1.3.3.创建设置类 1.4.添加飞船图 ...

最新文章

  1. 【通俗理解线性代数】 -- 施密特正交化与QR分解
  2. 网络营销再掀波澜,微博独领风骚
  3. openfire插件开发的四种方式
  4. Mybatis 学习系列文章
  5. [scala-spark]8. RDD的实现和编程接口
  6. 从M2M迁移到IIoT工业物联网
  7. 松露菌行业调研报告 - 市场现状分析与发展前景预测
  8. 保留五天的日志 php,怎样让日志在归档目录保留5天?
  9. exfat linux 驱动_「正点原子Linux连载」第二章Ubuntu系统入门(2)
  10. 银保监会发函!股份制银行助贷、联合贷业务红线划定!
  11. java毕业设计易医就医购药交互平台Mybatis+系统+数据库+调试部署
  12. 利用平面投影坐标与经纬度粗略自动计算高斯投影坐标系参数
  13. 加权平均数的例子_加权平均值计算器
  14. python把英语句子成分字母_python把英语句子成分字母
  15. Spring Boot:The field file exceeds its maximum permitted size of 1048576 bytes.
  16. LODOP设计打印模板
  17. Navicat模糊查询表
  18. leaflet怎么获取矩形的中心点位 getCenter()
  19. 【洛谷】P2689 东南西北 c++
  20. 不是技术也能看懂云计算,大数据,人工智能

热门文章

  1. 算法设计-归并两个非递减有序排列的线性表
  2. PCL下使用区域增长算法进行点云平面分割的实现
  3. 关于copy树形结构
  4. 操盘策略:KDJ三线合一 必定孕育大牛股
  5. 如何提高移动应用的客户满意度?
  6. 实训七 思科三层交换机基本配置
  7. label 的for属性的使用
  8. xsi:noNamespaceSchemaLocation的解析
  9. App Store上架iOS App的详细流程
  10. 屏蔽电脑右下角弹广告