python实现贪吃蛇双人大战


Posted in Python onApril 18, 2020

本文实例为大家分享了python实现贪吃蛇双人大战的具体代码,供大家参考,具体内容如下

晚上家里小朋友要玩贪吃蛇游戏,还要跟我对战,一时半会我没想到去哪里下这样一个游戏,忽然灵机一动,可以自己写一个,顺便还可以跟小朋友展示一下程序员的厉害,于是开工。

原始版本

这是一个很基础的程序,自然不用从头写,在网上随便一搜,找到有人共享的代码,点击链接,据说是来源于《Raspberry Pi 用户指南》的代码,我也没有去查。代码如下:

#!/usr/bin/env python
import pygame,sys,time,random
from pygame.locals import *
# 定义颜色变量
redColour = pygame.Color(255,0,0)
blackColour = pygame.Color(0,0,0)
whiteColour = pygame.Color(255,255,255)
greyColour = pygame.Color(150,150,150)

# 定义gameOver函数
def gameOver(playSurface):
 gameOverFont = pygame.font.Font('arial.ttf',72)
 gameOverSurf = gameOverFont.render('Game Over', True, greyColour)
 gameOverRect = gameOverSurf.get_rect()
 gameOverRect.midtop = (320, 10)
 playSurface.blit(gameOverSurf, gameOverRect)
 pygame.display.flip()
 time.sleep(5)
 pygame.quit()
 sys.exit()

# 定义main函数
def main():
 # 初始化pygame
 pygame.init()
 fpsClock = pygame.time.Clock()
 # 创建pygame显示层
 playSurface = pygame.display.set_mode((640,480))
 pygame.display.set_caption('Raspberry Snake')

 # 初始化变量
 snakePosition = [100,100]
 snakeSegments = [[100,100],[80,100],[60,100]]
 raspberryPosition = [300,300]
 raspberrySpawned = 1
 direction = 'right'
 changeDirection = direction
 while True:
  # 检测例如按键等pygame事件
  for event in pygame.event.get():
   if event.type == QUIT:
    pygame.quit()
    sys.exit()
   elif event.type == KEYDOWN:
    # 判断键盘事件
    if event.key == K_RIGHT or event.key == ord('d'):
     changeDirection = 'right'
    if event.key == K_LEFT or event.key == ord('a'):
     changeDirection = 'left'
    if event.key == K_UP or event.key == ord('w'):
     changeDirection = 'up'
    if event.key == K_DOWN or event.key == ord('s'):
     changeDirection = 'down'
    if event.key == K_ESCAPE:
     pygame.event.post(pygame.event.Event(QUIT))
  # 判断是否输入了反方向
  if changeDirection == 'right' and not direction == 'left':
   direction = changeDirection
  if changeDirection == 'left' and not direction == 'right':
   direction = changeDirection
  if changeDirection == 'up' and not direction == 'down':
   direction = changeDirection
  if changeDirection == 'down' and not direction == 'up':
   direction = changeDirection
  # 根据方向移动蛇头的坐标
  if direction == 'right':
   snakePosition[0] += 20
  if direction == 'left':
   snakePosition[0] -= 20
  if direction == 'up':
   snakePosition[1] -= 20
  if direction == 'down':
   snakePosition[1] += 20
  # 增加蛇的长度
  snakeSegments.insert(0,list(snakePosition))
  # 判断是否吃掉了树莓
  if snakePosition[0] == raspberryPosition[0] and snakePosition[1] == raspberryPosition[1]:
   raspberrySpawned = 0
  else:
   snakeSegments.pop()
  # 如果吃掉树莓,则重新生成树莓
  if raspberrySpawned == 0:
   x = random.randrange(1,32)
   y = random.randrange(1,24)
   raspberryPosition = [int(x*20),int(y*20)]
   raspberrySpawned = 1
  # 绘制pygame显示层
  playSurface.fill(blackColour)
  for position in snakeSegments:
   pygame.draw.rect(playSurface,whiteColour,Rect(position[0],position[1],20,20))
   pygame.draw.rect(playSurface,redColour,Rect(raspberryPosition[0], raspberryPosition[1],20,20))

  # 刷新pygame显示层
  pygame.display.flip()
  # 判断是否死亡
  if snakePosition[0] > 620 or snakePosition[0] < 0:
   gameOver(playSurface)
  if snakePosition[1] > 460 or snakePosition[1] < 0:
   for snakeBody in snakeSegments[1:]:
    if snakePosition[0] == snakeBody[0] and snakePosition[1] == snakeBody[1]:
     gameOver(playSurface)
  # 控制游戏速度
  fpsClock.tick(5)

if __name__ == "__main__":
 main()

此代码实现了基本功能,主循环中先判断按键事件,然后调整蛇的位置,若蛇吃到了豆子(这个代码里叫树莓,我嫌名字太长,改成了习惯的豆子),则增加蛇的长度,并重新生成豆子,接着刷新显示,最后判断是否死亡,若死亡则调用gameOver。

当然这个是满足不了小朋友的需求的,小朋友尝试了一下,马上提取了如下需求:

1、要跟我一起玩,也就是要有两条蛇,每人控制一个,看谁吃得多。
2、蛇死了之后不要结束,太麻烦,改为重新开始。
3、蛇的颜色要能自己定。
4、要能看出来蛇头,即蛇头需要用不同的颜色。
5、豆子数量太少,每次才一个,要一下子出现很多豆子,可以随便吃。

看来天下用户都一样,总是各种需求。于是为了便于以后的修改,我把蛇相关的操作提取了一个蛇的类如下。

蛇类

class Snake: 
 def __init__(self, color, headColor, ctrlKeys):
  self.color = color
  self.headColor = headColor
  self.ctrlKeys = ctrlKeys #按[上,下,左,右]的顺序
  self.direction = random.choice([-2,2,-1,1]) # 方向[-2,2,-1,1]分别表示[上,下,左,右]
  x = random.randrange(5,SCREEN_WIDTH/20-5)
  y = random.randrange(5,SCREEN_HEIGHT/20-5)
  self.headPos = [int(x*20),int(y*20)]
  self.segments = [self.headPos]
  self.moveAndAdd()
  self.moveAndAdd()  
 def changeDirection(self, pressKey):
  directions = [-2,2,-1,1]
  for direct, key in zip(directions, self.ctrlKeys):
   if key == pressKey and direct + self.direction != 0:
    self.direction = direct  
 def moveAndAdd(self):
  # 根据方向移动蛇头的坐标
  if self.direction == 1:
   self.headPos[0] += 20
  if self.direction == -1:
   self.headPos[0] -= 20
  if self.direction == -2:
   self.headPos[1] -= 20
  if self.direction == 2:
   self.headPos[1] += 20
  self.segments.insert(0,list(self.headPos)) # 在蛇头插入一格
 def pop(self):
  self.segments.pop() # 在蛇尾减去一格
 def show(self, playSurface):
  # 画蛇身
  for pos in self.segments[1:]:
   pygame.draw.rect(playSurface,self.color,Rect(pos[0],pos[1],20,20))
  # 画蛇头
  pygame.draw.rect(playSurface,self.headColor,Rect(self.headPos[0],self.headPos[1],20,20))
 def respawnIfDead(self):
  if self.headPos[0] > SCREEN_WIDTH-20 or self.headPos[0] < 0 or self.headPos[1] > SCREEN_HEIGHT-20 or self.headPos[1] < 0:  
   x = random.randrange(5,SCREEN_WIDTH/20-5)
   y = random.randrange(5,SCREEN_HEIGHT/20-5)
   self.direction = random.choice([-2,2,-1,1])
   self.headPos = [int(x*20),int(y*20)]
   self.segments = [self.headPos]
   self.moveAndAdd()
   self.moveAndAdd()

其中初始化函数有三个参数,分别是蛇的颜色,蛇头颜色,以及控制的按键。初始化的蛇为3格,随机出现在中央区域(太靠边怕还来不及反应就死了)。调用初始化的代码如下:

# 初始化蛇
ctrlKeys1 = [ord('w'),ord('s'),ord('a'),ord('d')]
ctrlKeys2 = [K_UP,K_DOWN,K_LEFT,K_RIGHT]
snake1 = Snake(GREEN,GREEN_HEAD,ctrlKeys1)
snake2 = Snake(RED,RED_HEAD,ctrlKeys2)

changeDirection 函数顾名思义是改变方向的,有一个参数是按键。self.direction 记录当前蛇移动的方向,用[-2,2,-1,1]分别表示[上,下,左,右],这主要是为了简化代码。changeDirection 函数根据按键值判断是否要改变方向。这里要注意蛇是不能后退的,例如往上走的时候按下键是没有效果的。

moveAndAdd 函数根据移动方向移动一格,并增加一格在蛇头。pop 函数在蛇尾减去一格。这两个函数结合起来即可实现蛇的移动,以及蛇增长一格并移动。

show 函数将蛇显示出来,先画蛇身,再画蛇头。以防蛇头被蛇身挡住。

respawnIfDead 函数判断蛇是否死亡,若死了就重生。目前死亡方式为超出边界。重生后的蛇随机出现在中央区域,身体恢复为3格。

为了满足很多豆子可以随便吃的需求,考虑到以后的扩展,把豆子也做了一个类Bean,并给豆子们也做了一个类Beans,如下。

豆类

class Bean:
 def __init__(self, color, pos):
  self.color = color
  self.pos = pos
 def beEaten(self, snakePos):
  if snakePos[0] == self.pos[0] and snakePos[1] == self.pos[1]:
   return True
  else:
   return False

class Beans:
 def __init__(self, color, totalNum):
  self.color = color
  self.totalNum = totalNum
  self.curNum = 0
  self.beans = []
 def generate(self):
  while self.curNum < self.totalNum:
   x = random.randrange(0,SCREEN_WIDTH/20)
   y = random.randrange(0,SCREEN_HEIGHT/20)
   self.beans.append(Bean(self.color, [int(x*20),int(y*20)]))
   self.curNum = self.curNum + 1
 def beEaten(self, snakePos):
  for bean in self.beans:
   if bean.beEaten(snakePos):
    self.beans.remove(bean)
    self.curNum = self.curNum - 1
    return True
  return False
 def show(self, playSurface):
  for bean in self.beans:
   pygame.draw.rect(playSurface,self.color,Rect(bean.pos[0],bean.pos[1],20,20))

豆类比较简单,初始化的时候要指定颜色和位置,有一个函数beEaten判断是否被吃了。

豆子们的类稍微复杂点,其包含了 totalNum 个豆子。豆子们初始化的时候需要指定颜色和数量。 curNum 用来记录当前有多少个豆子,因为有的豆子可能被吃掉了。generate 函数负责生成豆子,初始化以及豆子被吃掉后都可以用它来生成豆子,生成的豆子随机出现在屏幕范围内。beEaten 函数判断豆子们中是否有的被吃了,若被吃了就从列表 beans 中移除它,同时调整 curNum 用来记录当前还剩多少豆子。show 函数将豆子们都显示出来。
初始化豆子们的代码如下:

# 初始化豆子
yellowBeans = Beans(YELLOW, BEAN_NUM)
yellowBeans.generate()

在蛇和豆子们都初始化好了之后,主循环的代码可以简化如下:

while True:
  # 检测按键等pygame事件
  for event in pygame.event.get():
   if event.type == QUIT:
    pygame.quit()
    sys.exit()
   elif event.type == KEYDOWN:
    if event.key == K_ESCAPE:
     pygame.event.post(pygame.event.Event(QUIT))
    else:
     snake1.changeDirection(event.key)
     snake2.changeDirection(event.key)

  # 根据方向移动蛇并增加蛇长度一格
  snake1.moveAndAdd()
  snake2.moveAndAdd()

  # 如果豆子被吃掉,则重新生成豆子,否则蛇长度减少一格
  if yellowBeans.beEaten(snake1.headPos):
   yellowBeans.generate()
  else:
   snake1.pop()
  if yellowBeans.beEaten(snake2.headPos):
   yellowBeans.generate()
  else:
   snake2.pop()

  # 绘制刷新
  playSurface.fill(BLACK) # 绘制pygame显示层
  yellowBeans.show(playSurface)
  snake1.show(playSurface)
  snake2.show(playSurface)
  pygame.display.flip() # 刷新pygame显示层

  # 若死亡则重生
  snake1.respawnIfDead()
  snake2.respawnIfDead()

  # 控制游戏速度
  fpsClock.tick(5)

当然,为了能运行,pygame的初始化还是需要的:

pygame.init()
fpsClock = pygame.time.Clock()
# 创建pygame显示层
playSurface = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
pygame.display.set_caption('Snake Eat Beans')

最后,代码中用到的一些固定值定义如下:

# 定义界面大小
SCREEN_WIDTH = 1080
SCREEN_HEIGHT = 720

# 定义豆子数量
BEAN_NUM = 10

# 定义颜色变量
RED = pygame.Color(255,0,0)
RED_HEAD = pygame.Color(255,150,0)
BLACK = pygame.Color(0,0,0)
WHITE = pygame.Color(255,255,255)
GREY = pygame.Color(150,150,150)
GREEN = pygame.Color(0,255,0)
GREEN_HEAD = pygame.Color(100,200,200)
YELLOW = pygame.Color(255,255,0)
BLUE = pygame.Color(0,0,255)

终于,可以和我家小朋友一起愉快的玩耍了

python实现贪吃蛇双人大战

更多关于python游戏的精彩文章请点击查看以下专题:

更多有趣的经典小游戏实现专题,分享给大家:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python和shell变量互相传递的几种方法
Nov 20 Python
在Python中操作字符串之startswith()方法的使用
May 20 Python
python实现在windows服务中新建进程的方法
Jun 30 Python
Python使用设计模式中的责任链模式与迭代器模式的示例
Mar 02 Python
python3实现UDP协议的服务器和客户端
Jun 14 Python
利用python操作SQLite数据库及文件操作详解
Sep 22 Python
pandas DataFrame实现几列数据合并成为新的一列方法
Jun 08 Python
Python多项式回归的实现方法
Mar 11 Python
Python生成词云的实现代码
Jan 14 Python
Django跨域资源共享问题(推荐)
Mar 09 Python
Pytorch mask-rcnn 实现细节分享
Jun 24 Python
Python列表删除重复元素与图像相似度判断及删除实例代码
May 07 Python
Python的in,is和id函数代码实例
Apr 18 #Python
Python json读写方式和字典相互转化
Apr 18 #Python
Python figure参数及subplot子图绘制代码
Apr 18 #Python
Python数组拼接np.concatenate实现过程
Apr 18 #Python
Python稀疏矩阵及参数保存代码实现
Apr 18 #Python
Python try except异常捕获机制原理解析
Apr 18 #Python
Python数据正态性检验实现过程
Apr 18 #Python
You might like
php基于mcrypt的加密解密实例
2014/10/27 PHP
thinkphp数据查询和遍历数组实例
2014/11/28 PHP
PHP_SELF,SCRIPT_NAME,REQUEST_URI区别
2014/12/24 PHP
php 从一个数组中随机的取出若干个不同的数实例
2016/12/31 PHP
php session的应用详细介绍
2017/03/22 PHP
PHP仿tp实现mvc框架基本设计思路与实现方法分析
2018/05/23 PHP
PHP实现与java 通信的插件使用教程
2019/08/11 PHP
php 使用expat方式解析xml文件操作示例
2019/11/26 PHP
php中Swoole的热更新实现代码实例
2021/03/04 PHP
Web层改进II-用xmlhttp 无声息提交复杂表单
2007/01/22 Javascript
jquery.validate分组验证代码
2011/03/17 Javascript
javascript将异步校验表单改写为同步表单
2015/01/27 Javascript
javascript HTML+CSS实现经典橙色导航菜单
2016/02/16 Javascript
在网页中插入百度地图的步骤详解
2016/12/02 Javascript
jQuery实现弹幕效果
2017/02/17 Javascript
jQuery EasyUI 组件加上“清除”功能实例详解
2017/04/11 jQuery
vue.js中mint-ui框架的使用方法
2017/05/12 Javascript
JS中实现浅拷贝和深拷贝的代码详解
2019/06/05 Javascript
js判断复选框是否选中的方法示例【基于jQuery】
2019/10/10 jQuery
如何将Node.js中的回调转换为Promise
2020/11/10 Javascript
pthon贪吃蛇游戏详细代码
2019/01/27 Python
解决python打不开文件(文件不存在)的问题
2019/02/18 Python
详解Python打包分发工具setuptools
2019/08/05 Python
python解析yaml文件过程详解
2019/08/30 Python
结合OpenCV与TensorFlow进行人脸识别的实现
2019/10/10 Python
Python实现密码薄文件读写操作
2019/12/16 Python
简单了解Java Netty Reactor三种线程模型
2020/04/26 Python
python怎么自定义捕获错误
2020/06/29 Python
Idea安装python显示无SDK问题解决方案
2020/08/12 Python
Python用户自定义异常的实现
2020/12/25 Python
html5指南-2.如何操作document metadata
2013/01/07 HTML / CSS
创建文明城市标语
2014/06/16 职场文书
计算机应用应届生求职信
2014/07/12 职场文书
十佳好少年事迹材料
2014/08/21 职场文书
2014幼儿园小班工作总结
2014/11/10 职场文书
python如何利用traceback获取详细的异常信息
2021/06/05 Python