自制貪吃蛇遊戲中的幾個“大坑”
貪吃蛇遊戲已經告一段落了,在完成這個遊戲的過程中,我遭遇了許多“坎坷”和“挫折”,下面就幾個讓我印象深刻的“挫折”做一個具體的講解,以此來為這個貪吃蛇項目畫上一個完整句號。(包括打包這個遊戲時遇到的問題及解決方式。)
- BUG1 在運行貪吃蛇遊戲時,如果同時按下兩個方向鍵會出現貪吃蛇“莫名其妙”死亡的情況。針對此情況我先是判斷貪吃蛇的死亡原因:
#貪吃蛇死亡判定 def isdead(self): #條件1——貪吃蛇撞墻 if (self.snake_body[len(self.snake_body)-1][0] == self.living_space[0] - self.size orself.snake_body[len(self.snake_body)-1][0] == self.living_space[0] + self.living_space[2]) or (self.snake_body[len(self.snake_body)-1][1] == self.living_space[1] - self.size or self.snake_body[len(self.snake_body)-1][1] == self.living_space[1] + self.living_space[3]):#print(‘die for me 1‘) return True #條件2——貪吃蛇“追尾” for bodynet in self.snake_body[:-1]: if bodynet == self.snake_body[len(self.snake_body)-1]: #print(‘die for me 2‘) return True
通過貪吃蛇再次因此死亡而呈現的死亡原因(die for me 2)我得出貪吃蛇是因頭部觸碰到身體其他部位而“死亡”的結論。(即頭部坐標與身體其他部分坐標出現重合)。然而在貪吃蛇因為這種情況而死亡時窗體中並沒有呈現出其頭部與身體其他部分觸碰的情形,看來答案只能在代碼中尋找了。
#開始主循環 while True: #for _ in range(8): print(‘while start‘) for event in pygame.event.get(): #print(‘get event‘) #print(‘direction‘,DIRECTION) 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‘ #print(DIRECTION) elif event.key == K_UP and DIRECTION != ‘DOWN‘: DIRECTION = ‘UP‘ elif event.key == K_DOWN and DIRECTION != ‘UP‘: DIRECTION = ‘DOWN‘ #print(DIRECTION) elif event.key == K_SPACE: pause_flag = 0 while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() exit() if event.type == KEYDOWN: if event.key == K_SPACE: pause_flag = 1 if pause_flag == 1: break elif event.key == K_LSHIFT: SNAKE_SPEED *= 3 print(‘DIRECTION‘,DIRECTION) elif event.type == KEYUP: if event.key == K_LSHIFT: SNAKE_SPEED /= 3 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(): #判斷貪吃蛇是否死亡 #pygame.mixer.music.stop() #print(‘dead:‘,snakebody) print(‘dead‘,DIRECTION) sounddead.play() terminate(screen) else: pass
我在循環開始的地方添加了“print(‘while start‘)",在循環結束的地方,也就是pygame.display.update()之後添加了“print("update")”;通過這兩個語句來表示一次while循環的過程,根據這種方法可以分析出貪吃蛇死亡的那次while循環中發生了什麽。然後在監測鍵盤輸入的代碼之後添加“print(‘DIRECTION‘,DIRECTION)”,以此來打印出監測到的方向DIRECTION的值;並在terminate()之前打印出貪吃蛇死亡前的移動方向做確認。下面是貪吃蛇非正常死亡時命令行顯示的結果:
我在第一次看到這個結果時,感到十分納悶,為什麽print("DIRECTION",DIRECTION)這個語句會被執行兩次,這明明是在一次while循環裏面,真正百思不得其解。後面我看到了在這個語句外面還有一個while循環(之前的暫停功能while循環是寫在監聽鍵盤代碼的外面),於是就把暫停功能的while循環改了,放到了監聽方向的代碼之後(改後的代碼就是上面貼出的代碼);但是,然並卵,還是會出現這種問題,於是我繼續把這一段的代碼看了一遍,尋找循環語句。
然後我突然就看到了"for event in pygame.event.get():"這句代碼,再去看了一下pygame.event.get()方法的詳細資料,發現原來問題還真出現在這裏。(pygame.event資料鏈接:http://www.pygame.org/docs/ref/event.html)
通過上面的資料信息可以了解到pygame.event.get()方法返回的是Eventlist,是一個事件序列(多個事件的集合),那麽當在鍵盤按下不同按鍵的時間間隔足夠短,pygame.event.get()方法就會在一次while主循環中獲取到多個事件,for event in pygame.event.get(): 語句就會執行不止一次,這樣就會出現上面一次while主循環中打印兩次DIRECTION的結果。這樣一來,如果在遊戲初始貪吃蛇移動方向DIRECTION是RIGHT,那麽先按下上UP後再按下LEFT,就會使“elif event.key == K_RIGHT and DIRECTION != ‘LEFT‘”中的"and DIRECTION != ‘LEFT‘"語句的作用失效,就會出現貪吃蛇“逆行”的情況導致其頭部坐標與身體其他部位的坐標出現重合,進而死亡。
這個bug的解決辦法就是換.get()方法改用.poll()方法,因為.poll()方法在一次while主循環中只會監聽並返回一個事件,不會出現先UP後LEFT的情況。
- BUG 2 在普通模式我設置了一個傳送門,測試時發現貪吃蛇進入傳送門後如果馬上按方向鍵貪吃蛇會“無視”傳送門去往其他方向,傳送門“失效”。針對這個BUG,我直接限制貪吃蛇的“行動能力”,即在貪吃蛇進入傳送門的時候通過"pygame.event.get_blocked(KEYDOWN)“關閉鍵盤監聽,使玩家無法在此時控制貪吃蛇方向;同時在貪吃蛇徹底進入下一關之前使用”pygame.event.get_allowed(KEYDOWN)“恢復玩家對貪吃蛇移動方向的控制。
GATEWAY_FLAG標誌位為1表示貪吃蛇觸發了傳送門,正在進入
while True: #for _ in range(8): #print(‘while start‘) #進入傳送門的過程限制移動 if GATEWAY_FLAG == 1: pygame.event.set_blocked(KEYDOWN) #獲取事件 event = pygame.event.poll() #print(‘get event‘) #print(‘direction‘,DIRECTION) #判定事件 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‘ #print(DIRECTION) elif event.key == K_UP and DIRECTION != ‘DOWN‘: DIRECTION = ‘UP‘ elif event.key == K_DOWN and DIRECTION != ‘UP‘: DIRECTION = ‘DOWN‘ #print(DIRECTION)
GATEWAY_FLAG標誌位為2表示貪吃蛇已經徹底進入傳送門,即將前第三關
#判定是否進行傳送 if GATEWAY_FLAG == 2: #恢復貪吃蛇移動鎖定 pygame.event.set_allowed(KEYDOWN) GATEWAY_FLAG = 0
- 這個不算程序中的BUG,而是打包貪吃蛇遊戲時的一次慘痛經歷(解決這個的時候真的要哭了)。我使用的打包工具是pyinstaller,使用pip install pyinstaller指令安裝。(pyinstaller的安裝教程網上很多,這邊就不具體介紹了)我打包的環境是WIN10.。打包的時候要註意以下幾點(根據自己的打包過程總結):
- 打包後要把程序中用到的圖片、音頻文件拷貝到exe程序所在文件夾中(如果在程序中寫的路徑是當前工作路徑的話),程序中寫的文件路徑要對應為exe程序的路徑;否則會出現"failed to execute script"的錯誤。
- 要確保你的電腦有連接音頻設備,有安裝音頻驅動,否則程序同樣無法正常運行(這個教訓很慘痛,我後面發現是因為沒接喇叭導致的,真的巨蛋疼)
- 背景音樂無法打包。我在打包貪吃蛇的時候,發現背景音樂始終無法順利打包(始終會報錯),各種方法都嘗試了一遍,最後才發現是預初始化語句”pygame.mixer.pre_init()“出的幺蛾子,要使用”pygame.mixer.init()“進行初始化才能順利打包背景音樂。
貪吃蛇篇章,就此結束。
自制貪吃蛇遊戲中的幾個“大坑”