前言
本篇文章接着之前的内容,继续对游戏功能进行优化,主要是优化游戏状态以及对应的处理。
状态
一个游戏包含多种状态,这个状态是一个可以很复杂也可以很简单的内容。条件所限,我们这个游戏的状态就比较简单:
- 未开始
- 游戏中
- 暂停
- 结束
我们通过一个字段进行控制,并且将这个字段放置到setting
模块中。
class Setting:
"""系统设置类"""
def __init__(self):
-- snip --
# 游戏状态:0未开始,1游戏中,2暂停,3结束
self.game_status = 0
处理状态
根据不同的状态值,我们先对main
模块进行简单的处理。
import pygame
from pygame.sprite import Group
import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
from alien_invasion.ship import Ship
def run_game():
"""启动游戏"""
# 初始化pygame
pygame.init()
# 定义一个系统设置对象
setting = Setting()
# 新建窗口
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
# 窗口命名
pygame.display.set_caption(setting.caption)
# 定义一个飞船对象
ship = Ship(setting, screen)
# 创建子弹编组
bullets = Group()
# 创建外星人编组
aliens = Group()
# 创建编组内的外星人
gf.create_fleet(setting, screen, aliens)
while True:
if setting.game_status == 0:
pass
elif setting.game_status == 1:
# 处理监听事件
gf.check_event(ship, setting, screen, bullets)
# 移动飞船
ship.move()
# 更新子弹位置
gf.update_bullets(bullets, aliens, setting, screen)
# 更新外星人
gf.update_aliens(aliens, setting, ship)
# 刷新屏幕
gf.update_screen(setting, screen, ship, bullets, aliens)
elif setting.game_status == 2:
pass
elif setting.game_status == 3:
pass
if __name__ == '__main__':
run_game()
状态对应的业务
现在,我们要对状态思考响应的游戏业务。
这里直接描述一下我个人简单预定的功能:
- 启动游戏,弹出窗口,显示欢迎来到游戏,按回车键开始游戏。其他所有按键不生效。
- 在游戏中,按下回车暂停游戏;暂停游戏时按下回车继续游戏
- 开始游戏后,任意时刻按下
Esc
键退出游戏,回到黑色窗口,显示游戏介绍,暂停一秒关闭游戏。
代码实现
我们在第二步已经简单的对状态值进行了处理,接下来就根据上面的业务在对应的状态处理部分一一填充。
首先我们处理开始游戏的状态。
开始游戏
首先我们需要在setting.game_status == 0
的条件下新建一个not_start()
函数,传入参数setting
和screen
。这两个参数一个是要回写状态值到配置模块中,一个是渲染游戏窗口需要的一些属性。
修改main
模块中循环处理的部分如下:
-- snip --
while True:
# 没啥用,方便编写代码的时候看看代码执行情况
print(setting.game_status)
if setting.game_status == 0:
# 处理没启动的业务逻辑
gf.not_start(setting, screen)
-- snip --
main
模块处理好以后,我们在gf
模块进行实际的业务代码编写。
在函数中,我们需要完成两个功能,一个是渲染文字,一个是监听按键事件,是否有开始游戏,那么代码编写如下:
def not_start(setting, screen):
# 先渲染文字
handle_text(screen)
print("not_start")
# 处理监听事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if setting.game_status == 0:
setting.game_status = 1
print("开始游戏")
这里监听事件的部分没有单独拉函数出来,主要是不影响阅读,不过因为渲染文字的函数有点行数并且也算一个比较独立的功能,所以又单独创建了一个函数,后面的结束也是如此。
另外虽然我们这个时候只监听是否按下回车键的这个事件,但是点击退出窗口的监听事件,在任何地方都不能忘了,不然到时候可能你的窗口关不了,只能去通过停止程序关闭。
监听事件的代码比较简单的,就是在确认是按下了回车,状态是0的时候,回写状态值即可。
我们看看文字渲染的代码。
文字渲染我们在项目开始之前介绍pygame
就提前讲过,相比大家都不陌生,看代码注释即可,代码如下:
def handle_text(screen):
# 字体颜色
pink = pygame.Color('#FFFFFF')
# 使用Arial字体创建一个字体对象,32号,加粗,倾斜
font = arial_bold = pygame.font.SysFont('Arial', 32, bold=True, italic=True)
# 渲染文本
text_surface1 = font.render("Welcome to the Game Of Alien Invasion", True, pink)
# 获取文件的矩形区域
text_rect1 = text_surface1.get_rect()
# 设置文本Y坐标320,居中位置
text_rect1.midtop = (screen.get_width() // 2, 320)
# 渲染文本
text_surface2 = font.render("Press The Enter Key To Start The Game", True, pink)
# 获取文件的矩形区域
text_rect2 = text_surface2.get_rect()
# 设置文本Y坐标420,居中位置
text_rect2.midtop = (screen.get_width() // 2, 420)
# 绘制文本
screen.blit(text_surface1, text_rect1)
screen.blit(text_surface2, text_rect2)
# 更新显示
pygame.display.flip()
运行main模块,按下回车。
可以看到文字正常渲染,并且输出栏一直在出输出当前状态。
如果你觉得画面单调,可以自己加点东西渲染,pygame
渲染图片这些相比也不陌生了。
按下回车,游戏正常开始!
可以看到,已经进入到游戏页面,并且输出栏的状态值也变成了1。
游戏中
游戏中的处理比较简单,因为我们之前已经完成了游戏中的代码编写,所以我们只需要在游戏中的监听事件中加上回车键的监听事件暂停游戏。
ps: 这里其实也可以结束游戏,代码我先写上,但是不操作。业务代码也不复杂,就是多加一个Esc
键的监听事件。
gf
模块中的check_keydown_events()
函数代码修改如下:
def check_keydown_events(event, ship, setting, screen, bullets):
"""响应按键"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_UP:
ship.moving_top = True
elif event.key == pygame.K_DOWN:
ship.moving_bottom = True
elif event.key == pygame.K_SPACE:
fire(bullets, screen, setting, ship)
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RETURN:
if setting.game_status == 1:
setting.game_status = 2
print("暂停游戏")
elif event.key == pygame.K_ESCAPE:
setting.game_status = 3
print("游戏结束")
启动main
模块,开始游戏后再按回车,效果如下:
可以看到,我们的飞船,子弹,外星人全部暂停住了,输出栏一直打印状态也变成了2(暂停)。
屏幕中显示了一些文字,这些其实是暂停的业务。
我们马上进行暂停的业务代码编写。
暂停游戏
参考开始游戏的实现,我们在暂停的分支下调用一个函数stop()
。
main
模块循环部分代码如下:
-- snip --
while True:
# 没啥用,方便编写代码的时候看看代码执行情况
print(setting.game_status)
if setting.game_status == 0:
# 处理没启动的业务逻辑
gf.not_start(setting, screen)
elif setting.game_status == 1:
# 处理监听事件
gf.check_event(ship, setting, screen, bullets)
# 移动飞船
ship.move()
# 更新子弹位置
gf.update_bullets(bullets, aliens, setting, screen)
# 更新外星人
gf.update_aliens(aliens, setting, ship)
# 刷新屏幕
gf.update_screen(setting, screen, ship, bullets, aliens)
elif setting.game_status == 2:
gf.stop(setting, screen)
-- snip --
同样,我们去到gf
模块创建stop()
函数并实现相应的功能,代码如下:
-- snip --
def stop(setting, screen):
handle_stop_text(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if setting.game_status == 2:
setting.game_status = 1
print("继续游戏")
elif event.key == pygame.K_ESCAPE:
setting.game_status == 3:
print("结束游戏")
-- snip --
代码依旧分两部分实现,渲染文字和监听事件。在暂停的监听事件中我们和开始一样,监听关闭窗口,回车和结束,并在回车事件中回写状态值,让游戏继续运行,在Esc
时间中结束游戏。
文字渲染部分代码如下:
def handle_stop_text(screen):
# 字体颜色
pink = pygame.Color('#000000')
# 使用Arial字体创建一个字体对象,32号,加粗,倾斜
font = arial_bold = pygame.font.SysFont('Arial', 32, bold=True, italic=True)
# 渲染文本
text_surface1 = font.render("Game Stop", True, pink)
# 获取文件的矩形区域
text_rect1 = text_surface1.get_rect()
# 设置文本Y坐标50,居中位置
text_rect1.midtop = (screen.get_width() // 2, 320)
# 渲染文本
text_surface2 = font.render("Press The Enter Key To restore The Game", True, pink)
# 获取文件的矩形区域
text_rect2 = text_surface2.get_rect()
# 设置文本Y坐标50,居中位置
text_rect2.midtop = (screen.get_width() // 2, 420)
# 绘制文本
screen.blit(text_surface1, text_rect1)
screen.blit(text_surface2, text_rect2)
# 更新显示
pygame.display.flip()
基本上就是复制开始的文字渲染,改了下文字内容。
效果的话这里就不展示了,就是暂停开始,开始暂停这一套。
结束游戏
在游戏中和暂停的时候,我们都可以通过按键Esc
实现游戏结束。
在main
模块的改动依旧是调用一个结束函数,但是结束函数不需要回写状态值,所以我们只需要一个screen
参数。
代码如下:
while True:
# 没啥用,方便编写代码的时候看看代码执行情况
print(setting.game_status)
if setting.game_status == 0:
# 处理没启动的业务逻辑
gf.not_start(setting, screen)
elif setting.game_status == 1:
# 处理监听事件
gf.check_event(ship, setting, screen, bullets)
# 移动飞船
ship.move()
# 更新子弹位置
gf.update_bullets(bullets, aliens, setting, screen)
# 更新外星人
gf.update_aliens(aliens, setting, ship)
# 刷新屏幕
gf.update_screen(setting, screen, ship, bullets, aliens)
elif setting.game_status == 2:
gf.stop(setting, screen)
elif setting.game_status == 3:
gf.end(screen)
gf
模块改动如下:
def end(screen):
# 重新填充屏幕
screen.fill((0, 0, 0))
# 渲染文字
handle_end_text(screen)
# 休眠一秒
sleep(1)
# 退出
pygame.quit()
sys.exit()
和之前比对有一点不同,多了几部,其实没有特别含义,就是看起来流畅好看一点。
文字渲染代码如下:
def handle_end_text(screen):
# 字体颜色
pink = pygame.Color('#FFFFFF')
# 使用Arial字体创建一个字体对象,32号,加粗,倾斜
font = arial_bold = pygame.font.SysFont('Arial', 48, bold=True, italic=True)
# 渲染文本
text_surface1 = font.render("Game Over", True, pink)
# 获取文件的矩形区域
text_rect1 = text_surface1.get_rect()
# 设置文本Y坐标50,居中位置
text_rect1.midtop = (screen.get_width() // 2, screen.get_height() // 2)
# 绘制文本
screen.blit(text_surface1, text_rect1)
# 更新显示
pygame.display.flip()
运行效果如下:
结尾
今天的内容就是这些,设定状态值,并且在对应的状态中处理不同的逻辑,渲染不同的画面。
当然我们是学习,所以内容都比较简单,有兴趣的话可以深入一下加点自己的元素进去。
后面我们继续优化游戏功能,加油!!!