运行效果
1.开发环境
win10操作系统,pycharm 社区版 、anaconda3、pygame库
2.数据结构
列表list
3. 设计思路
对于贪吃蛇小游戏,首先简化成控制一个蛇头在地图中上下左右的移动,如下图所示,在此基础上再做以下的问题分析
3.1 贪吃蛇移动问题
由于贪吃蛇是不断朝着一个方向移动的,所以当蛇身具有多段,蛇头的位置将会是蛇头后的下一个位置,即蛇身会始终跟着蛇头的方向走,于是可以定义一个列表,列表中存蛇从蛇头到蛇尾每一个身段的坐标,这样在绘制的时候只需要迭代该列表,于是就解决了贪吃蛇移动的问题。
3.2 贪吃蛇吃食物问题
在蛇吃食物的判定中,只需要判断蛇头和食物是否重合,即判断x和y坐标是否完全相同,原理是蛇身始终跟着蛇头走,若蛇头没有吃食物,蛇身是不可能碰到食物的。
3.3 贪吃蛇加长身段问题
由于蛇是不断移动的,在判定吃到食物后,首先记录蛇尾的坐标,接着将蛇整体移动到下一个位置,之后在蛇尾新加一个坐标列表[x,y]存之前的蛇尾坐标,实际上就是在列表中新加一个存储x和y值的列表,最后则实现了贪吃蛇吃到食物后加长身段。
3.4 游戏结束判定
游戏结束条件有三种:
- 蛇越界 -> 游戏失败
- 蛇通过吃食物达到指定长度 -> 游戏胜利
- 蛇吃到自己 -> 游戏失败
3.5 积分统计问题
这个参考 3.2 的部分,在判定吃到食物后,使得存储分数的变量+1即可
4. 代码分析
4.1 全局变量说明
import pygame as pg, sys, random #将pygame库 命名为pg 简化代码
WIDTH = 800 #int型 游戏窗口宽度
HEIGHT = 600 #int型 游戏窗口高度
TITLE = 'Greedy Snake Game' #String型 游戏的标题
pg.init(); #pygame 库的初始化 具体实现细节可暂时忽略
screen = pg.display.set_mode((WIDTH,HEIGHT)) #Surface型 定义新的窗口 存到screen中
screen.fill([255,255,255]) #用白色填充窗口
# 之后会使用到的颜色, 三个参数分别为RGB的参数
RED = (255,0,0) #红色,蛇头
BLUE = (70,130,180) #蓝色,用来提示信息
PINK = (255,192,203) #粉色,蛇身
ORANGE = (255,165,0) #橘黄色,食物
BLACK = (0,0,0) #黑色,游戏边界
SCORE = int(0) #记录分数
GameState = 'run' #记录游戏状态、分别有run、over、lose、finish、分别表示游戏正在进行、游戏结束、游戏失败、请求退出游戏
4.2 定义贪吃蛇的类Snake
在变量名前加两个下划线表示为私有成员变量,但依然可以通过对象名. _ 类名 _ xxx
这种特殊方式来访问,这里为了体现类的三大特性之一封装。
4.2.1 变量记录表
变量名称 | 类型 | 作用说明 |
---|---|---|
__len | int | 记录蛇的身长(包括蛇头蛇尾) |
__speed | int | 记录蛇的移动速度 |
__point | list | 记录蛇身的坐标(x, y) ,便于绘制图形 |
__direction | string | 记录蛇目前的运动方向 分别有left、right、up、down |
__beforeDirection | string | 记录蛇之前的运动方向, 默认和上面的参数一致 |
getLonger | bool | 判断是否需要加长,为True则需要加长 |
4.2.3 方法记录表
方法名 | 传入参数 | 返回类型 | 作用说明 |
---|---|---|---|
__ init __ | len | Snake | 构造函数,在创建对象的时候会自动调用,根据蛇长len创建类Snake的对象 |
getPoint | None | list | 返回存储蛇身坐标的列表__point |
getDic | None | string | 返回蛇的当前运动方向 |
getPreDic | None | string | 返回蛇的之前运动方向 |
getLen | None | int | 返回蛇的当前身长__len |
getHeadXY | None | list | 返回蛇头的坐标信息[x,y] |
getTailXY | None | list | 返回蛇尾的坐标信息[x,y] |
getSpeed | None | int | 返回蛇的移动速度 |
setXY | index, X, Y | None | 设置蛇身索引为index的(默认0为蛇头) 的坐标为[X,Y] |
setDicAndPreDic | dic | None | 同时更新蛇的之前运动方向和 当前运动方向 |
setDic | dic | None | 只设置蛇的当前运动方向 |
setLonger | x, y | None | 将坐标[x,y]加入到蛇尾,并更新__len蛇长的值 |
4.2.4 类Snake的代码
class Snake(object):
__len = 1 #身长,默认为1
__speed = 10 #移动速度默认为10
__point = [[WIDTH / 2,HEIGHT / 2]] #存储蛇身的坐标,默认只有蛇头在中间位置
__direction = 'left' #蛇的运动方向,默认朝左
__beforeDirection = 'left' #记录蛇之前的方向, 默认朝左
getLonger = False #标记是否需要变长,即是否吃到食物
def __init__(self,len): #初始化
self.__point.clear() #考虑到重新开始,所以首先清除蛇身坐标
self.__point.append([WIDTH / 2, HEIGHT / 2]) # 加蛇头坐标在屏幕中间
self.__len = len #更新蛇身长度
for i in range(len - 1): #遍历创建除蛇头外蛇身的坐标,默认在上一个基础下 x坐标加一,即实现贪吃蛇默认水平在屏幕中间
p = self.__point[i]
self.__point.append([p[0] + 20, p[1]])
def getPoint(self):
return self.__point
def getDic(self):
return self.__direction
def getPreDic(self):
return self.__beforeDirection
def setXY(self, index, X,Y):
point = self.__point[index]
point[0], point[1] = X, Y
def setDicAndPreDic(self, dic):
self.__beforeDirection = self.__direction
self.__direction = dic
def setDic(self, dic):
self.__direction = dic
def getLen(self):
return self.__len
def getHeadXY(self):
return self.__point[0]
def getTailXY(self):
return self.__point[self.__len - 1]
def setLonger(self, x, y):
self.__point.append([x,y])
self.__len += 1
def getSpeed(self):
return self.__speed
通过Snake类的代码可以看出,缺少了绘制蛇身的代码,为了更加清晰地编写程序,绘制贪吃蛇的代码会放在另外的函数当中。
4.3 定义食物的类Food
4.3.1 变量记录表
变量名称 | 类型 | 作用说明 |
---|---|---|
_X | int | 存储食物的X坐标 |
_Y | int | 存储食物的Y坐标 |
4.3.2 方法记录表
方法名 | 传入参数 | 返回类型 | 作用说明 |
---|---|---|---|
__ init __ | None | Food | 构造函数,在创建对象的时候会自动调用,默认随机设置食物位置 |
isOK | snake | bool | 判断食物当前坐标与类Snake的对象snake的坐标是否冲突 |
setRandomFood | snake | None | 随机设置食物的位置,保证最终食物的位置与蛇身不冲突 |
draw | snake | None | 在食物位置与蛇头位置不冲突的前提下绘制食物 |
4.3.3 类Food的代码
class Food:
_X , _Y = 0, 0 #记录食物的坐标
def __init__(self):
self._X, self._Y = random.randint(2, WIDTH / 25) * 20, random.randint(2, HEIGHT / 25) * 20 #创建对象时自动产生坐标
def isOK(self,snake): #判断食物当前坐标是否合法
flag = True # 默认合法,接下来遍历蛇的坐标判断是否合法
for point in snake.getPoint():
if point[0] == self._X and point[1] == self._Y:
flag = False
return flag
def setRandomFood(self, snake):
while self.isOK(snake) == False: