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自动化测试工具Splinter简介和使用实例
May 13 Python
Python base64编码解码实例
Jun 21 Python
python常见的格式化输出小结
Dec 15 Python
python+matplotlib绘制旋转椭圆实例代码
Jan 12 Python
浅谈python的dataframe与series的创建方法
Nov 12 Python
Python数据可视化教程之Matplotlib实现各种图表实例
Jan 13 Python
python实现定时压缩指定文件夹发送邮件
Dec 22 Python
python实现对服务器脚本敏感信息的加密解密功能
Aug 13 Python
Python3并发写文件与Python对比
Nov 20 Python
tensorflow2.0与tensorflow1.0的性能区别介绍
Feb 07 Python
python手写均值滤波
Feb 19 Python
python输出国际象棋棋盘的实例分享
Nov 26 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程序员的技术瓶颈分析
2011/07/17 PHP
PHP查询MySQL大量数据的时候内存占用分析
2011/07/22 PHP
php中使用临时表查询数据的一个例子
2013/02/03 PHP
php获取新浪微博数据API实例
2013/11/12 PHP
PHP中对于浮点型的数据需要用不同的方法解决
2014/03/11 PHP
PHP数字字符串左侧补0、字符串填充和自动补齐的几种方法
2014/05/10 PHP
PHP 中常量的知识整理
2017/04/14 PHP
ajax不执行success回调而是执行了error回调
2012/12/10 Javascript
javascript实现TreeView 无刷新展开的实例代码
2013/07/13 Javascript
js几秒以后倒计时跳转示例
2013/12/26 Javascript
JSONP跨域GET请求解决Ajax跨域访问问题
2014/12/31 Javascript
jquery.uploadify插件在chrome浏览器频繁崩溃解决方法
2015/03/01 Javascript
JavaScript DOM节点操作方法总结
2016/08/23 Javascript
基于JS快速实现导航下拉菜单动画效果附源码下载
2016/10/27 Javascript
Vue+Element使用富文本编辑器的示例代码
2017/08/14 Javascript
Vue.js做select下拉列表的实例(ul-li标签仿select标签)
2018/03/02 Javascript
详解javascript中var与ES6规范中let、const区别与用法
2020/01/11 Javascript
JavaScript中使用Spread运算符的八种方法总结
2020/06/18 Javascript
完美解决通过IP地址访问VUE项目的问题
2020/07/18 Javascript
基于JavaScript实现轮播图效果
2021/01/02 Javascript
python用ConfigObj读写配置文件的实现代码
2013/03/04 Python
python列表与元组详解实例
2013/11/01 Python
详解Python中的strftime()方法的使用
2015/05/22 Python
Python使用turtule画五角星的方法
2015/07/09 Python
详解python的数字类型变量与其方法
2016/11/20 Python
使用tensorflow实现线性svm
2018/09/07 Python
Python图像处理之直线和曲线的拟合与绘制【curve_fit()应用】
2018/12/26 Python
python识别图像并提取文字的实现方法
2019/06/28 Python
我们为什么要减少Python中循环的使用
2019/07/10 Python
Python实现直方图均衡基本原理解析
2019/08/08 Python
python Web flask 视图内容和模板实现代码
2019/08/23 Python
如何写你的创业计划书
2014/01/07 职场文书
《长江之歌》教学反思
2014/04/17 职场文书
2014年班组建设工作总结
2014/12/01 职场文书
2019毕业论文致谢词
2019/06/24 职场文书
总结高并发下Nginx性能如何优化
2021/11/01 Servers