前言
打脸了,忘记了这部分,还以为自己的外星人项目已经结束了。
这部分主要是讲如何使用json文件保存我们的等级、得分、最高分等等的游戏数据。
之前有一个写如何保存最高分的博客,这次我们进行了一些改进。
逻辑:
- 用户点击’q’或者关闭窗口时,我们将此时的stats数据(主要是得分、等级、剩余飞船数等)以及设置中的部分数据(飞船、外星人的移动速度,这些和等级有关)写入json文件。
- 在点击play按钮后我们采用另外一个选项框来让用户进行选择,可以读取json文件的数据继续游戏,也可以重新开始。
json文件格式
整个文件是用花括号包起来的,和py的字典差不多,不过key只能用双引号包起来(py中字典的key单双引号都行)
运行生成的json文件:
当然还有其他的格式,比如这个vscode生成的json文件:
字典中的value有列表,列表的元素还是字典……
注意最里层的也是双引号
写入json
首先找到需要写入的位置,"q"按钮和关闭窗口,都在我们的事件函数部分。
写入的话,我们直接采用w方法(各种方法的功能、区别讲解看上面的博客),因为之前的文件已经不需要再使用了。
首先我们需要导入json包:
import json
写入的方法为dump(内容,文件变量)
with open('./alien/memory.json',"w") as m:memory = {
"high_score":stats.high_score,"ships_left":stats.ships_left,"score":stats.score,"level":stats.level,"big_bullet":stats.big_bullet,"alien_speed_factor":ai_settings.alien_speed_factor,"ship_speed_factor":ai_settings.ship_speed_factor,"bullet_speed_factor":ai_settings.bullet_speed_factor,"alien_points":ai_settings.alien_points}json.dump(memory,m)
可能看到了那个奇奇怪怪的文件路径,这个我也在上面的博客中提到过,因为我们希望能在模块同一层中显示,所以才选择了这样的路径。
在两处的sys.exit之前添加:
开始逻辑
状态
在前言中,我简单讲了一下我想要的效果,点击play,进入下一个选项,然后就可以开始游戏。
也就是之前的双阶段游戏(点击play和没点击)被我分成了三部分,第一部分点击play按钮,第二部分为是否(我叫yon模块),第三部分是游戏开始。
所以我将game_stats中的game_activate改为了0。
没错,剩下的两个状态就是1和2。
这部分的想法主要是因为之前写过数逻的状态机,所以采用了类似的方法
。
新按钮模块
因为之前的按钮模块已经成了一个体系,不方便继续修改,我就自己又添加了一个模块,用来显示选项,长这样:(好叭已经听到有人骂它丑了)
模块叫做yon.py(yes or not)
import pygame.font
class YON():def __init__(self,screen):self.screen = screenself.screen_rect = screen.get_rect()self.text_color = (255,255,255)self.font = pygame.font.SysFont(None,20)# 背景框&文字self.bg_width,self.bg_height = 600, 70self.bg_color = (0,127,0)self.bg_msg = "do you want to follow?"self.bg_rect = pygame.Rect(0,0,self.bg_width,self.bg_height)self.bg_rect.center = self.screen_rect.center# yes按钮self.l_width,self.l_height = 100,35self.l_color = (242,13,39)self.l_msg = "Y"self.l_rect = pygame.Rect(0,0,self.l_width,self.l_height)self.l_rect.bottom = self.bg_rect.bottomself.l_rect.left = self.bg_rect.left+200# no按钮self.r_width,self.r_height = 100,35self.r_color = (82,209,141)self.r_msg = "N"self.r_rect = pygame.Rect(0,0,self.r_width,self.r_height)self.r_rect.bottom = self.bg_rect.bottomself.r_rect.right = self.bg_rect.right-200self.prep_msg()def prep_msg(self):self.bg_msg_image = self.font.render(self.bg_msg,True,self.text_color,self.bg_color)self.bg_msg_image_rect = self.bg_msg_image.get_rect()self.bg_msg_image_rect.top = self.bg_rect.top+10self.bg_msg_image_rect.midtop = self.bg_rect.midtopself.l_msg_image = self.font.render(self.l_msg,True,self.text_color,self.l_color)self.l_msg_image_rect = self.l_msg_image.get_rect()self.l_msg_image_rect.center = self.l_rect.centerself.r_msg_image = self.font.render(self.r_msg,True,self.text_color,self.r_color)self.r_msg_image_rect = self.r_msg_image.get_rect()self.r_msg_image_rect.center = self.r_rect.centerdef draw(self):self.screen.fill(self.bg_color,self.bg_rect)self.screen.blit(self.bg_msg_image,self.bg_msg_image_rect)self.screen.fill(self.l_color,self.l_rect)self.screen.blit(self.l_msg_image,self.l_msg_image_rect)self.screen.fill(self.r_color,self.r_rect)self.screen.blit(self.r_msg_image,self.r_msg_image_rect)
其实,和我们的button模块的逻辑基本上相同,这里如果有兴趣可以考虑自己重构一下
修改主模块
此时,我们的主模块alien_invasion也需要修改一下。
至于导入模块,创建对象就不需要多说了,另外我们的游戏逻辑也需要修改一下。
if stats.game_active == 2:
修改函数事件
需要修改的地方(其实这里可以ctrl+f,修改之前的stats):
- check_events有一个鼠标点击位置是否为play按钮的判断,我们需要改为两组判断
elif event.type == pygame.MOUSEBUTTONDOWN: #按下鼠标mouse_x, mouse_y = pygame.mouse.get_pos()if not stats.game_active:# 0状态,我们需要判断是否点击play按钮check_play_button(play_button, stats, mouse_x, mouse_y)else:# 1状态,判断是否点击进度按钮check_yon(ai_settings, screen, stats, sb, play_button,ship, aliens, aliens_bullet, aliens_more,aliens_shield, bullets, bullets_big, mouse_x,mouse_y,choose)
- 在刷新屏幕处,我们需要修改按钮的显示逻辑:
if not stats.game_active:play_button.draw_button()elif stats.game_active == 1:choose.draw()else:pass
- 添加第二个按钮判断的函数(对于两个选项,调用相同的按钮。
def check_yon(ai_settings, screen, stats, sb, play_button,ship, aliens, aliens_bullet, aliens_more,aliens_shield, bullets, bullets_big, mouse_x,mouse_y,choose):ret_l = choose.l_rect.collidepoint(mouse_x,mouse_y)ret_r = choose.r_rect.collidepoint(mouse_x,mouse_y)if ret_l:follow(ai_settings, screen, stats, ship, aliens, aliens_bullet, aliens_more, aliens_shield, sb)elif ret_r:new_begin(ai_settings, screen, stats, sb, ship, aliens,aliens_bullet, aliens_more, aliens_shield, bullets,bullets_big)stats.game_active = 2
重新开始
这部分,直接照抄之前的模块即可:
# 重新开始
def new_begin(ai_settings, screen, stats, sb, ship, aliens,aliens_bullet, aliens_more, aliens_shield, bullets, bullets_big):#隐藏光标pygame.mouse.set_visible(False)#重置游戏 stats.reset_stats()sb.prep_image()aliens.empty()aliens_more.empty()aliens_bullet.empty()aliens_shield.empty()bullets.empty()bullets_big.empty()create_fleet(ai_settings, screen, stats, ship, aliens, aliens_bullet,aliens_more, aliens_shield)ship.center_ship()ai_settings.initialize_dynamic_settings() #恢复
延续之前的游戏
这部分,就需要我们读取json文件了,并且我们在修改了统计数据后还需要调用函数更新计分板的数据。
读取json文件,我们使用的是json.load(文件变量)
def follow(ai_settings, screen, stats, ship, aliens, aliens_bullet, aliens_more, aliens_shield, sb):#隐藏光标pygame.mouse.set_visible(False)#延续游戏with open("./alien/memory.json")as m:memory = json.load(m)stats.high_score = memory["high_score"]stats.ships_left = memory["ships_left"]stats.score = memory["score"]stats.level = memory["level"]stats.big_bullet = memory["big_bullet"]ai_settings.alien_speed_factor = memory["alien_speed_factor"]ai_settings.ship_speed_factor = memory["ship_speed_factor"]ai_settings.bullet_speed_factor = memory["bullet_speed_factor"]ai_settings.alien_points = memory["alien_points"]sb.prep_image()
这样,我们就实现了整体的功能:
退出游戏保存等级等信息,在新游戏之前用选项框判断是否需要重新开始游戏。