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))

第一行更新速度,这里公式是由 速 度 = 蛇 长 ÷ [ ( 最 长 长 度 − 初 始 长 度 ) ÷ 最 长 长 度 ] 速度=蛇长 \div [(最长长度 - 初始长度) \div 最长长度] =÷[()÷],将 ( 最 长 长 度 − 初 始 长 度 ) ÷ 最 长 长 度 (最长长度 - 初始长度) \div 最长长度 ()÷改成 最 长 长 度 ÷ 最 长 长 度 − 初 始 长 度 ÷ 最 长 长 度 最长长度 \div 最长长度 - 初始长度 \div 最长长度 ÷÷也就是 1 − 3 / ( s i z e [ 0 ] ∗ s i z e [ 1 ] ) 1 - 3 / (size[0] * size[1]) 13/(size[0]size[1]),整理一下,变成 s p e e d = l ÷ [ 1 − 3 / ( s i z e [ 0 ] ∗ s i z e [ 1 ] ) ] speed=l \div [1 - 3 / (size[0] * size[1])] speed=l÷[13/(size[0]size[1])],由于速度是整数,所以去一下整。


再往后就要解释一下为什么speed越大,速度越快了,因为speed指的就是经过speed个轮回之后蛇就自动挪一下,那么换句话说,speed 越大,不挪的轮回数就越多,挪动的时间间隔就越长
所以每次 n = n + 1 n = n + 1 n=n+1就指的是又轮回了一次(因为 n n n这的就是轮回数),如果 n = s p e e d n = speed n=speed了,那么 n   %   s p e e d n~\%~speed n % speed就等于0,于是每次经过 n = ( n + 1 )   %   s p e e d n = (n + 1)~\%~speed n=(n+1) % speed之后如果 n = 0 n = 0 n=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

github传送门

作者

hit-road

拜拜,下课!
回到顶部

猜你喜欢

转载自blog.csdn.net/weixin_42954615/article/details/112900009