本程序是在TinyBrick的《使用python自带turtle库实现《贪吃蛇小游戏》》的基础上修改而来,如有侵权,告之删除,谢谢!链接:https://www.jianshu.com/p/993e8740104d
一、概述
实现的功能有:
苹果随机出现,且当苹果被吃掉时消失,再随机生成一个苹果;
蛇的初始长度为6个block,初始方向向东;
蛇通过方向键控制方向;
当蛇吃掉一个苹果后,身体会增加一个block;
当蛇撞墙或者撞到身体后游戏结束;
游戏中按空格暂停,结束后按回车再玩一次;
每吃5个苹果游戏速度会提速,并且蛇会变颜色。
主要逻辑图大致如下:
程序效果图:
二、完整代码
# 使用turtle做一个贪吃蛇游戏
import turtle as t
import random
WIN_WIDTH = 500 # 窗口宽度
WIN_HEIGHT = 500 # 窗口高度
SPOT_WIDTH = 10 # 贪吃蛇的宽度(方格的宽度)
isPause = False # 是否暂停
# 定义贪吃蛇类
class HungrySnake(object):
"""docstring for HungrySnake"""
x_add = SPOT_WIDTH # 贪吃蛇x坐标增量,改变移动方向
y_add = 0 # 贪吃蛇y坐标增量
#贪吃蛇坐标初始化,长度为六格
snakePos = [(0, 0), (SPOT_WIDTH, 0), (SPOT_WIDTH*2, 0), \
(SPOT_WIDTH*3, 0), (SPOT_WIDTH*4, 0), (SPOT_WIDTH*5, 0)]
speed = 1 # 速度
eatNum = 0 # 吃了几个苹果
# 苹果x坐标(-25,23)*10
x_apple = random.randint(int(-1*WIN_WIDTH/20), int(WIN_WIDTH/20-2)) * 10
# 苹果y坐标(-23,25)*10
y_apple = random.randint(int(-1*WIN_WIDTH/20+2), int(WIN_WIDTH/20)) * 10
# turtle颜色库
COLOR = ('black', 'grey', 'brown', 'orange', 'gold', 'olive', 'tomato', 'yellowgreen', 'lightgreen',\
'green', 'aquamarine', 'teal', 'deepskyblue', 'blue', 'violet', 'purple', 'pink', 'fuchsia')
# 贪吃蛇的颜色
snakeColor = "black"
# 构造函数,不用
def __init__(self):
super(HungrySnake, self).__init__()
# 画方格,(x,y)为坐标,width为宽度,color为颜色
def drawSpot(self, x, y, width, color):
t.penup()
t.goto(x, y)
t.pendown()
t.color(color) # 设定填充颜色
t.begin_fill() # 申明开始填充
for i in range(4):
t.forward(SPOT_WIDTH)
t.right(90)
t.end_fill() # 申明结束填充
t.penup()
# 画贪吃蛇
def drawSnake(self, color="black"):
for spot in self.snakePos:
self.drawSpot(spot[0], spot[1], SPOT_WIDTH, color)
# 贪吃蛇移动一步
def moveStep(self):
self.snakePos.pop(0) # 去掉尾
self.snakePos.append((self.snakePos[-1][0] + self.x_add, self.snakePos[-1][1] + self.y_add)) # 增加头
# 改变贪吃蛇运行方向的矢量
def changeDirection(self, x_add, y_add):
# 注意不能使用self.x_add = x_add, 这个是改变实例变量,不能改变类的变量
HungrySnake.x_add = x_add
HungrySnake.y_add = y_add
# 判断是否碰撞
def isCrash(self):
# 判断是否撞墙:左上角(-250, 250), 右下角(230, -230)
if self.snakePos[-1][0] < int(-1*WIN_WIDTH/20)*10 or self.snakePos[-1][0] > int(WIN_WIDTH/20-2)*10 or \
self.snakePos[-1][1] < int(-1*WIN_WIDTH/20+2)*10 or self.snakePos[-1][1] > int(WIN_WIDTH/20)*10:
return True
# 判断是否撞到蛇自己的身体
elif self.snakePos[-1] in self.snakePos[:-1]:
return True
else: # 啥也没撞到
return False
# 随机产生一个苹果
def generateApple(self):
# 苹果x坐标(-25,23)*10
x_apple = random.randint(int(-1*WIN_WIDTH/20), int(WIN_WIDTH/20-2)) * 10
# 苹果y坐标(-23,25)*10
y_apple = random.randint(int(-1*WIN_WIDTH/20+2), int(WIN_WIDTH/20)) * 10
HungrySnake.x_apple = x_apple
HungrySnake.y_apple = y_apple
return (x_apple, y_apple)
# 吃到苹果, 蛇身变长,再append放到蛇头
def eatApple(self):
self.snakePos.append(self.snakePos[-1])
HungrySnake.eatNum += 1 # 苹果数量加1
HungrySnake.speed = 1 + 0.5 * (HungrySnake.eatNum//5) # 每吃5个提速一次
if HungrySnake.eatNum % 5 == 0: # 每吃5个改变一次颜色
HungrySnake.snakeColor = random.choice(HungrySnake.COLOR)
# 显示苹果
def showApple(self):
# 如果吃到了苹果,就重新生产一个苹果
if self.snakePos[-1] == (HungrySnake.x_apple, HungrySnake.y_apple):
self.eatApple()
self.generateApple()
else: # 如果没有吃到苹果,就显示苹果
self.drawSpot(HungrySnake.x_apple, HungrySnake.y_apple, SPOT_WIDTH, "red")
print("apple:{}".format((HungrySnake.x_apple, HungrySnake.y_apple)))
# 显示信息
def showMsg():
t.pencolor("gray")
t.goto(-115, 120)
t.write("空格键:暂停/开始", font=("Arial", 16, 'normal'))
t.goto(-115, 90)
t.write("回车键:再玩一次!", font=("Arial", 16, 'normal'))
t.goto(-60, 60)
t.write("得分:{}".format(HungrySnake.eatNum), font=("Arial", 16, 'normal'))
# 游戏主循环
def game_loop():
t.clear() # 清除屏幕
snake = HungrySnake() # 实例化
snake.showApple() # 显示苹果
snake.drawSnake(snake.snakeColor) # 画贪吃蛇
snake.moveStep() # 贪吃蛇移动一步
# print("snake2:{}".format(snake))
print("snake:{}".format(snake.snakePos)) # 打印贪吃蛇坐标
print("direction:{}".format((snake.x_add, snake.y_add))) # 前进方向
print("crash:{}".format(snake.isCrash())) # 打印是否撞到
print("eatnum:{}".format(snake.eatNum))
print("speed:{}".format(snake.speed))
if not snake.isCrash() and not isPause: # 如果没有撞到且没有暂停,就不断循环
# 安装一个计时器,在 t 毫秒后调用 fun 函数 ,只执行一次
t.ontimer(game_loop, int(200/snake.speed))
elif snake.isCrash(): # 如果撞到,蛇头画红色,游戏结束
# 移动前蛇头画红色
snake.drawSpot(snake.snakePos[-2][0], snake.snakePos[-2][1], SPOT_WIDTH, "red")
showMsg() # 显示信息
t.done() # 开始事件循环 - 调用 Tkinter 的 mainloop 函数
else:
pass
# 设置游戏暂停
def setPause():
# 没撞到了才能暂停
if not HungrySnake().isCrash():
global isPause
isPause = not isPause
print("isPause:{}".format(isPause))
game_loop()
# 游戏重新开始
def restart():
# 撞到了才能重新游戏
if HungrySnake().isCrash():
# 恢复初始参数
HungrySnake.x_add = SPOT_WIDTH
HungrySnake.y_add = 0
HungrySnake.snakePos = [(0, 0), (SPOT_WIDTH, 0), (SPOT_WIDTH*2, 0), \
(SPOT_WIDTH*3, 0), (SPOT_WIDTH*4, 0), (SPOT_WIDTH*5, 0)]
HungrySnake.speed = 1
HungrySnake.eatNum = 0
HungrySnake.snakeColor = "black"
HungrySnake().generateApple()
game_loop() # 重新开始游戏
# 主函数
def main():
t.setup(WIN_WIDTH, WIN_HEIGHT) # 设置窗口大小
t.title("贪吃蛇") # 窗口标题
t.hideturtle() # 隐藏海龟
t.tracer(False) # 启用/禁用海龟动画并设置刷新图形的延迟时间
t.listen() # 开始监听按键事件
# 绑定 fun 指定的函数到指定键的按下事件,反应较快。使用lambda函数
t.onkeypress(lambda: HungrySnake().changeDirection((-1)*SPOT_WIDTH, 0), 'Left') # 左
t.onkeypress(lambda: HungrySnake().changeDirection(SPOT_WIDTH, 0), 'Right') # 右
t.onkeypress(lambda: HungrySnake().changeDirection(0, SPOT_WIDTH), 'Up') # 上
t.onkeypress(lambda: HungrySnake().changeDirection(0, (-1)*SPOT_WIDTH), 'Down') # 下
t.onkeypress(lambda: setPause(), 'space') # 空格,暂停游戏
t.onkeypress(lambda: restart(), 'Return') # 回车,重新游戏
game_loop() # 进入游戏主循环
t.done() # 开始事件循环 - 调用 Tkinter 的 mainloop 函数
if __name__ == '__main__':
main()