python简易贪吃蛇
前言
开始
献上代码
from tkinter.messagebox import showinfo as msg
from random import randint as rand
from pygame.locals import *
import tkinter as tk
import pygame
import time
win = tk.Tk()
win.withdraw()
pygame.init()
def render(surf,blocks):
for x,y in blocks.keys():
renderingf = (x * 40,y * 40)
color = blocks[(x,y)]
pygame.draw.rect(surf,color,Rect(renderingf,(40,40)))
def render_snake(surf,pos):
def unique(pos):
for i in pos:
if(pos.count(i) > 1):
return False
return True
if(not unique(pos)):
return False
d = {}
for i in pos:
d[tuple(i)] = (255,255,255)
d[pos[-1]] = (0,0,255)
render(surf,d)
return True
def main(size):
scr = pygame.display.set_mode((40 * size[0],40 * size[1]))
stop = False
l = 3
pos = [(0,0),(1,0),(2,0)]
last = 'd'
dir = {'w':(0,-1),'s':(0,1),'a':(-1,0),'d':(1,0)}
oppsite = {'w':'s','s':'w','a':'d','d':'a'}
food = (rand(0,size[0] - 1),rand(0,size[1] - 1))
n = 1
speed = 30
while(1):
if((food in pos) and (not stop)):
l += 1
food = (rand(0,size[0] - 1),rand(0,size[1] - 1))
if(l == size[0] * size[1]):
return l
if(pos[-1][0] < 0 or pos[-1][1] < 0 or pos[-1][0] >= size[0] or pos[-1][1] >= size[1]):
return l
pygame.display.update()
speed = 30 - int(l / (1 - 3 / (size[0] * size[1])))
n += 1
n %= speed
pos = pos[-l:]
if((not n) and (not stop)):
t = (pos[-1][0] + dir[last][0],pos[-1][1] + dir[last][1])
pos.append(t)
scr.fill((0,0,0))
for ev in pygame.event.get():
if(ev.type == QUIT):
exit()
elif(ev.type == KEYDOWN and ev.unicode in 'aAwWsSdD' and ev.unicode):
if(ev.unicode == oppsite[last]):
return l
t = (pos[-1][0] + dir[ev.unicode.lower()][0],
pos[-1][1] + dir[ev.unicode.lower()][1])
pos.append(t)
last = str(ev.unicode.lower())
elif(ev.type == MOUSEBUTTONDOWN):
stop = not stop
elif(ev.type == KEYDOWN and ev.key == K_ESCAPE and stop):
return l
if(stop):
img = pygame.image.load('pause.jpg')
img = pygame.transform.scale(img,(40 * size[0],40 * size[1]))
scr.blit(img,(0,0))
else:
render(scr,{food:(0,255,0)})
if(not render_snake(scr,pos)):
return l
if(__name__ == '__main__'):
size = [15,15]
while(1):
l = main(size)
msg("GAMEOVER!","GAMEOVER! score:%s"%l)
代码剖析
from tkinter.messagebox import showinfo as msg
from random import randint as rand
from pygame.locals import *
import tkinter as tk
import pygame
import time
win = tk.Tk()
win.withdraw()
pygame.init()
导入库并初始化
注意⚠️:tkinter一定要在pygame之前初始化
def render(surf,blocks):
for x,y in blocks.keys():
renderingf = (x * 40,y * 40)
color = blocks[(x,y)]
pygame.draw.rect(surf,color,Rect(renderingf,(40,40)))
渲染格子,surf是要渲染的Surface对象,blocks是要渲染的位置和颜色
def render_snake(surf,pos):
def unique(pos):
for i in pos:
if(pos.count(i) > 1):
return False
return True
if(not unique(pos)):
return False
d = {}
for i in pos:
d[tuple(i)] = (255,255,255)
d[pos[-1]] = (0,0,255)
render(surf,d)
return True
渲染蛇,即为render(点击跳转)的包装。surf为要渲染的Surface对象,pos是蛇身子的每个格子的坐标列表。其中unique为蛇是否碰到自己的检测,其思路是,如果pos中有重复的,蛇就否碰到自己,所以,unique就是检测数组中是否有重复数据的
def main(size):
scr = pygame.display.set_mode((40 * size[0],40 * size[1]))
stop = False
l = 3
pos = [(0,0),(1,0),(2,0)]
last = 'd'
dir = {'w':(0,-1),'s':(0,1),'a':(-1,0),'d':(1,0)}
oppsite = {'w':'s','s':'w','a':'d','d':'a'}
food = (rand(0,size[0] - 1),rand(0,size[1] - 1))
n = 1
speed = 30
while(1):
if((food in pos) and (not stop)):
l += 1
food = (rand(0,size[0] - 1),rand(0,size[1] - 1))
if(l == size[0] * size[1]):
return l
if(pos[-1][0] < 0 or pos[-1][1] < 0 or pos[-1][0] >= size[0] or pos[-1][1] >= size[1]):
return l
pygame.display.update()
speed = 30 - int(l / (1 - 3 / (size[0] * size[1])))
n += 1
n %= speed
pos = pos[-l:]
if((not n) and (not stop)):
t = (pos[-1][0] + dir[last][0],pos[-1][1] + dir[last][1])
pos.append(t)
scr.fill((0,0,0))
for ev in pygame.event.get():
if(ev.type == QUIT):
exit()
elif(ev.type == KEYDOWN and ev.unicode in 'aAwWsSdD' and ev.unicode):
if(ev.unicode == oppsite[last]):
return l
t = (pos[-1][0] + dir[ev.unicode.lower()][0],
pos[-1][1] + dir[ev.unicode.lower()][1])
pos.append(t)
last = str(ev.unicode.lower())
elif(ev.type == MOUSEBUTTONDOWN):
stop = not stop
elif(ev.type == KEYDOWN and ev.key == K_ESCAPE and stop):
return l
if(stop):
img = pygame.image.load('pause.jpg')
img = pygame.transform.scale(img,(40 * size[0],40 * size[1]))
scr.blit(img,(0,0))
else:
render(scr,{food:(0,255,0)})
if(not render_snake(scr,pos)):
return l
这里分开分析
def main(size):
scr = pygame.display.set_mode((40 * size[0],40 * size[1]))
stop = False
l = 3
pos = [(0,0),(1,0),(2,0)]
last = 'd'
dir = {'w':(0,-1),'s':(0,1),'a':(-1,0),'d':(1,0)}
oppsite = {'w':'s','s':'w','a':'d','d':'a'}
food = (rand(0,size[0] - 1),rand(0,size[1] - 1))
n = 1
speed = 30
创建窗口,初始化是否暂停,蛇长,身体个块坐标,最后一个操作,方向与偏移量的关系字典,相反动作字典,食物位置,轮回数,速度(住:这里speed越大,速度越慢,请记住这件事情,后面会用到)
while(1):
if((food in pos) and (not stop)):
l += 1
food = (rand(0,size[0] - 1),rand(0,size[1] - 1))
if(l == size[0] * size[1]):
return l
if(pos[-1][0] < 0 or pos[-1][1] < 0 or pos[-1][0] >= size[0] or pos[-1][1] >= size[1]):
return l
进入游戏循环,处理吃到食物,蛇占满格子的问题
pygame.display.update()
speed = 30 - int(l / (1 - 3 / (size[0] * size[1])))
n += 1
n %= speed
pos = pos[-l:]
if((not n) and (not stop)):
t = (pos[-1][0] + dir[last][0],pos[-1][1] + dir[last][1])
pos.append(t)
scr.fill((0,0,0))
刷新窗口,清屏,这里中间的代码的作用向↓
\downarrow↓看
speed = 30 - int(l / (1 - 3 / (size[0] * size[1])))
n += 1
n %= speed
pos = pos[-l:]
if((not n) and (not stop)):
t = (pos[-1][0] + dir[last][0],pos[-1][1] + dir[last][1])
pos.append(t)
scr.fill((0,0,0))
第一行更新速度,这里公式是由速
度
=
蛇
长
÷
[
(
最
长
长
度
−
初
始
长
度
)
÷
最
长
长
度
]
速度=蛇长 \p [(最长长度 - 初始长度) \p 最长长度]速度=蛇长÷[(最长长度−初始长度)÷最长长度],将(
最
长
长
度
−
初
始
长
度
)
÷
最
长
长
度
(最长长度 - 初始长度) \p 最长长度(最长长度−初始长度)÷最长长度改成最
长
长
度
÷
最
长
长
度
−
初
始
长
度
÷
最
长
长
度
最长长度 \p 最长长度 - 初始长度 \p 最长长度最长长度÷最长长度−初始长度÷最长长度也就是1
−
3
/
(
s
i
z
e
[
0
]
∗
s
i
z
e
[
1
]
)
1 - 3 / (size[0] * size[1])1−3/(size[0]∗size[1]),整理一下,变成s
p
e
e
d
=
l
÷
[
1
−
3
/
(
s
i
z
e
[
0
]
∗
s
i
z
e
[
1
]
)
]
speed=l \p [1 - 3 / (size[0] * size[1])]speed=l÷[1−3/(size[0]∗size[1])],由于速度是整数,所以去一下整。
再往后就要解释一下为什么speed越大,速度越快了,因为speed指的就是经过speed个轮回之后蛇就自动挪一下,那么换句话说,speed 越大,不挪的轮回数就越多,挪动的时间间隔就越长
所以每次n
=
n
+
1
n = n + 1n=n+1就指的是又轮回了一次(因为n
nn这的就是轮回数),如果n
=
s
p
e
e
d
n = speedn=speed了,那么n
%
s
p
e
e
d
n~\%~speedn%speed就等于0,于是每次经过n
=
(
n
+
1
)
%
s
p
e
e
d
n = (n + 1)~\%~speedn=(n+1)%speed之后如果n
=
0
n = 0n=0,就代表改挪一下了。
for ev in pygame.event.get():
if(ev.type == QUIT):
exit()
elif(ev.type == KEYDOWN and ev.unicode in 'aAwWsSdD' and ev.unicode):
if(ev.unicode == oppsite[last]):
return l
t = (pos[-1][0] + dir[ev.unicode.lower()][0],
pos[-1][1] + dir[ev.unicode.lower()][1])
pos.append(t)
last = str(ev.unicode.lower())
elif(ev.type == MOUSEBUTTONDOWN):
stop = not stop
elif(ev.type == KEYDOWN and ev.key == K_ESCAPE and stop):
return l
处理事件
点x就退出
点awsd就按照方向移动
点击鼠标暂停或继续
暂停时点ESC就再来一局
if(stop):
img = pygame.image.load('pause.jpg')
img = pygame.transform.scale(img,(40 * size[0],40 * size[1]))
scr.blit(img,(0,0))
else:
render(scr,{food:(0,255,0)})
if(not render_snake(scr,pos)):
return l
如果是没有在玩(暂停),那么将图片与窗口调成同样大小后显示,显示的图片向↓
\downarrow↓看
不难看出就是暂停继续的图案
如果在玩(没暂停),那么调用render_snake(点击跳转)渲染蛇,如果返回False,也就是撞自己上了,游戏结束。
if(__name__ == '__main__'):
size = [15,15]
while(1):
l = main(size)
msg("GAMEOVER!","GAMEOVER! score:%s"%l)
运行游戏,size中的两个值表明区域大小,可自行更改(推荐≤
15
\le15≤15)。
项目github
作者
hit-road
拜拜,下课!
回到顶部