由于明天回学校了,因此项目也暂时不会动弹什么了,值得庆幸的是,总算在走以前初步写完了整个项目,虽然bug遗留下很多……嘛嘛,反正呢,这篇也就是相似于第一部大结局般的存在。 node
这篇讨论的内容是save和load功能。事先说明,这部分代码赶工出来的,而且我对这部份内容没有一点积累,代码质量很糟糕,而且因为没有大量的测试,也很差说会在哪里出现问题。事实上,就我测试的结果来看,有可能会出现index无法找到的状况,缘由尚不明朗。站在如今的角度来看,使用状态机或许能解决很多逻辑混乱的问题,遗憾的是我这里没时间了…… python
言归正传,我这里谈谈本身的思路,请随意指出个人疏漏、不足和错误,若是有更好的解决思路和方法我会至关感激的。 app
通常来讲,不论是save仍是load,都是须要切换界面的。老实说,切换界面这个行为也就是把新界面的图片覆盖在屏幕上就好,真不太费事儿。可是要发生切换这个动做,就须要按下按钮不是么,因此首先从按钮开始。 函数
这里给出我这里的效果图……如你所见,右边那两个丑丑的按钮就是了,固然为了完成这个目标,位置要反复调试好,且很少说了,代码很容易。其实也就是NodeItem每次渲染的时候多渲染两个按钮就行了…… 测试
def __initSettingButtons(self): dic_settingButtons = {} dic_settingButtons['save'] = Button((\ (SAVEBUTTONPOSX,SAVEBUTTONPOSY)),\ SETTINGBUTTONSIZE,'SLButton.png',os.path.join('FONT','hksn.ttf'),\ 'save',None,15) dic_settingButtons['load'] = Button((\ (LOADBUTTONPOSX,LOADBUTTONPOSY)),\ SETTINGBUTTONSIZE,'SLButton.png',os.path.join('FONT','hksn.ttf'),\ 'load',None,15) return dic_settingButtons
上面是关键代码,来龙去脉我都舍掉了。反正也就是每帧多绘制俩按钮。 this
而后是切换,我考虑了一下是否须要把触发函数写成一个抽象函数放在按钮超类中,子类化的时候再去完成这个触发函数。可是最后我懒得这样作,由于感受挺麻烦的……好吧,实际上我没有去考证过这种写法是否会更好…… spa
切换的时候,新的surface覆盖了当前的surface,就成了所谓的存/载档界面。我这里大体是这样的(渣设计): 设计
固然我这个是最终完成的效果。一点一点说。 调试
除了最下面的背景以外,其他的都是按钮,事实上,先前那个按钮类工做得很好。中间三个方形物体就是存/载档位置,本来都是如同第三个般的白色,可是因为有记录,因此加载了图片,这个技巧等会讨论。最左下角是退出按钮,点击会回到主界面。值得一提的是,这货是我本身画的…… code
先讨论如何进行主界面和存取界面的切换。
这里实质上发生了一个控制流的改变----貌似一会儿高端了?简单说就是,原本鼠标点击应该是单纯进行帧切换的,可是点击了存取按钮以后,鼠标点击的效果也就改变了,帧切换也再也不发生。
顺便说一句,可不是单击按钮以后覆盖图片就无论了这么简单哦。由于事实上在进行着1秒8帧的刷新,因此,单纯的覆盖的话,很快又会被从新覆盖掉。写到这里,我忽然想到,貌似对galgame使用帧刷新是个败笔?毕竟不须要像通常游戏那样有运动的事物不是么,由于增长了刷新反倒很复杂?……
额,之后再研究这个问题,如今先无视掉。
回到上面,我不知道大家会怎么处理这个问题(刚切换的新界面被从新覆盖),我这里用了一个全局变量来freeze了刷新这个行为,这样。
而后是save行为和load行为,虽然我很想分开讨论,可是毕竟这两家伙的行为有太多类似之处,因此仍是写在一个函数就好。可是还不止是这样,因为游戏是事件驱动的,因此还有一个通常的行为……这个很难详细解释,可是试验的结果就是这样。为了区分这三种状态,函数须要有一个特别的值:flag去区分到底当前是哪一种状态。
如同这样:
if dict_settingButtons['save'].is_over(click_point):
freeze = True
button_settings(surface,click_point,frame,'save')
elif dict_settingButtons['load'].is_over(click_point):
freeze = True
button_settings(surface,click_point,frame,'load')
elif freeze:
button_settings(surface,click_point,frame)
def button_settings(surface,pos,Frame,flag=''):
上面的就是函数的定义和使用,没必要深究每一行代码。
上面也说了,save和load存在某些通用的行为,具体来讲,就是绘制背景和相关按钮,这部分就能够做为通常行为,save和load各自的行为使用if判断标识符就行了。
如今说一下save的特有行为。
save状态下,点击三个按钮中的任意一个,都会在按钮上覆盖当前帧的缩略图,并在本地保存当前帧的信息,好比图片,音乐,index之类的。
这里须要讨论一下,事实上,不光须要保存帧信息,还须要保存两个特殊的数据:数据被绑定在哪一个按钮上,和当前的缩略图。
我借用了parser的一些方法,把被绑定按钮的index看做index来解析,缩略图当作background来解析,帧信息放在一个元组中,用pickle进行序列化。这样。而后save文件大体就是这个样子:
0
[background='0.png']
(I19
S'M2.png'
p0
S'BGM/2-10.ogg'
p1
(dp2
tp3
.
具体的也请参看源代码。
load实质上就是一个解包的过程,虽然这里说得很轻巧,可是如何引出数据是个很麻烦的问题,一把辛酸泪。最后仍是用全局变量来弄了一遍,代码就很难看了。
总而言之,具体的思路就是这样,有兴趣参看源代码就好。须要注意的是,逻辑事实上比较乱……却是,本身从新写反倒比较好理解?
## 这个函数负责解析出save文件,注意,save文件位置和名字都不能更改 ## 大体来讲,就是借用了parser类 ## 使用parser的split方法,还有parser方法 ## 最后返回两个字典。 ## 两个字典的key都是按钮的index,三个按钮中,第一个index是0, ## 以此类推 ## 第一个字典的value是载入好转化好的缩略图 ## 第二个字典的value是一个元组,第一项是图片名称 ## 第二项是指定的帧的index,供读取跳转 def savefile_parser(): fullname = os.path.join('SAVE','save.dat') saveInfo = save_parser.split(fullname) saveInfos = [] ##saveInfos = [save_parser.parser(i).getSaveData() for i in saveInfo] try: for i in saveInfo: save_parser.parser(i) saveInfos.append(save_parser.getSaveData(i)) ## i->like this:(0,'xxx.jpg,25) ## saveSurfaces composed like this: ## [(0,),(1,...)] saveSurfaces = [(i[0],pygame.transform.scale(pygame.image.load(os.path.join('SAVE',i[1])).convert(),(200,200))) for i in saveInfos] dict_item = {} dict_name = {} for i in saveSurfaces: ## dict_item's value is an ## instance of Surface class dict_item[i[0]] = i[1] for i in saveInfos: ## dict_name is a dict ## like this {0:('xxx.jpg',25,xxxxxxxx)} dict_name[i[0]] = (i[1],i[2]) except AttributeError: dict_item = {} dict_name = {} return (dict_item,dict_name) ## 负责把应当储存的数据写到磁盘中 def format_save_data(saveDict,savefile): return '\n\n'.join([str(i[0])+'\n'+'''[background='''+"'"+ \ str(i[1][0])+"']" + '\n' \ + str(i[1][1]) for i in saveDict.items()]) ## 这个是主要的函数,负责界面渲染什么的 ## save和load的处理也在里面 def button_settings(surface,pos,Frame,flag=''): global freeze global g_flag global now_frame global load_data ##save_image_surface = pygame.image.load(save_image).convert() ##save_image_surface = pygame.transform.scale(save_image_surface,(200,200)) ## need updating? ## 判断是处于哪一种状态 if flag: now_frame = surface.copy() now_frame = pygame.transform.scale(now_frame,(200,200)) g_flag = flag ## init the setting screen ## 绘制背景 fullname = os.path.join('SYSTEM','SL.jpg') SLImage = pygame.image.load(fullname).convert() SLImage = pygame.transform.scale(SLImage,SIZE) surface.blit(SLImage,(0,0)) ## 绘制exit按钮 button_exit = NodeItems.Button(EXIT_POS,EXIT_SIZE,os.path.join('SYSTEM','exit.png'),os.path.join('FONT','hksn.ttf')) button_exit.render(surface) ## 绘制白色的储存按钮 white_buttons_pos = [150,400,650] white_buttons = [NodeItems.Button((i,300),(200,200),os.path.join('SYSTEM','white.png'),os.path.join('FONT','hksn.ttf')) for i in white_buttons_pos] for i in white_buttons: i.render(surface) ## get save data dict_items , dict_names = savefile_parser() ## 把缩略图放到应该的按钮上面 if dict_items and dict_names: for i in dict_items: surface.blit(dict_items[i],(50+250*i,200)) ##surface.blit(save_image_surface,(50,200)) ## save behavior ## save主要是对应该保存的信息进行获取和保存 if g_flag == 'save': for (count,i) in enumerate(white_buttons): if i.is_over(pos): ## Let screenshoting image be displayed screen.blit(now_frame,(50+250*count,200)) ## Save it pygame.image.save(now_frame,os.path.join('SAVE',str(count)+'.png')) ## Write it to dict pickle_elements = (Frame.getNodeIndex(),Frame.getBackground(),Frame.getBGM(),Frame.getPortraits()) pickled_data = pickle.dumps(pickle_elements) ## storage pickled data dict_names[count] = (str(count)+'.png',pickled_data) ## Update local datafile savefile = open(os.path.join('SAVE','save.dat'),'w') ## Write data savefile.write(format_save_data(dict_names,savefile)) savefile.close() break ## load去获取对应的数据,用一个叫load_data的全局变量传出去 ## 并切换回主界面 elif g_flag == 'load': for (count,i) in enumerate(white_buttons): if i.is_over(pos): next_data = dict_names[count][1] ## To be sure it is None load_data = None load_data = next_data freeze = False break ## To exit if button_exit.is_over(pos): freeze = False while True: for event in pygame.event.get(): if event.type == QUIT: exit() if event.type == MOUSEBUTTONDOWN: ## check where the click point on click_point = event.dict['pos'] surface = nodeItem.getScreen() frame = nodeItem ## ugly codes...who can tell me ## how to write these codes well? ## What I wrote were fucking complex ## 点击save时,冻结刷新操做,flag置为save if dict_settingButtons['save'].is_over(click_point): freeze = True button_settings(surface,click_point,frame,'save') ## 同上 elif dict_settingButtons['load'].is_over(click_point): freeze = True button_settings(surface,click_point,frame,'load') ## 正常状态 elif freeze: button_settings(surface,click_point,frame) if load_data: continue else: if nodeItem.getChoiceButtons(): dict_buttons = nodeItem.getChoiceButtons() ##Through over which button been clicked ##Surily if the point out of any button's ##area,we make it freeze :-) for key in dict_buttons.keys(): ## each dict_button is a tuple, ## like this (index,button) ## index is the next Node index ## to change the control stream. ## button is a instance of Button index = int(dict_buttons[key][0]) button = dict_buttons[key][1] if button.is_over(click_point): nodeItem.setNextIndex(index) break else: ## pick = pickle.Pickler(f) ## pick.dump((nodeItem.getBGM(),nodeItem.getBackground(),nodeItem.getPortraits())) ## 对load_data反序列化,获取值,使用parser类把值传入nodeitem ## 再更新 if load_data: pickle_data = pickle.loads(load_data) load_data = None load_index,load_bg,load_bgm,load_portr = pickle_data parser.setNodeIndex(load_index-1) parser.setBackground(os.path.basename(load_bg)) parser.setBGM(os.path.basename(load_bgm)) parser.setPortrait(load_portr) nodeItem.update(parser) nodeItem.setNextIndex() ##The following codes update screen if not freeze: NextIndex = nodeItem.getNextIndex() ##get key of one Node try: Node = dirNode[NextIndex] ##get a Node which is a string parser.parser(Node) nodeItem.update(parser) except KeyError: print 'Cannot search the index:',NextIndex Node = dirNode[ERRORINDEX] ## freeze the inscreasing of index parser.parser(Node) nodeItem.update(parser) else: pass
基本就是这样,没什么好说的了,拖拖拉拉的,把最后一点东西也弄完了。回头来看,这不算什么大项目,可是基本是我一我的独立完成的最大的一个了,写到这里仍是很有那么一点唏嘘的。站在如今的角度,发现其实不少均可以用更好的方法去解决的(或许)。不过完成就好不是么?总结的话:这玩意挺好玩的~~~
最后,恩,话说galgame最大的问题果真仍是剧本,画师和音乐来着……