手把手教你使用Pygame制作飞机大战小游戏,4万字超详细讲解!
点击上方“早起Python”,关注并“星标”
每日接收原创Python干货!
大家好,偷学Python系列是由小甜同学从初学者的角度学习Python的笔记,其特点就是全文大多由新手易理解的代码与注释及动态演示。刚入门的读者千万不要错过!
本文带来的是偷学Python最后的项目二:使用Python开发飞机大战游戏,本文目录如下
甜甜先说
这次用Python中的pygame
模块来完成一个飞机大战的小游戏;基本思路是通过方向键来控制飞机的左右移动射击飞船。先来看下最后的效果
为了新手也能完成,本文记录了编写的全部流程,也就是每次修改的代码也包括在内,并且给大多数代码都加上了能看懂的注释,看一下最终的的统计字数
一共敲了4万个字符,希望能帮到感兴趣的读者!
安装Pygame
要完成这个项目肯定要安装pygame第三方库,首先通过命令行工具检测系统是否安装的pip工具
python -m pip --version
小甜是Windows系统,这里只提供Windows系统的检测方法 如果未安装则安装pip工具,安装则请跳过这一步
python get-pip.py
安装完毕以后退回第一步重新检测,现在安装pygame
python -m pip install pygame --user
或者通过pycharm安装第三个库,现在导入pygame即可
import pygame
制作小飞机
搞起来
目标:创建一个可以左右移动的小飞机,用户可以通过空格space
键来控制飞机发射子弹。
创建背景
创建一个空背景
首先编写一个空的pygame
窗口,文件名为plane_war.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
import sys # 用于退出游戏def run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率screen = pygame.display.set_mode((1000, 600)) # 大小为1000px乘以600px# 打印其类型# print(type(screen)) # <class 'pygame.Surface'>pygame.display.set_caption("飞机大战") # 标题# 存储背景的变量bg_img = pygame.image.load("./imgs/bg_img.png") # 相对路径print(bg_img)# 开始游戏的主循环while True:# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():# 每次循环都会重新绘制屏幕screen.blit(bg_img, [0, 0]) # 绘制图像if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()# 将完整显示Surface更新到屏幕pygame.display.flip()run_game()
display.set_mode
返回的是一个Surface
数据类型
效果图
创建设置类
一个游戏通常有n多个设置,如果每次想改变其中的某一个值的话在主文件中寻找容易眼花缭乱,现在创建一个新的文件settings.py
,专门用来存储这些信息
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygameclass Settings:"""存储飞机大战的所有设置"""def __init__(self):# 屏幕设置self.screen_width = 1000self.screen_height = 600self.bg_img = pygame.image.load("./imgs/bg_img.png")
现在来改写plane_war.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
import sys # 用于退出游戏
from settings import Settings # 引入settings.pydef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))pygame.display.set_caption("飞机大战") # 标题# 开始游戏的主循环while True:# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():# 每次循环都会重新绘制屏幕screen.blit(setting.bg_img, [0, 0]) # 绘制图像if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()# 将完整显示Surface更新到屏幕pygame.display.flip()run_game()
添加小飞机
这里用到的小飞机
绘制小飞机
现在图像也有了,来创建一个plane.py
模块,其中有一个Plane
类,来存储飞机的各种行为
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygameclass Plane:def __init__(self, screen):# 初始化小飞机并设置其初始位置self.screen = screen# 加载图像,并获得其矩形区域self.img_plane = pygame.image.load("./imgs/plane.png")self.rect = self.img_plane.get_rect() # 得到小飞机的的矩形区域self.screen_rect = self.screen.get_rect() # 得到screen的矩形区域# 将小飞机放到底部中央self.rect.centerx = self.screen_rect.centerx # 水平居中self.rect.bottom = self.screen_rect.bottom # 底部def blitme(self):# 在指定位置绘制小飞机self.screen.blit(self.img_plane, self.rect)
get_rect
会返回Surface的矩形的区域,.centerx
和.bottom
是其两个属性
改写plane_war.py
将小飞机绘制在屏幕上
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
import sys # 用于退出游戏
from settings import Settings # 引入settings.py
from plane import Planedef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建小飞机plane = Plane(screen)# 开始游戏的主循环while True:# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():# 每次循环都会重新绘制屏幕screen.blit(setting.bg_img, [0, 0]) # 绘制图像plane.blitme() # 将飞船绘制到屏幕上if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()# 将完整显示Surface更新到屏幕pygame.display.flip()run_game()
效果图
创建一个存储运行函数的模块
为了不使plane_war.py
太长而影响阅读,来创建一个名为game_func.py
的模块,用其飞机大战运行的函数,使其逻辑更容易理解
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import sys
import pygamedef check_events():# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()def update_screen(screen, bg_img, plane):# 更新屏幕的图像# 每次循环都会重新绘制屏幕screen.blit(bg_img, [0, 0]) # 绘制图像plane.blitme() # 将飞船绘制到屏幕上# 将完整显示Surface更新到屏幕pygame.display.flip()
check_events
函数用来完成窗口不会关闭的功能,update_screen
用来完成更新图像的功能,有3个形参,Surface对象、背景图像、小飞机函数
因为check_events
完成了退出游戏的操作,所以plane_war.py
就不需要sys模块了,更新后的plane_war.py
如下
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings # 引入settings.py
from plane import Plane
import game_func as fgdef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建小飞机plane = Plane(screen)# 开始游戏的主循环while True:# 不关闭窗口fg.check_events()# 绘制图像fg.update_screen(screen, setting.bg_img, plane)run_game()
控制小飞机
通过修改小飞机的坐标来完成移动,在用户按下方向键的时候小飞机的坐标进行有规律的变化
控制小飞机移动
当用户按键时,都会在pygame
中注册一个事件,任何一个事件都是通过pygame.event.get()
获取的,因此可以在函数体内,为每个按键都注册一个KEYDOWN
事件。
现在将check_events
函数改写,通过检测按下键位,来对小飞机进行移动
def check_events(plane):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:if event.key == pygame.K_RIGHT:# 小飞机往又移动plane.rect.centerx += 1
现在按一下小飞机移动一个像素,一般的游戏都是通过按下不送则一直移动,
Pygame
中的pygame.KEYUP
可以检测用户是否松开按键 现在结合KEYDOWN
和KEYUP
来完成一个持续移动
控制小飞机持续移动
来定义一个标志位,来判断用户是否按下按键,默认为Flase
一旦检测到用户按下俺家则为True
,小飞机就可以持续移动
由于小飞机是通过plane.py
文件来控制的,对这个文件进行改写
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygameclass Plane:def __init__(self, screen):# 初始化小飞机并设置其初始位置self.screen = screen# 加载图像,并获得其矩形区域self.img_plane = pygame.image.load("./imgs/plane.png")self.rect = self.img_plane.get_rect() # 得到小飞机的的矩形区域self.screen_rect = self.screen.get_rect() # 得到screen的矩形区域# print(self.screen_rect)# 将小飞机放到底部中央self.rect.centerx = self.screen_rect.centerx # 水平居中self.rect.bottom = self.screen_rect.bottom # 底部# 标志位self.mv_right = False# 定义一个调整小飞机位置的方法def update(self):# 根据标志位的调整小飞机的位置if self.mv_right:self.rect.centerx += 1def blitme(self):# 在指定位置绘制小飞机self.screen.blit(self.img_plane, self.rect)
update
方法是标志位为True时,小飞机就开始移动
改写game_func.py
中的check_events
函数
def check_events(plane):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:if event.key == pygame.K_RIGHT:# 当用户按下键位时标志位为Trueplane.mv_right = Trueelif event.type == pygame.KEYUP:if event.key == pygame.K_RIGHT:# 当用户松开键位为falseplane.mv_right = False
最后只要在plane_war.py
中调用update
方法就可以完成持续移动的操作
完成左右移动
用同样的方法完成向左移动
改写后的plane.py
文件
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygameclass Plane:def __init__(self, screen):# 初始化小飞机并设置其初始位置self.screen = screen# 加载图像,并获得其矩形区域self.img_plane = pygame.image.load("./imgs/plane.png")self.rect = self.img_plane.get_rect() # 得到小飞机的的矩形区域self.screen_rect = self.screen.get_rect() # 得到screen的矩形区域# print(self.screen_rect)# 将小飞机放到底部中央self.rect.centerx = self.screen_rect.centerx # 水平居中self.rect.bottom = self.screen_rect.bottom # 底部# 标志位self.mv_right = Falseself.mv_left = False# 定义一个调整小飞机位置的方法def update(self):# 根据标志位的调整小飞机的位置if self.mv_right:self.rect.centerx += 1if self.mv_left:self.rect.centerx -= 1def blitme(self):# 在指定位置绘制小飞机self.screen.blit(self.img_plane, self.rect)
改写后的game_func.py
中的check_events
函数
def check_events(plane):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:if event.key == pygame.K_RIGHT:# 当用户按下键位时标志位为Trueplane.mv_right = Trueelif event.key == pygame.K_LEFT:plane.mv_left = Trueelif event.type == pygame.KEYUP:if event.key == pygame.K_RIGHT:# 当用户松开键位为falseplane.mv_right = Falseelif event.key == pygame.K_LEFT:plane.mv_left = False
调整速度
现在的小飞机一次是按1px来移动的,那速度是相当的缓慢,修改一下小飞机的移动速度
首先在setting.py
中添加一行
self.plane_speed = 2.5
现在对plane.py
做修改
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygameclass Plane:def __init__(self, screen, setting):# 初始化小飞机并设置其初始位置self.screen = screenself.setting = setting # 实例化属性# 加载图像,并获得其矩形区域self.img_plane = pygame.image.load("./imgs/plane.png")self.rect = self.img_plane.get_rect() # 得到小飞机的的矩形区域self.screen_rect = self.screen.get_rect() # 得到screen的矩形区域# print(self.screen_rect)# 将小飞机放到底部中央self.rect.centerx = self.screen_rect.centerx # 水平居中self.rect.bottom = self.screen_rect.bottom # 底部# 将其修改为浮点数self.center = float(self.rect.centerx)# 标志位self.mv_right = Falseself.mv_left = False# 定义一个调整小飞机位置的方法def update(self):# 根据标志位的调整小飞机的位置if self.mv_right:self.center += self.setting.plane_speed # settings中的属性if self.mv_left:self.center -= self.setting.plane_speed# 根据self.center的值来更新self.rect.centerxself.rect.centerx = self.centerdef blitme(self):# 在指定位置绘制小飞机self.screen.blit(self.img_plane, self.rect)
将plane_war.py
中的plane
增加一个属性
plane = Plane(screen, setting)
限制小飞机的活动范围
现在小飞机已经可以飞呀飞,但是没有东西限制他,很容易就飞出了屏幕。现在将其限制在屏幕中,避免飞出去。
只需要修改plane.py
中的update
方法
重构game_func.py
中的check_events
函数
随着小飞机的功能愈来愈多,现在将check_events
重构为3个函数,捕捉用户按键和用户松开键分别定义两个函数
重构后的check_events
def check_keydown_events(event, plane):# 捕捉用户按下if event.key == pygame.K_RIGHT:# 当用户按下键位时标志位为Trueplane.mv_right = Trueelif event.key == pygame.K_LEFT:plane.mv_left = Truedef check_keyup_events(event, plane):# 捕捉用户松开if event.key == pygame.K_RIGHT:# 当用户松开键位为falseplane.mv_right = Falseelif event.key == pygame.K_LEFT:plane.mv_left = Falsedef check_events(plane):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, plane)elif event.type == pygame.KEYUP:check_keyup_events(event, plane)
效果图
完成射击功能
通过玩家按下空格来发射子弹(一小小小的矩形)
添加子弹的设置
在settings.py
中的__init__
方法中添加以下数据
# 子弹的设置
self.bullet_speed = 3 # 速度
self.bullet_width = 3 # 子弹的宽
self.bullet_height = 15 # 子弹的高
self.bullet_color = 100, 100, 100 # 子弹的颜色
创建Bullet类
创建存储子弹的Bullet
类的bullet.py
文件
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from pygame.sprite import Spriteclass Bullet(Sprite): # 继承pygame.sprite中的Sprite类"""子弹的管理"""def __init__(self, setting, screen, plane):super().__init__()self.screen = screen# 在(0,0)处创建一个表示子弹的矩形# pygame.Rect# 用于存储直角坐标的pygame对象self.rect = pygame.Rect(0,0, setting.self.bullet_width, setting.self.bullet_height)# 设置显示的位置self.rect.centerx = plane.rect.centerxself.rect.top = plane.rect.top# 让子弹的位置跟小飞机重叠,当子弹飞出了以后,就显得跟从小飞机里面射出来一样# 将子弹的坐标转换为浮点数self.y = float(self.rect.y)# 子弹的颜色self.color = setting.bullet.color# 子弹的速度self.speed = setting.bullet.speeddef update(self):# 向上移动子弹self.y -= self.speed# 根据self.y的值更新self.rect.yself.rect.y = self.ydef draw_bullet(self):"""绘制子弹"""# pygame.draw.rect()画一个矩形的形状pygame.draw.rect(self.screen, self.color, self.rect)
Bullet
类继承于pygame.sprite
中的Sprite
类,此类可以将游戏中的元素进行编组,可以同时操作编组中的所有元素
将子弹存储到编组中
首先在plane_war.py
中创建一个编组,用于存储所有有效的子弹,以便能够管理发射出去的子弹;这个编组是pygame.sprite.Group
类的一个实例;pygame.sprite.Group
类类似于列表,但是提供了有助于开发游戏的额外功能。在主循环中,我们将使用这个编组在屏幕上绘制子弹,以及更新没颗子弹的位置。
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings
from plane import Plane
import game_func as fg
from pygame.sprite import Groupdef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建一个存储子弹的编组bullets = Group()# 创建小飞机plane = Plane(screen, setting)# 开始游戏的主循环while True:# 不关闭窗口fg.check_events(plane, setting, screen, bullets)# 调用小飞机移动的方法plane.update()bullets.update()# 绘制图像fg.update_screen(screen, setting.bg_img, plane, bullets)run_game()
开火
通过修改game_func.py
中的函数来完成发射子弹的操作
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import sys
from bullet import Bullet
import pygamedef check_keydown_events(event, plane, setting, screen, bullets):# 捕捉用户按下if event.key == pygame.K_RIGHT:# 当用户按下键位时标志位为Trueplane.mv_right = Trueelif event.key == pygame.K_LEFT:plane.mv_left = Trueelif event.key == pygame.K_SPACE:# 创建一个子弹,并将其加入到编组bullets中new_bullet = Bullet(setting, screen, plane)bullets.add(new_bullet)def check_keyup_events(event, plane):# 捕捉用户松开if event.key == pygame.K_RIGHT:# 当用户松开键位为falseplane.mv_right = Falseelif event.key == pygame.K_LEFT:plane.mv_left = Falsedef check_events(plane, setting, screen, bullets):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, plane, setting, screen, bullets)elif event.type == pygame.KEYUP:check_keyup_events(event, plane)def update_screen(screen, bg_img, plane, bullets):# 更新屏幕的图像# 每次循环都会重新绘制屏幕screen.blit(bg_img, [0, 0]) # 绘制图像# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet() # 绘制子弹plane.blitme() # 将飞船绘制到屏幕上# 将完整显示Surface更新到屏幕pygame.display.flip()
用户按下空格之后会创建一个子弹(一个名为new_bullet的Bullet实例),并使用
add
追加到编组中 方法bullets.sprites
返回一个列表,包含了编组中的所有精灵,遍历编组中的精灵,并通过draw_bullet()绘制到屏幕上
效果图:
现在已经完成基本的射击功能了,虽然子弹到达屏幕顶端后消失了,这仅仅是因为pygame
无法绘制屏幕外面的东西,这些子弹实际还是存在的,他们的y
坐标为负数且越来越少,会继续消耗内存
删除已经消失的子弹
这里通过.copy
进行浅拷贝,然后检测子弹是否消失,然后再将其删除
对plane_war.py
中的while
语句中添加下面这一句
# 删除已经消失的子弹
for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)# print(len(bullets)) # 用于测试子弹是否删除
注意:在
fg.update_screen
之前进行添加
限制子弹的数量
为了不使这个小游戏跟开挂似得,肯定要限制一下发射子弹的数量,在settings.py
中添加一行
# 限制子弹的数量
self.bullet_allowed = 5
在check_keydown_events
函数体中增加一个判断即可
简化plane_war.py
中的while
语句
将发射子弹移步到game_func.py
文件中并创建一个update_bullets
def update_bullets(bullets):# 将编组中的每个子弹调用bullet.update()bullets.update()# 删除已经消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)
此时的while
语句中就4行代码
while True:fg.check_events(plane, setting, screen, bullets) # 不关闭窗口plane.update() # 调用小飞机移动的方法fg.update_bullets(bullets) # 绘制子弹fg.update_screen(screen, setting.bg_img, plane, bullets) # 绘制图像
小飞机添加完毕的效果
制作飞船
现在小飞机也创建完成了,现在就该创建小飞机的敌人了,同样通过一个类来控制其所有行为,先来看看这个卡哇伊的飞船
目标:创建好非常让其随意移动,可以射杀飞船、当飞船碰到小飞机GAMEOVER,飞船碰到地面也GAMEOVER
创建飞船
创建Spaceship
类
创建一个名为spaceship.py
的文件来存储Spaceship
类
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""
import pygame
from pygame.sprite import Spriteclass Spaceship(Sprite):'''表示飞船的类'''def __init__(self, setting, screen):super().__init__()self.screen = screenself.setting = setting# 添加飞船图像self.img = pygame.image.load("./imgs/enemy.png")# 获取rect属性self.rect = self.img.get_rect()# 每个飞船最初都在屏幕左上角附近self.rect.x = self.rect.width # 飞船图像的左边距等于图像的宽度self.rect.y = self.rect.height # 飞船图书的上边距等于图像的高度self.x = float(self.rect.x)def blitme(self):# 绘制飞船图像self.screen.blit(self.img, self.rect)
这里除了位置基本与
Plane
类相同
实例化Spaceship
类
在plane_war.py
中添加Spaceship
实例
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings # 引入settings.py
from plane import Plane
import game_func as fg
from pygame.sprite import Group
from spaceship import Spaceshipdef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建一个存储子弹的编组bullets = Group()# 创建小飞机plane = Plane(screen, setting)# 创建飞船spaceship = Spaceship(setting, screen)# 开始游戏的主循环while True:# 不关闭窗口fg.check_events(plane, setting, screen, bullets)# 调用小飞机移动的方法plane.update()# 绘制子弹fg.update_bullets(bullets)# 绘制图像fg.update_screen(screen, setting.bg_img, plane, bullets, spaceship)run_game()
这里导入了一下新创建的
Spaceship
类,在while
循环外创建一个实例,给update_screen
传递一个飞船的实例
让飞船出现在屏幕上
修改update_screen
函数
def update_screen(screen, bg_img, plane, bullets, spaceship):# 更新屏幕的图像# 每次循环都会重新绘制屏幕screen.blit(bg_img, [0, 0]) # 绘制图像# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet() # 绘制子弹plane.blitme() # 将飞船绘制到屏幕上# 绘制飞船spaceship.blitme()# 将完整显示Surface更新到屏幕pygame.display.flip()
注意其顺序
现在这个好看的小飞船已经出现在了屏幕的左上角
创建一群小飞船
要绘制一群小飞船,需要确定一行能容纳多少个飞船以及要绘制多少行飞船。
确定一行可以容纳多少个飞船
确定一行可以容纳多少个外星人,需要看一下可以用的水平空间有多大。我们的游戏的屏幕宽度在settings.py
中的screen.width
存储,但需要在屏幕两遍都留下一定的边距,把它设置为小飞船的宽度。由于有两个边距,可以放置飞船的的水平空间为屏幕的宽度减去飞船宽度的2倍
公式为
available_space_x = setting.screen_width - (2 * spaceship_width)
还需要在飞船之间留有一定的宽度,即飞船宽度。所以显示飞船所需要的水平宽度为飞船宽度的2倍;现在确定一行可以容纳多少个飞船
number_spaceship_x = available_space_x / (2 * spaceship_width)
根据这些公式来创建飞船
创建一行飞船
为了创建一行飞船,首先在plane_war.py
中创建一个spaceships
的空编组用来存储全部的飞船,在调用game_func.py
中创建飞船群的函数
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings # 引入settings.py
from plane import Plane
import game_func as fg
from pygame.sprite import Groupdef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建小飞机plane = Plane(screen, setting)# 创建一个存储子弹的编组bullets = Group()# 创建飞船编组spaceships = Group()# 创建飞船群fg.create_fleet(setting, screen, spaceships)# 开始游戏的主循环while True:# 不关闭窗口fg.check_events(plane, setting, screen, bullets)# 调用小飞机移动的方法plane.update()# 绘制子弹fg.update_bullets(bullets)# 绘制图像fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships)run_game()
改造game.func.py
文件并编写创建飞船群函数create_fleet
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import sys
from bullet import Bullet
from spaceship import Spaceship
import pygamedef check_keydown_events(event, plane, setting, screen, bullets):# 捕捉用户按下if event.key == pygame.K_RIGHT:# 当用户按下键位时标志位为Trueplane.mv_right = Trueelif event.key == pygame.K_LEFT:plane.mv_left = Trueelif event.key == pygame.K_SPACE:if len(bullets) <= setting.bullet_allowed:# 创建一个子弹,并将其加入到编组bullets中new_bullet = Bullet(setting, screen, plane)bullets.add(new_bullet)def check_keyup_events(event, plane):# 捕捉用户松开if event.key == pygame.K_RIGHT:# 当用户松开键位为falseplane.mv_right = Falseelif event.key == pygame.K_LEFT:plane.mv_left = Falsedef check_events(plane, setting, screen, bullets):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, plane, setting, screen, bullets)elif event.type == pygame.KEYUP:check_keyup_events(event, plane)def update_screen(screen, bg_img, plane, bullets, spaceships):# 更新屏幕的图像# 每次循环都会重新绘制屏幕screen.blit(bg_img, [0, 0]) # 绘制图像# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet() # 绘制子弹# 绘制飞机plane.blitme()# 绘制飞船for spaceship in spaceships.sprites():spaceship.blitme()# 将完整显示Surface更新到屏幕pygame.display.flip()def update_bullets(bullets):# 将编组中的每个子弹调用bullet.update()bullets.update()# 删除已经消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)# print(len(bullets)) # 用于测试子弹是否删除def create_fleet(setting, screen, spaceships): # 创建飞船群函数# 创建一个飞船spaceship = Spaceship(setting, screen)spaceship_width = spaceship.rect.width # 飞船的宽度# 计算可以容纳多个飞船的宽度available_space_x = setting.screen_width - (2 * spaceship_width)number_spaceship_x = int(available_space_x / (2 * spaceship_width)) # 将其转换为整数for spaceship_number in range(number_spaceship_x):# 创建一个飞船并加入当前行spaceship = Spaceship(setting, screen)spaceship.x = spaceship_width + 2 * spaceship_width * spaceship_numberspaceship.rect.x = spaceship.xspaceships.add(spaceship)
效果图
因为一个飞船的宽度是占两个的位置,所以最后的空隙有点大,后期反正这个飞船是动起来的,这里先暂时忽略
将create_fleet
改写一下,拆分为三个函数体
# 计算每行可以容纳多少个外星人的函数
def get_number_spaceship_x(setting, spaceship_width):# 计算可以容纳多个飞船的宽度available_space_x = setting.screen_width - (2 * spaceship_width)number_spaceship_x = int(available_space_x / (2 * spaceship_width)) # 将其转换为整数return number_spaceship_xdef create_spaceship(setting, screen, spaceships, spaceship_number):# 创建一个飞船并加入当前行spaceship = Spaceship(setting, screen)spaceship_width = spaceship.rect.width # 飞船的宽度spaceship.x = spaceship_width + 2 * spaceship_width * spaceship_numberspaceship.rect.x = spaceship.xspaceships.add(spaceship)def create_fleet(setting, screen, spaceships):# 创建一个飞船spaceship = Spaceship(setting, screen)number_spaceship_x = get_number_spaceship_x(setting, spaceship.rect.width)for spaceship_number in range(number_spaceship_x):create_spaceship(setting, screen, spaceships, spaceship_number)
添加多行小飞船
添加多行就跟一行添加多个是类似的,同样用屏幕的高度减去飞船高度的2倍,这里需要注意的是为了不让小飞机死的很快下面留两倍的高度,还要减去小飞机的高度
available_space_y = setting.screen_heitght - 3 * spaceship_height - plane_height
计算可以放多少行
number_rows = available_space_y / spaceship_height
在game.func.py
中进行改写
# 计算每行可以容纳多少个外星人的函数
def get_number_spaceship_x(setting, spaceship_width):# 计算可以容纳多个飞船的宽度available_space_x = setting.screen_width - (2 * spaceship_width)number_spaceship_x = int(available_space_x / (2 * spaceship_width)) # 将其转换为整数return number_spaceship_x# 计算可以容纳多少行
def get_number_rows(setting, plane_height, spaceship_height):available_space_y = setting.screen_height - 3 * spaceship_height - plane_heightprint(available_space_y, spaceship_height)number_rows = int(available_space_y / spaceship_height)return number_rowsdef create_spaceship(setting, screen, spaceships, spaceship_number, number_rows):# 创建一个飞船并加入当前行spaceship = Spaceship(setting, screen)spaceship_width = spaceship.rect.width # 飞船的宽度spaceship.x = spaceship_width + 2 * spaceship_width * spaceship_numberspaceship.rect.x = spaceship.xspaceship.rect.y = spaceship.rect.height + 2 * spaceship.rect.height * number_rowsspaceships.add(spaceship)def create_fleet(setting, screen, spaceships, plane):# 创建一个飞船spaceship = Spaceship(setting, screen)number_spaceship_x = get_number_spaceship_x(setting, spaceship.rect.width)number_rows = get_number_rows(setting, plane.rect.height, spaceship.rect.height)for row_number in range(number_rows): # 循环的嵌套for spaceship_number in range(number_spaceship_x):create_spaceship(setting, screen, spaceships, spaceship_number, row_number)
这个写的话游戏刚开始我们的飞机就死掉了,现在来做一下修改
首先修改`spaceship.py
# 每个飞船最初都在屏幕左上角附近self.rect.x = self.rect.width # 飞船图像的左边距等于图像的宽度self.rect.y = self.rect.height # 飞船图书的上边距等于图像的高度self.rect.w = self.rect.width self.rect.h = int(self.rect.height / 2) # 将高度设置为一半self.x = float(self.rect.x)
由于其高度进行了改变,原来的公式也要进行相应的改变
available_space_y = setting.screen_heitght - 7 * spaceship_height - plane_height # 由之前的3变为7(因为高缩小了一般)
效果图
让飞船动起来
首先在settings.py
中增加小飞船相应的设置
# 飞船移动的速度
self.spaceship_speed = 1
# 飞船下降的速度
self.fleet_drop_speed = 10
# 标志位,1表示右移 -1表示左移
self.fleet_direction = 1 # 默认右移动
在spaceship.py
中增加判断是否位于边缘的方法和移动的方法
def check_edges(self):
'''如果有飞船位于屏幕边缘,就返回true'''
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:return True
elif self.rect.left <= 0:return Truedef update(self):
"""移动飞船"""
self.x += (self.setting.spaceship_speed * self.setting.fleet_direction)
self.rect.x = self.x
在game_func.py
中对spaceship.py
方法实例化
def change_fleet_direction(setting, spaceships):"""将所有飞船下移,并改变方向"""for spaceship in spaceships.sprites():spaceship.rect.y += setting.fleet_drop_speedsetting.fleet_direction *= -1 # 如果为1则相乘为-1,如果为-1相乘为1def check_fleet_edges(setting, spaceships):"""有飞船到达了边缘应采取的措施"""for spaceship in spaceships.sprites():if spaceship.check_edges(): # 如果为true,已经到了边缘,就执行change_fleet_directionchange_fleet_direction(setting, spaceships)breakdef update_spaceships(setting, spaceships):# 更新飞船的位置spaceships.update()# 检测时候又飞船处于边缘,并及时更新check_fleet_edges(setting, spaceships)
最后在主文件的while
语句中增加
fg.update_spaceships(setting, spaceships)
射击飞船
现在子弹和飞船碰撞在一起飞船并不会消失,而是从飞船上穿了过去,并没有达到射击飞船的效果,现在我们将完成这种效果
在这里我们使用game.sprite.groupcollide()
方法,此方法检测两个rect
是否有元素重叠,并返回一个字典
检测子弹与飞船碰撞
子弹击中飞船后飞船需要马上消失,所以需要在更新子弹的位置后面检测碰撞
方法game.sprite.groupcollide()
将每个子弹的rect
和每个飞船的rect
进行比较,返回一个字典,其中包含了发证碰撞的子弹和飞船。这个字典中每个键都是射中飞船的一颗子弹,相应的值为被击中的飞船
在函数update_bullets()
中来检测碰撞
def update_bullets(bullets, spaceships):# 将编组中的每个子弹调用bullet.update()bullets.update()# 删除已经消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)collisions = pygame.sprite.groupcollide(bullets, spaceships, True, True)
修改plane_war.py
中的fg.update_bullets
为其增加一个参数
fg.update_bullets(bullets, spaceships)
生成新的飞船
当把所有的飞船非射击完毕以后,其不会生成新的飞船
这里需要在update_bullets()
之后来判断其长度是否为0,如果为0则调用create_fleet
def update_bullets(bullets, spaceships, setting, screen, plane):# 将编组中的每个子弹调用bullet.update()bullets.update()# 删除已经消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)collisions = pygame.sprite.groupcollide(bullets, spaceships, True, True) # 两个实参true则是为了将其删除if len(spaceships) == 0:bullets.empty() # 删除编组中的所有精灵(子弹)create_fleet(setting, screen, spaceships, plane) # 重新调用生成飞船
修改plane_war.py
中的fg.update_bullets
为其增加参数
fg.update_bullets(bullets, spaceships, setting, screen, plane)
测试效果
我这里为了测试我将子弹的宽度给修改了自己写的游戏想怎么改就怎么改,游戏意思,哈哈~
总结
游戏结束
当然了,这么玩就失去了游戏的乐趣了,肯定是不可以啊。
现在就增加难度,当飞船碰到飞机、飞船到达地面时就要搞点事情了,不过也不能不给小飞机机会
检测飞船与飞机碰撞
现在我们编写一个新的类GameStats
用来跟踪游戏的信息,将其保存为一个新的文件game_stats.py
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/4
"""class GameStats:"""跟踪游戏统计信息"""def __init__(self, setting):self.setting = settingself.reset_stats()def reset_stats(self):# 初始化在游戏运行期间可能变化的统计信息self.planes_left = self.setting.plane_limit
在settings
中添加一行设置
self.plane_limit = 3 # 小飞机的生命限制
在plane_war.py
中创建GameStats
的实例
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings # 引入settings.py
from plane import Plane
import game_func as fg
from pygame.sprite import Group
from game_stats import GameStatsdef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建一个用于存储游戏统计信息的实例stats = GameStats(setting)# 创建小飞机plane = Plane(screen, setting)# 创建一个存储子弹的编组bullets = Group()# 创建飞船编组spaceships = Group()# 创建飞船群fg.create_fleet(setting, screen, spaceships, plane)# 开始游戏的主循环while True:# 不关闭窗口fg.check_events(plane, setting, screen, bullets)# 调用小飞机移动的方法plane.update()# 更新子弹位置fg.update_bullets(bullets, spaceships, setting, screen, plane)# 更新飞船位置fg.update_spaceships(setting, spaceships, plane, stats, screen, bullets)# 绘制图像fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships)run_game()
因为检测飞船是否与飞机碰撞,所有要对update_spaceships
函数进行改写
在添加一个发生碰撞后执行什么从操作的函数plane_ship()
def plane_hit(setting, spaceships, plane, stats, screen, bullets):"""有飞船撞击到飞机以后已经数量减1,创建一批新的飞创,并将飞机重新反之到屏幕的原始位置还将引入time模块的sleep函数实现暂停的效果"""stats.planes_left -= 1 # 将planes_left减1# print(stats.planes_left)# 清空飞船和子弹的编组spaceships.empty()bullets.empty()# 创建新的飞机和飞船create_fleet(setting, screen, spaceships, plane)plane.center_plane()sleep(1) # 暂停1秒def update_spaceships(setting, spaceships, plane, stats, screen, bullets):# 更新飞船的位置spaceships.update()# 检测时候又飞船处于边缘,并及时更新check_fleet_edges(setting, spaceships)# 检测飞船与飞机直接的碰撞'''pygame.sprite.spritecollideany方法* 接受两个参数,一个精灵和一个编组,* 检测编组中的成员是否与碰撞,如果检测到碰撞则停止遍历编组* 如果没有发生碰撞则返回None'''game_over = pygame.sprite.spritecollideany(plane, spaceships)if game_over:plane_hit(setting, spaceships, plane, stats, screen, bullets)
注意:在一开始要导入time
模块的sleep
函数
最后在plane.py
文件中添加center.plane
方法,来实现居中
def center_plane(self):"""让小飞机居中"""self.center - self.screen_rect.centerx
这里并没有对小飞机进行重新的绘制,仅仅是将其重新放回中间
检测飞船到达屏幕底部
将创建一个新的函数来完成这项任务,名为check_spaceship_bottom()
def check_spaceship_bottom(setting, spaceships, plane, stats, screen, bullets):# 检测是否有飞船触碰到底部screen_rect = screen.get_rect()for spaceship in spaceships.sprites():if spaceship.rect.bottom >= screen_rect.bottom:# 跟飞船碰撞一样处理plane_hit(setting, spaceships, plane, stats, screen, bullets)
在update_spaceships
函数体中调用此函数
check_spaceship_bottom(setting, spaceships, plane, stats, screen, bullets)
结束游戏
现在游戏还是不会结束的,planes_left
只会无限的减少,这里在GameStats
中添加一个标志位game_active
,用来记录飞船的数量是否为0
self.game_active = True
修改plane_hit()
的代码
def plane_hit(setting, spaceships, plane, stats, screen, bullets):"""有飞船撞击到飞机以后已经数量减1,创建一批新的飞创,并将飞机重新反之到屏幕的原始位置还将引入time模块的sleep函数实现暂停的效果"""if stats.planes_left > 0:stats.planes_left -= 1 # 将planes_left减1# print(stats.planes_left)# 清空飞船和子弹的编组spaceships.empty()bullets.empty()# 创建新的飞机和飞船create_fleet(setting, screen, spaceships, plane)plane.center_plane()sleep(1) # 暂停1秒else:stats.game_active = False
这里游戏也不会真正的结束,这里暂时空着,下面为其补全。想要退出的话在else子句中调用
sys.exit()
即可
完善项目
现在这个小游戏的基本雏形已经出来了,但是还称不上一个完整的游戏,现在为其增加一个开始按钮,用于启动游戏和结束游戏;随着游戏时间的增长游戏难度也将进行增长;在增加一个记分系统。
play按钮
我们的目的是让游戏一开始点击play按钮可以开始游戏,游戏结束时在点击play按钮又能开始游戏
所以我们现在需要将GameStats
中的标志位game_active
为False
,让游戏默认为不活动状态
self.game_active = False
只有这个样子才能完成play按钮才能完成他想完成的工作
由于pygame
中没有创建按钮的方法,需要创建一个Button
类,用于创建带标签的实心矩形。
现在创建一个button.py
的文件
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/5
"""
import pygame.font # 导入pygame.font模块,可以使屏幕渲染在屏幕上class Button:def __init__(self, setting, screen, msg):"""初始化按钮的属性"""self.screen = screenself.screen_rect = screen.get_rect()# 设置按钮的大小self.width = 200self.height = 20# 设置按钮的颜色self.button_color = (0, 255, 0)# 设置文本的颜色self.text_color = (200, 200, 200) # 设置字体的大小self.font = pygame.font.SysFon("SimHei", 48) # 字体为黑体大小为48像素# 创建按钮的rect对象self.rect = pygame.Rect(0, 0, self.width, self.height)# 使按钮居中self.rect.center = self.screen_rect.center# 按钮的标签只需要创建一次self.prep_msg(msg)def prep_mas(self, msg):# msg渲染为图像"""font.reder方法是将msg中的文本转换为图像* 参数True是开启抗锯齿模式* self.text_color是文本的颜色* self.button_color是背景颜色"""self.msg_image = self.font.reder(msg, True, self.text_color, self.button_color)# 使其在按钮上居中self.msg_image_rect = self.msg_image.get_rect()self.msg_image_rect.center = self.rect.centerdef draw_button(self):# 绘制按钮self.screen.fill(self.button_color, self.rect) # 用一个颜色填充文本self.screen.blit(self.msg_imagem, self.msg_image_rect) # 绘制文本
现在将按钮绘制出来,并设置在非活动状态下显示按钮
在plane_war.py
中添加Button
类的实例化,并将其作为参数传递给update_screen()
以便可以在屏幕更新时显示按钮
...
from button import Buttondef run_game():...pygame.display.set_caption("飞机大战") # 标题# 创建play按钮play_button = Button(setting, screen, "Play")...# 开始游戏的主循环while True:...# 绘制图像fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships, stats, play_button)run_game()
现在修改一下update_screen()
函数
def update_screen(screen, bg_img, plane, bullets, spaceships, stats, play_button):# 更新屏幕的图像# 每次循环都会重新绘制屏幕screen.blit(bg_img, [0, 0]) # 绘制图像# 绘制子弹for bullet in bullets.sprites():bullet.draw_bullet() # 绘制子弹# 绘制飞机plane.blitme()for spaceship in spaceships.sprites():spaceship.blitme()# 如果游戏处于非活动状态,绘制Play按钮if not stats.game_active:play_button.draw_button()# 将完整显示Surface更新到屏幕pygame.display.flip()
开始游戏
现在按钮出来了,但是没有任何功能,现在来完成这个按钮的功能
这里需要检测鼠标按下的事件来做出相应的操作,修改check_events()
函数,为其增加两个参数stats, **play_butto
然后做出相应的操作
def check_events(plane, setting, screen, bullets, stats, play_button):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, plane, setting, screen, bullets)elif event.type == pygame.KEYUP:check_keyup_events(event, plane)elif event.type == pygame.MOUSEBUTTONDOWN: # 检测MOUSEBUTTONDOWN事件mouse_x, mouse_y = pygame.mouse.get_pos() # 返回一个元组,包含鼠标单击时的坐标check_play_button(stats, play_button, mouse_x, mouse_y) # 调用check_play_buttondef check_play_button(stats, play_button, mouse_x, mouse_y):# 用于检测鼠标的坐标是否与按钮相重合# 玩家单机play按钮时开始游戏if play_button.rect.collidepoint(mouse_x, mouse_y): # collidepoint检测单击的位置是否在按钮的rect内stats.game_active = True
修改一下循环中的check_events()
函数将参数传递进去
fg.check_events(plane, setting, screen, bullets, stats, play_button)
效果图
如果3次机会用完了这个play按钮还是会出来
现在单机play按钮对于游戏来说没有任何影响,下面对这个功能进行完善
重新游戏
现在完成当玩家点击play按钮都会重置游戏(重置游戏的活动状态和飞机的次数),删除所有的子弹和飞船,创建一批新的飞船,并让飞船居中
def check_events(plane, setting, screen, bullets, stats, play_button, spaceships):# 为了防止游戏窗口启动会立马关闭,在其中增加一个游戏循环(无限循环),for event in pygame.event.get():if event.type == pygame.QUIT: # QUIT用户请求程序关闭sys.exit()elif event.type == pygame.KEYDOWN:check_keydown_events(event, plane, setting, screen, bullets)elif event.type == pygame.KEYUP:check_keyup_events(event, plane)elif event.type == pygame.MOUSEBUTTONDOWN: # 检测MOUSEBUTTONDOWN事件mouse_x, mouse_y = pygame.mouse.get_pos() # 返回一个元组,包含鼠标单击时的坐标check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships)def check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships):# 玩家单机play按钮时开始游戏if play_button.rect.collidepoint(mouse_x, mouse_y): # collidepoint检测单击的位置是否在按钮的rect内stats.game_active = True# 重置游戏统计信息stats.reset_stats()# 清空飞船列表和子弹列表spaceships.empty()bullets.empty()# 让飞机居中plane.center_plane()
在主循环中添加相应的参数
fg.check_events(plane, setting, screen, bullets, stats, play_button, spaceships)
解决一个bug
现在游戏中有一个小bug,不管游戏开没开始,单击中间的按钮区域都会重新开始,修改这个bug,可以让游戏在stats.game_active
值为False时才开始,还有一个问题就是游戏开始以后光标没有任何的作用,这个时候可以将光标隐藏
修改check_play_button()
函数
def check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships):# 玩家单机play按钮时开始游戏# collidepoint检测单击的位置是否在按钮的rect内button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)if button_clicked and not stats.game_active: # 当stats.game_active的值为False时,取反才会执行# 隐藏光标pygame.mouse.set_visible(False)# 游戏状态stats.game_active = True# 重置游戏统计信息stats.reset_stats()# 清空飞船列表和子弹列表spaceships.empty()bullets.empty()# 让飞机居中plane.center_plane()
当然,光标隐藏了就要需要显示,在plane_hit()
中stats.game_active
一起修改,当游戏状态位False
时,就要需要鼠标,所以在其下面修改为True
def plane_hit(setting, spaceships, plane, stats, screen, bullets):"""有飞船撞击到飞机以后已经数量减1,创建一批新的飞创,并将飞机重新反之到屏幕的原始位置还将引入time模块的sleep函数实现暂停的效果"""if stats.planes_left > 0:stats.planes_left -= 1 # 将planes_left减1# print(stats.planes_left)# 清空飞船和子弹的编组spaceships.empty()bullets.empty()# 创建新的飞机和飞船create_fleet(setting, screen, spaceships, plane)plane.center_plane()sleep(1) # 暂停1秒else:stats.game_active = False# 将光标设置为显示pygame.mouse.set_visible(True)
提高等级
现在这个游戏虽然有了死亡,但是 这种游戏难度只有想玩,还是不会死掉的,现在要随着消灭的飞船的数量来增加游戏的难度
修改速度的设置
现在重新组织一下Settings
类,将游戏中的这还是分为静态和动态两类,在添加一个提高速度的方法
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygameclass Settings:"""存储飞机大战的所有设置"""def __init__(self):# 屏幕设置self.screen_width = 1000self.screen_height = 600self.bg_img = pygame.image.load("./imgs/bg_img.png")#小飞机设置# 小飞机的生命限制self.plane_limit = 3# 子弹的设置self.bullet_width = 3 # 子弹的宽self.bullet_height = 15 # 子弹的高self.bullet_color = 190, 190, 190 # 子弹的颜色# 限制子弹的数量self.bullet_allowed = 5# 飞船下降的速度self.fleet_drop_speed = 10# 以什么样的速度加快游戏节奏self.speedup_scale = 1.2self.initialize_dynamic_settings()def initialize_dynamic_settings(self):#小飞机设置# 小飞机的移动速度self.plane_speed = 2.5# 子弹的设置self.bullet_speed = 3 # 速度# 飞船移动的速度self.spaceship_speed = 2# 标志位,1表示右移 -1表示左移self.fleet_direction = 1 # 默认右移动def increase_speed(self):"""提高游戏节奏"""self.plane_speed *= self.speedup_scaleself.bullet_speed *= self.speedup_scaleself.spaceship_speed *= self.speedup_scale
这里设置好了,现在我们想要在新的一批飞船来临之前来增加游戏的节奏,在创建新的一批飞船
def update_bullets(bullets, spaceships, setting, screen, plane):# 将编组中的每个子弹调用bullet.update()bullets.update()# 删除已经消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)collisions = pygame.sprite.groupcollide(bullets, spaceships, True, True) # 两个实参true则是为了将其删除if len(spaceships) == 0:bullets.empty() # 删除编组中的所有精灵(子弹)# 加快游戏节奏setting.increase_speed()create_fleet(setting, screen, spaceships, plane) # 重新调用生成飞船
通过修改
update_bullets
,在飞船数量为0的时候进行加速
修改plane_war.py
这时不论game_active
的值为False
还是True
一开始都会创建一些图像,这里通过if
语句来判断是否创建
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
from settings import Settings # 引入settings.py
from plane import Plane
import game_func as fg
from pygame.sprite import Group
from game_stats import GameStats
from button import Buttondef run_game():# 初始化游戏pygame.init()# 设置屏幕的分辨率setting = Settings()screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600pxpygame.display.set_caption("飞机大战") # 标题# 创建play按钮play_button = Button(setting, screen, "Play")# 创建一个用于存储游戏统计信息的实例stats = GameStats(setting)# 创建小飞机plane = Plane(screen, setting)# 创建一个存储子弹的编组bullets = Group()# 创建飞船编组spaceships = Group()# 开始游戏的主循环while True:# 不关闭窗口fg.check_events(plane, setting, screen, bullets, stats, play_button, spaceships)if stats.game_active: # 根据游戏状态来判断是否需要创建其图像# 调用小飞机移动的方法plane.update()# 更新子弹位置fg.update_bullets(bullets, spaceships, setting, screen, plane)# 更新飞船位置fg.update_spaceships(setting, spaceships, plane, stats, screen, bullets)# 绘制图像fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships, stats, play_button)run_game()
重置游戏速度
目前来说每一次游戏开始都是接着上一次的速度开始增加的,这肯定是不可以的,现在修改当玩家点击play按钮是,速度复原
修改check_play_button()
函数
def check_play_button(plane, setting, screen, bullets, stats, play_button, mouse_x, mouse_y, spaceships):# 玩家单机play按钮时开始游戏# collidepoint检测单击的位置是否在按钮的rect内button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)if button_clicked and not stats.game_active: # 当stats.game_active的值为False时,取反才会执行# 重置游戏设置setting.initialize_dynamic_settings()# 隐藏光标pygame.mouse.set_visible(False)# 游戏状态stats.game_active = True# 重置游戏统计信息stats.reset_stats()# 清空飞船列表和子弹列表spaceships.empty()bullets.empty()# 让飞机居中plane.center_plane()
分数系统
现在完成一个跟踪玩家的事情情况来展示得分、最高分、当前的等级、余下的飞船数量
得分是游戏的一项统计信息,所以我们在GamaStats
中添加一个socre
属性
class GameStats:...def reset_stats(self):# 初始化在游戏运行期间可能变化的统计信息self.planes_left = self.setting.plane_limit# 统计得分self.score = 0
显示得分
为了在屏幕上显示得分,我们先创建一个类Scoreboard
。这个类不光显示得分,最高分、飞船数量以及等级都会在此展示
新建一个scoreboard.py
来存储这个新类
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/5
"""
import pygame.fontclass Scoreboard:"""显示得分信息的类"""def __init__(self, setting, screen, stats):"""初始化得分涉及到的属性"""self.screen = screenself.screen_rect = screen.get_rect()self.stats = stats# 显示得分的字体设置# 设置文本的颜色self.text_color = (20, 20, 20)# 设置字体的大小self.font = pygame.font.SysFont("SimHei", 40) # 字体为黑体大小为40像素# 初始化得分图像self.prep_score()def prep_score(self):"""将得分转换为图像"""score_str = str(self.stats.score)self.score_image = self.font.render(score_str, True, self.text_color)# 将得分放到屏幕的右上角self.score_rect = self.score_image.get_rect()self.score_rect.right = self.screen_rect.right - 20 # 与右边差20 像素self.score_rect.top = 20 # 与顶部差20像素def show_score(self):"""在屏幕上显示得分"""self.screen.blit(self.score_image, self.score_rect)
现在创建类的实例化
...
from scoreboard import Scoreboarddef run_game():...# 创建一个用于存储游戏统计信息的实例stats = GameStats(setting)# 创建记分的实例score_board = Scoreboard(setting, screen, stats)...while True:...fg.update_screen(screen, setting.bg_img, plane, bullets, spaceships, stats, play_button, score_board)run_game()
创建
Scoreboard
类的实例化,并在update_screen()
传入score_board
让其能够在屏幕显示得分
在update_screen
调用show_score()
使其在屏幕上绘制出来
def update_screen(screen, bg_img, plane, bullets, spaceships, stats, play_button, score_board):...# 显示得分score_board.show_score()...# 将完整显示Surface更新到屏幕pygame.display.flip()
效果图
更新得分
首先设置击落一个飞船给多少分,在settings.py
中的initialize_dynamic_settings()
方法添加一个属性
# 击落一个飞船的得分
self.spaceship_points = 10
在子弹与飞船碰撞之后更新得分,这里修改update_bullets()
函数
def update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board):# 将编组中的每个子弹调用bullet.update()bullets.update()# 删除已经消失的子弹for bullet in bullets.copy():if bullet.rect.bottom <= 0:bullets.remove(bullet)collisions = pygame.sprite.groupcollide(bullets, spaceships, True, True) # 两个实参true则是为了将其删除"""这个字典中的键是打中小飞船的子弹,值则为一个列表,其中包含了所有被打中的值"""if collisions: # 当发生碰撞,会有有返回值,才会为Truefor spaceship in collisions.values():"""遍历这个字典,确保每个外星人的点数都计入得分"""stats.score += setting.spaceship_points * len(spaceship) # 发生碰撞加分"""因为子弹的值是一个列表,所有这里每次计算一下列表的长度来进行加分"""score_board.prep_score() # 并绘制在屏幕上if len(spaceships) == 0:bullets.empty() # 删除编组中的所有精灵(子弹)# 加快游戏节奏setting.increase_speed()create_fleet(setting, screen, spaceships, plane) # 重新调用生成飞船
在主循环中为其增加实参
fg.update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board)
提高游戏分数
现在我们的基本打怪给分已经完成了,但是随着游戏难度的增长,打怪的得分并不会随着增长,这里类似于难度的设置也增加几行代码让其完成这个功能
在settings.py
中的__init__
添加l两行
...
class Settings:"""存储飞机大战的所有设置"""def __init__(self):...# 提高分数的速度self.score_scale = 1.5self.initialize_dynamic_settings()def initialize_dynamic_settings(self):...def increase_speed(self):...# 提高飞船的分数self.spaceship_points = int(self.spaceship_points * self.score_scale)
最高得分
为了超越自己,肯定会有一个最高得分系统,在GameStats
类中的__init__
方法中添加一行
# 最高得分
self.high_score = 0
现在来修改一下Scoreboard
类以便显示最高分
...
class Scoreboard:"""显示得分信息的类"""def __init__(self, setting, screen, stats):...self.font = pygame.font.SysFont("SimHei", 40) # 字体为黑体大小为40像素# 初始化得分图像self.prep_score()# 初始化最高分图像self.prep_high_score()def prep_score(self):...def prep_high_score(self):"""将得分转换为图像"""high_score_str = self.stats.high_scoreself.high_score_image = self.font.render(high_score_str, True, self.text_color)# 将得分放到屏幕的顶部中间self.score_rect = self.score_image.get_rect()self.score_rect.centerx = self.screen_rect.centerxself.score_rect.top = 10def show_score(self):"""在屏幕上显示得分"""self.screen.blit(self.score_image, self.score_rect)# 显示最高分self.screen.blit(self.high_score_image, self.high_score_rect)
在game_func.py
中添加一个函数,用于计算当前的分数是否大于最高分,并在update_bullets
中进行调用
...
def update_bullets(bullets, spaceships, setting, screen, plane, stats, score_board):...if collisions: # 当发生碰撞,会有有返回值,才会为Truefor spaceship in collisions.values():"""遍历这个字典,确保每个外星人的点数都计入得分"""stats.score += setting.spaceship_points * len(spaceship) # 发生碰撞加分"""因为子弹的值是一个列表,所有这里每次计算一下列表的长度来进行加分"""score_board.prep_score() # 并绘制在屏幕上check_high_score(stats, score_board)if ......
def check_high_score(stats, score_board):"""用于检测是否产生最高分"""if stats.score > stats.high_score:stats.high_score = stats.score # 如果的得分大于最高分,则值赋给最高分score_board.prep_high_score()
甜甜有话说
终于把这个小项目给完成了,这里用的pygame
模块一边学一边这就很难哎,反正最后也弄出来
现在好好休息几天,开始学习爬虫
座右铭:不要在该奋斗的年纪选择安逸!!
本文参考美国作家埃里克·马瑟斯《Python编程从入门到精通》
往期精选(????猛戳可查看)
在Django中快速使用Bootstrap模版
Python分析周杰伦的Mojito
还想了解更多干货?
关注早起Python,查看更多精彩文章↓
觉得这篇文章还不错?点亮「在看」鼓励一下甜甜!
- THE END-
手把手教你使用Pygame制作飞机大战小游戏,4万字超详细讲解!相关推荐
- 体感游戏 | 手势识别玩飞机大战游戏(一) 用pygame实现飞机大战小游戏
Color Space OpenCV与AI深度学习 后面将分四篇文章来介绍实现手势识别控制飞机大战游戏的功能,它们分别是: 使用Pygame实现简易飞机大战小游戏 使用Python+OpenCV实现简 ...
- jq制作飞机大战小游戏
飞机大战小游戏 页面布局: <h1 class="score">0</h1><div class="contain">< ...
- 拾起童年的回忆 - 手把手教你制作飞机大战小游戏
拾起童年的回忆 最记得小学时,每逢放学便会打开电视机,接上红白机/小霸王,插上一张游戏卡带,魂斗罗.超级玛丽.冒险岛.足球小将,拳王.飞机大战.雪人兄弟--,这些游戏到现在还是如数家珍,一张游戏卡可以 ...
- pygame为游戏添加背景_用 Python 制作飞机大战小游戏
这这次用Python中的pygame模块来完成一个飞机大战的小游戏:基本思路是通过方向键来控制飞机的左右移动射击飞船.先来看下最后的效果为了新手也能完成,本文记录了编写的全部流程,也就是每次修改的代码 ...
- 自己制作飞机大战小游戏 canvas应用实例
图片只能打包上传 百度云链接:https://pan.baidu.com/s/1bxHZYtsZxTzPFis4Fq4AvA 密码:xshc <!DOCTYPE html> <htm ...
- Pygame实现飞机大战小游戏,不用精灵版
之前写过C语言版的,这次用Python写了个,不过原理还是用的C里GDI那套循环,把Python的类当C里的结构体用,因为Pygame的精灵研究了下还是没弄明白 不过写完后,和找的几篇别人用精灵写的参 ...
- HTML5用canvas制作飞机大战小游戏
css样式: <!DOCTYPE html> <html lang="en"><head><meta charset="UTF- ...
- 使用小程序制作一个飞机大战小游戏
此文主要基于微信小程序制作一个飞机大战小游戏,上手即用,操作简单. 一.创建小程序 二.页面实现 三.代码块 一.创建小程序 访问微信公众平台,点击账号注册. 选择小程序,并在表单填写所需的各项信息进 ...
- Qt学习总结——飞机大战小游戏制作
Qt学习总结--飞机大战小游戏制作 1. 需求分析 这篇文章写于2020年暑假,完成学校实训项目之后,对自己的项目实践做了一个总结,回顾整个项目的制作过程,同时也复习一下Qt的相关知识,总结项目制作过 ...
最新文章
- JDeveloper开发环境设置
- c#导出包含图片的word文档
- 海思移植opencv+车辆检测
- QQ空间面试题放送,速度教科书式扑街补救offer!
- ansys 常用结构单元类型
- php中unset函数是在哪一章_PHP引用(amp;)使用详解
- BZOJ3626 神思路的树链剖分+线段树维护
- 自动驾驶3-2 安全保证和测试的行业方法 Industry Methods for Safety Assurance and Testing
- mysql+enable+sql+log_MySQL -- redolog + binlog
- 大数据剖析 | 薪资没那么高,延毕率超60%,现代人读博都图什么?
- QQ会员等级升级加速正式开始
- iVMS-4200 Vs区别_45466足球推荐分析 法甲 21:00 安格斯 VS 梅斯
- ORDER BY用法,避坑
- Office Web apps可以利用Excel Web JavaScript编程
- 微信小程序——个人版微信小程序与企业版微信小程序区别
- Prism4学习笔记(七):State-Based Navigation QuickStart
- CSS学习笔记(九)display: inline-block,CSS 布局- 水平和垂直对齐
- 诸侯安置 简单的递推
- PHP面试技巧——如何应对面试官的“激将法”语言?
- 2019中信软开社招
热门文章
- matlab 信号相位角,FFT信号处理后幅度、相角的问题
- SAP FICO 如何看一个总账科目的修改记录?
- 新氧科技与京东健康签订合作协议 迎来医美服务高品质上飞跃
- Access转出到Mysql,咱自己动手丰衣足食.. -- 纯JS代码
- 赵运泓: 12:3下周黄金行情走势分析
- 智慧宿舍系统--宿舍安全管理解决方案
- 信创铺路,数据库格局颠覆的时刻到了
- 英语时态和完成时详解
- 【web安全】从2022中科大hackgame web中学习pdflatex RCE和python反序列化
- Python金融大数据分析-BSM、Term Struc、Ho-Lee 与Vasicek模型路径仿真