偷学Python之最后的项目二:飞机大战小游戏(超详细)
古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼
甜甜先说
这次用Python中的pygame
模块来完成一个飞机大战的小游戏;基本思路是通过方向键来控制飞机的左右移动射击飞船。
成品效果
此博客是记录编写的全部流程,每次修改的代码也包括在内
看一下SCDN的统计字数
当然,这里面大部分是代码,而且是一步一步的代码。如果跟着敲的话预计好几个小时,介意的话我开了另一帖,那个贴只有成品代码,传送门
安装pygame
要完成这个项目肯定要安装pygame
第三方库,安装流程如下
-
首先通过命令行工具检测系统是否安装的
pip
工具python -m pip --version
小甜是Windows系统,这里只提供Windows系统的检测方法
-
如果未安装则安装
pip
工具,安装则请跳过这一步python get-pip.py
安装完毕以后退回第一步重新检测
-
安装
pygame
python -m pip install pygame --user
或者通过
pycharm
安装第三个库,流程如下 -
通过
import
关键字导入第三方库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 pygame
class Settings:
"""存储飞机大战的所有设置"""
def __init__(self):
# 屏幕设置
self.screen_width = 1000
self.screen_height = 600
self.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.py
def 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 pygame
class 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 Plane
def run_game():
# 初始化游戏
pygame.init()
# 设置屏幕的分辨率
setting = Settings()
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600px
pygame.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 pygame
def 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 fg
def run_game():
# 初始化游戏
pygame.init()
# 设置屏幕的分辨率
setting = Settings()
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height)) # 大小为1000px乘以600px
pygame.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 pygame
class 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 += 1
def 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:
# 当用户按下键位时标志位为True
plane.mv_right = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
# 当用户松开键位为false
plane.mv_right = False
最后只要在plane_war.py
中调用update
方法就可以完成持续移动的操作
完成左右移动
用同样的方法完成向左移动
改写后的plane.py
文件
"""
-*- coding:uft-8 -*-
author: 小甜
date:2020/6/3
"""
import pygame
class 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
self.mv_left = False
# 定义一个调整小飞机位置的方法
def update(self):
# 根据标志位的调整小飞机的位置
if self.mv_right:
self.rect.centerx += 1
if self.mv_left:
self.rect.centerx -= 1
def 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:
# 当用户按下键位时标志位为True
plane.mv_right = True
elif event.key == pygame.K_LEFT:
plane.mv_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
# 当用户松开键位为false
plane.mv_right = False
elif 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 pygame
class Plane:
def __init__(self, screen, setting):
# 初始化小飞机并设置其初始位置
self.screen = screen
self.setting = setting # 实例化属性
# 加载图像,并获得其矩形区域
self.img_plane = pygame.image.load("./imgs/plane.png")
self.rect = self.img_plane.get_rect() # 得到小飞机的的矩形区域
self.screen_r