自制贪吃蛇游戏中的几个“大坑”
贪吃蛇游戏已经告一段落了,在完成这个游戏的过程中,我遭遇了许多“坎坷”和“挫折”,下面就几个让我印象深刻的“挫折”做一个具体的讲解,以此来为这个贪吃蛇项目画上一个完整句号。(包括打包这个游戏时遇到的问题及解决方式。)
- BUG1 在运行贪吃蛇游戏时,如果同时按下两个方向键会出现贪吃蛇“莫名其妙”死亡的情况。针对此情况我先是判断贪吃蛇的死亡原因:
#贪吃蛇死亡判定 def isdead(self): #条件1——贪吃蛇撞墙 if (self.snake_body[len(self.snake_body)-1][0] == self.living_space[0] - self.size or \ self.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()“进行初始化才能顺利打包背景音乐。
贪吃蛇篇章,就此结束。