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实现对PPT文件进行截图操作的方法
Apr 28 Python
在Windows服务器下用Apache和mod_wsgi配置Python应用的教程
May 06 Python
Python基于二分查找实现求整数平方根的方法
May 12 Python
python logging日志模块以及多进程日志详解
Apr 18 Python
浅谈Python2、Python3相对路径、绝对路径导入方法
Jun 22 Python
python实现合并两个排序的链表
Mar 03 Python
python调用动态链接库的基本过程详解
Jun 19 Python
PYTHON EVAL的用法及注意事项解析
Sep 06 Python
python使用ctypes库调用DLL动态链接库
Oct 22 Python
python实现数学模型(插值、拟合和微分方程)
Nov 13 Python
python实现excel公式格式化的示例代码
Dec 23 Python
python脚本使用阿里云slb对恶意攻击进行封堵的实现
Feb 04 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.NET的入门教程
2006/10/09 PHP
PHP 批量删除 sql语句
2009/06/05 PHP
PHP中捕获超时事件的方法实例
2015/02/12 PHP
PHP文件上传问题汇总(文件大小检测、大文件上传处理)
2015/12/24 PHP
PHP实现生成带背景的图形验证码功能
2016/10/03 PHP
利用php-cli和任务计划实现订单同步功能的方法
2017/05/03 PHP
使用IE的地址栏来辅助调试Web页脚本
2007/03/08 Javascript
简单的Jquery遮罩层代码实例
2013/11/14 Javascript
常用jQuery代码分享
2015/07/14 Javascript
学习Bootstrap滚动监听 附调用方法
2016/07/02 Javascript
完美的js div拖拽实例代码
2016/09/24 Javascript
详解Vue使用 vue-cli 搭建项目
2017/04/20 Javascript
JS实现下拉菜单列表与登录注册弹窗效果
2017/08/10 Javascript
AngularJS实现的2048小游戏功能【附源码下载】
2018/01/03 Javascript
JS实现的全选、全不选及反选功能【案例】
2019/02/19 Javascript
使用Vue Composition API写出清晰、可扩展的表单实现
2020/06/10 Javascript
[50:20]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第四局
2016/03/06 DOTA
详解Python程序与服务器连接的WSGI接口
2015/04/29 Python
python图像处理之镜像实现方法
2015/05/30 Python
Python批量更改文件名的实现方法
2017/10/29 Python
python微信跳一跳游戏辅助代码解析
2018/01/29 Python
python实现将读入的多维list转为一维list的方法
2018/06/28 Python
python 同时读取多个文件的例子
2019/07/16 Python
python openpyxl使用方法详解
2019/07/18 Python
Python占用的内存优化教程
2019/07/28 Python
css3实现画半圆弧线的示例代码
2017/11/06 HTML / CSS
ZWILLING双立人英国网上商店:德国刀具锅具厨具品牌
2018/05/15 全球购物
.net工程师笔试题
2012/06/09 面试题
保安拾金不昧表扬信
2014/01/15 职场文书
开会迟到检讨书
2014/02/03 职场文书
2014年关于两会精神的心得体会
2014/03/17 职场文书
计算机软件专业求职信
2014/06/10 职场文书
文明城市标语
2014/06/16 职场文书
农村文化活动总结
2014/08/28 职场文书
对党的十八届四中全会的期盼
2014/10/17 职场文书
加强干部作风建设整改方案
2014/10/24 职场文书