由於明天回學校了,因此項目也暫時不會動彈什麼了,值得慶幸的是,總算在走以前初步寫完了整個項目,雖然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最大的問題果真仍是劇本,畫師和音樂來着……