貪吃蛇「大做戰」(終結篇)

  經歷幾天的「挖坑」、「填坑」,貪吃蛇程序也要完結了,如今已經能夠作到正常運行貪吃蛇這個遊戲了。在編寫貪吃蛇這個遊戲的過程當中,真的是酸甜苦辣都經歷個遍,不過靠本身編寫而不是徹底「借鑑」別人的代碼,這種收穫感和成就感真的是滿滿的。好了,多的不說,先上代碼。(在從命令行貪吃蛇到pygame貪吃蛇的過程當中參考了別人的思路,這是連接:https://www.jianshu.com/p/f339356fd9eepython

  以前的博客貼出了草地、食物、貪吃蛇三個對象的類模板,這邊我再給出主程序的代碼。(「填坑」過程當中對三個類模板有作修改,後面會把最新的代碼更新  函數


def
terminate(): if g.ccbox("GameOver!!!", choices=('Continue','Exit')): main() else: pygame.quit() exit() #控制遊戲暫停/開始 運行的程序(按一下空格暫停,再按一下從新開始) def pause(): global PAUSE_STATE if PAUSE_STATE == 0: pass elif PAUSE_STATE == 2: PAUSE_STATE = 0 #設置常量 BACK_GROUND_FILL = (255,245,238) #窗體背景填充顏色設置 GRASS_COLOR = (0,250,0) #草地的顏色設置爲亮綠色 FOOD_COLOR = (250,0,0) #食物的顏色設置爲紅色 SNAKE_COLOR = (100,100,0) #貪吃蛇的顏色設置爲黃色 SNAKE_INNER_COLOR = (250,250,0) #貪吃蛇身體內部顏色設置爲亮黃色 LIVING_SPACE = (60,60,520,360) #設置生存空間的位置和大小 SIZE = 20 #定義貪吃蛇和食物在窗體中的大小(體型) SNAKE_LENGTH = 5 #定義貪吃蛇在遊戲初始的長度 SNAKE_SPEED = 10 #定義貪吃蛇的移動速度 PAUSE_STATE = 0 #貪吃蛇遊戲暫停標誌位 #初始化pygame組件 pygame.init() #建立一個窗口對象s screen = pygame.display.set_mode((640,480),0,32) #設置窗口標題 pygame.display.set_caption("貪吃蛇大做戰!") def main(): global PAUSE_STATE DIRECTION = 'RIGHT' #實例化草地對象 grass = GrassMap(GRASS_COLOR,LIVING_SPACE) #實例化食物對象 food = Food(FOOD_COLOR,SIZE,LIVING_SPACE) #實例化貪吃蛇對象 snake = Snake(SNAKE_COLOR,SNAKE_LENGTH,SIZE,LIVING_SPACE) #定義clock對象用以控制FPS(Frame Per Seconds) clock = pygame.time.Clock() #初始化貪吃蛇身體的座標集 snake.generate() #導出此時的貪吃蛇身體座標集 snakebody = snake.snake_body #初始化食物座標 food.generate(snakebody) #開始主循環 while True: #for _ in range(8): pause() #經過巧妙使用while循環來達到暫停/開始 遊戲的功能 while True: for event in pygame.event.get(): #監聽是否退出程序 if event.type == QUIT: pygame.quit() exit() elif event.type == KEYDOWN: #監聽鍵盤 if event.key == K_LEFT and DIRECTION != 'RIGHT': DIRECTION = 'LEFT' elif event.key == K_RIGHT and DIRECTION != 'LEFT': DIRECTION = 'RIGHT' elif event.key == K_UP and DIRECTION != 'DOWN': DIRECTION = 'UP' elif event.key == K_DOWN and DIRECTION != 'UP': DIRECTION = 'DOWN' elif event.key == K_SPACE: PAUSE_STATE += 1 if PAUSE_STATE != 1: break foodbody = food._food_pos #獲取當前食物的座標,用以判斷貪吃蛇移動後是不是吃食物 snake.move(DIRECTION,foodbody) #貪吃蛇開始移動 #print('before:',food._food_pos,snake.snake_body[snake.head]) snakebody = snake.snake_body #獲取移動後貪吃蛇的座標集,用以確保食物不會在貪吃蛇身體中生成 exist = snake.foodstate #獲取食物狀態標誌位,判斷食物是否被貪吃蛇吃了 food.if_exist(exist,snakebody) #根據食物狀態判斷是否隨機生成新的食物 #print('foodbody:',food._food_pos) #print('snakebodyhead:',snake.snake_body[snake.head]) if snake.isdead(): #判斷貪吃蛇是否死亡 terminate() else: pass screen.fill(BACK_GROUND_FILL) #窗體背景填充 grass.generate(screen) #在窗體中生成草地 snake.draw(screen,SNAKE_INNER_COLOR) #在窗體中畫出貪吃蛇 food.draw(screen) #在窗體中畫出食物 #print('update') pygame.display.update() #刷新 time_passed = clock.tick(SNAKE_SPEED) #控制幀率,等效蛇的移動速度 #運行主體程序 main()

  上面的代碼就是貪吃蛇主程序的代碼,主體程序的結構介紹以下:測試

  從上到下依次是:貪吃蛇死亡時運行的程序(函數)——控制貪吃蛇遊戲 暫停/開始 的程序(函數)——程序中的常量——pygame遊戲初始化的過程——main()主函數ui

  main()主函數結構細分:實例化遊戲對象(草地、食物、貪吃蛇),可看做主程序的初始化過程——while循環主體spa

  while()循環主體結構細分(while循環主體是貪吃蛇遊戲的核心):鍵盤監聽代碼——貪吃蛇與食物的座標變化過程代碼——在窗體中渲染草地、食物、貪吃蛇三個對象的代碼命令行

  在完成貪吃蛇代碼的編寫後,我滿懷期待的第一次運行這個程序,然而,這一切只是開始;(沒有什麼會是一次成功的,或許有,但對目前的我來講還有很長的路要走。)接下來,各類讓我懷疑人生的「填坑」之旅就此展開。(唉,本身挖的坑,再深也得含淚填完)設計

  哦對了,先上張完成程序的效果圖(額,暫時沒搞懂怎麼上視頻):code

  

  現就我填坑過程當中印象比較深入或常常碰到的「坑」和在此做個簡要說明:視頻

  •   全局變量與局部變量

  這個是我比較常遇到的坑之一,看標題像是基礎知識方面的問題,其實,嗯,的確是基礎知識,但我就是常常把這個問題忽略了。在python或其餘語言中,函數中的變量爲局部變量,函數外的變量相對這個函數而言是全局變量。在python中,局部變量在該函數調用完畢後就被回收,騰出內存空間,而全局變量在整個程序運行過程當中都會存在;嗯,扯遠了,我挖的「坑」其實就是在函數中調用了全局變量,但卻沒有在函數中定義這個全局變量(python中特別定義全局變量時使用global關鍵字),而致使python報錯。舉個例子:對象

  

  在我把global屏蔽後使用暫停遊戲功能,python會報錯以下

  

  •   跨模塊調用模塊

  這個是我至今還不是很清楚的問題。我一開始是在主程序中直接採用"import"方法導入草地、食物、貪吃蛇三個類模板,然而程序出現報錯,緣由是找不到pygame模塊和識別不了"randint"方法,而後我嘗試在這些類中一樣導入pyggame模塊和randint方法,再運行主程序,發現主程序能夠正常運行。後面我判斷單獨運行模塊和調用模塊後運行模塊是有區別的,起碼這個模塊只能識別本模塊中導入的模塊,而不能識別本模塊以外導入的模塊(好吧,有點繞口,但意思是這個意思)。我目前是把三個類模板和主程序放在同一個py文件中,共同使用pygame等模塊。

  •   代碼執行順序

  我記得我在以前的博客也提到過這個問題,代碼執行的順序特別重要,特別是在while循環中的代碼,代碼執行的前後順序直接與運行結果相關,要把代碼一條一條地在腦中過一遍,模擬執行結果,這樣才能避免錯誤,有興趣的能夠本身試試換個順序會發生什麼。特別的,在pygame中pygame.display.update()的順序要注意一下。我在設計遊戲 暫停/播放功能時就利用了這一特色。

  •   類中的變量、函數定義與調用

  在類中定義及調用變量、函數時要特別當心,尤爲是在區別類方法、對象方法、靜態方法、類屬性、對象屬性,加上一個全局變量這幾者以前的關係時。講真,一開始有點後悔用對象的方法(採用類)來寫貪吃蛇了,有點搬起石頭砸本身的腳。(還好後面都解決了,起碼用這種方法寫看着更清楚,符合python易讀的特性)此次的坑大都是由類定義、對象定義的問題引發的。特別是各類屬性、方法互相調用,一不當心就會發生問題。好比我以前定義貪吃蛇的頭部時是在generate()方法中定義了一個self.head屬性,後面統一調用這個屬性名;爲了找到這個「坑」,我找的都快懷疑人生了,實在太隱蔽了(或許我對python瞭解還不夠),經過各類方法測試,根據測試數據的逐步比對一步步排除,我縮小了範圍,最後花了半天才找到這個「罪魁禍首」。這個bug是在貪吃蛇遲到第一個食物,身體長長後出現的。緣由是以前定義self.head = len(self.snake_body) -1的self.snake_body仍是初始定義的長度,當貪吃蛇長長以後本來的頭部索引就變成頭部以後一截身體的索引了,由此致使了這個大坑的出現。

  其中還有不少「坑」,就不在這一一介紹了(說多了都是淚),此外,我會把貪吃蛇遊戲「進階」,作出一個功能更豐富的貪吃蛇遊戲,提升它的可玩性。這會在下篇博客介紹。

相關文章
相關標籤/搜索