pygame写贪吃蛇
python小白尝试写游戏..
学了点pygame不知道那什么练手好,先拿贪吃蛇开刀吧.
一个游戏可以粗略的分为两个部分:
- 数据(变量)
- 处理数据(函数,方法)
设计变量
首先预想下,画面的那些部分需要存储在变量里

整个画面上只会有矩形,而且这些矩形整整齐齐,大小相等,原本一个矩形需要四个变量表示位置,这里,只需要两个变量(行数,列数)就能表示方块的位置
蛇头,食物可以用二元元组表示,蛇身的数量不确定,只能用包含数个元组的列表表示
另外设定窗口大小800x600,每个方块都是50x50
import pygame
import sys pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("贪吃蛇") food = (4, 5)
body = [(1, 1),(1,2)]
head = (1, 3) BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255 while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
变量设定好了,游戏已经完成了一半( ̄▽ ̄)~*
下一步
变量到画面
pygame.draw.rect()是根据矩形四元数组绘制图像的,那就写个函数"对接"下我的二元坐标
这里就成数学的问题了...
def new_draw_rect(zb, color,screen):
pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))
鉴于50x50时相邻方块们会"粘"在一起,方块向里收一下成48x48 ↑
绘制图形,
...
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() screen.fill(WHITE)
new_draw_rect(food, RED, screen)
for i in body:
new_draw_rect(i, BLUE, screen)
new_draw_rect(head, GREEN, screen) pygame.display.update()
由静到动
两个问题:
1.什么时候动
2.怎么动
问题1,什么时候动,这里有两个思路,
- 间隔固定时间(1秒),动一次
- 按一次键动一次,无操作一定时间(1秒)后,重复最后一次操作
看起来第一种方案 简单 不错
首先,先用pygame的clock类限制帧率(100帧),以方便计时
再者,加入新变量times,times每次加1,超过一百就"动一动"
加入新变量direction,表示蛇头朝向,衔接键盘操作和"怎么动"
PS:程序结束之前,很难知道要用多少变量
import pygame
import sys pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("贪吃蛇") fclock = pygame.time.Clock() food = (4, 5)
body = [(1, 1)]
head = (1, 2)
times = 0
direction = 'right' BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255 def new_draw_rect(zb, color,screen):
pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48))
pass while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
direction = "up"
elif event.key == pygame.K_LEFT:
direction = "left"
elif event.key == pygame.K_DOWN:
direction = "down"
elif event.key == pygame.K_RIGHT:
direction = "right"
if times >= 100:
pass#动一动
times = 0
else:
times += 1 screen.fill(WHITE)
new_draw_rect(food, RED, screen)
for i in body:
new_draw_rect(i, BLUE, screen)
new_draw_rect(head, GREEN, screen) fclock.tick(100)
pygame.display.update()
蛇头的运动规律 : 向临近的格子移动,上下左右具体那个格子由键盘确定
那就写个新函数去生成蛇头的新位置
def get_front(head,direction):
x, y = head
if direction == "up":
return x-1, y
elif direction == "left":
return x, y-1
elif direction == "down":
return x+1, y
elif direction == "right":
return x, y+1
然后
head = get_front(head,direction)
但是蛇蛇一头扎墙里怎么办.......
你的好友【front : 临时记下蛇头前方的位置】已上线
你的好友【alive : 记录存活信息】已上线
PS:front可以不是全局变量
def ask_alive(front,body):
x, y = front
if x < 0 or x > 12 or y < 0 or y >16 :
return False
if front in body:
return False
return True
然后这样用
front = get_front(head,direction)
alive = ask_alive(front,body)
if alive:
head = front
人话 :先看看蛇头前面是否有危险,有危险就死了 , 不动啦
另外alive得加到前面的if里面当限制条件,死了就不能乱动啦~~~~~
蛇身动的规律 : 近头端跟头走,尾端也跟着走--向程序靠拢-->>用过的head加入body,同时删去body最老的成员
这里可以看出body必须有序,可变.python里面就用列表了
如果使用list的append方法,head加在body的末尾,那么body[0]就会是"最老的成员"就得使用pop(0)删去
PS. body.append(head)得写在head=front前面,在head更新前加进body
if times >= 100 and alive:
front = get_front(head,direction)
alive = ask_alive(front,body)
if alive:
body.append(head)
head = front
body.pop(0)
times = 0
else:
times += 1
食物的运动规律:被吃掉后,随机位置再出现 --向程序靠拢-->> 当head== food为真时 food随机选择一个蛇之外的地方出现
def new_food(head,body):
while 1:
x = random.randint(1, 12)
y = random.randint(1, 16)
if (x, y) != head and (x, y) not in body:
return x, y
这里存在一个隐式BUG,要是蛇充满了每一个角落,那这就是死循环 ...........然后整个程序卡在这里....
版本1 : 我选择没看见,不会有什么人能吃到"全屏"的(´⊙ω⊙`) --来自开发者的懒惰
版本2 【我的选择】: 修了这个BUG
def new_food(head,body):
i = 0
while i < 100:
x = random.randint(1, 12)
y = random.randint(1, 16)
if (x, y) != head and (x, y) not in body:
return (x, y), True
i += 1
else:
return (0, 0), False food, alive = new_food()
100次机会 否则就死( ̄へ ̄) --来自开发者的恶意
另外,蛇吃了食物就要长一格====>>蛇头前进一格,蛇尾不动,蛇就因此长了一格====>>当head = food 为真时body.pop(0)不用执行
if alive:
body.append(head)
head = front
if food == head:
food = new_food(head, body)
else:
body.pop(0)
完结撒花
细节优化
1,
什么?游戏结束了?
黑个屏,提醒下
你的好友【back_color】已上线
back_color一开始等于WHITE , alive为假时变为BLOCK
标题栏变成"游戏结束"
if times >= 100 and alive:
front = get_front(head, direction)
alive = ask_alive(front, body)
if alive:
body.append(head)
head = front
if food == head:
food, alive = new_food(head, body)
else:
body.pop(0)
else:
back_color = BLOCK
pygame.display.set_caption("游戏结束")
times = 0
else:
times += 1
2,

要是一开始按了一下left......
恭喜你获得技能【撞脖子自杀】
要么那就
你的好友【old_direction】已上线
old_direction = "right" def direction_yes_no(direction,old_direction):
d = {"up": "down", "down": "up", "left": "right", "right": "left"}
if d[direction] == old_direction:
return old_direction
return direction
PS : 字典代替if-elif结构省心省时
然后
if times >= 100 and alive:
direction = direction_yes_no(direction, old_direction)
old_direction = direction
front = get_front(head, direction)
alive = ask_alive(front, body)
人话 : 添加一个变量记录上一次有效的输入,两个方向关系不正确时,以老变量为准--向人靠拢-->>向右跑时,不准向左!!!
Q : direction也可以在按键事件处理时限制呀,例如
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
direction = "up"
"""改为"""
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
if direction != "down"
direction = "up"
A:在发现这个方法的BUG之前,我也是这样想的
还是上面的例子

直接按left行不通,right--XX-->left
先按down,迅速再按left ,,right --pass-->> down --pass-->> left
恭喜你获得技能【绕过开发者防护,高水平撞脖子自杀】
3,
Q : 我要暂停!!!
A : 好好玩游戏,不要动不动就暂停
你的好友【pause】已上线
逻辑值,P键控制
pause = False
elif event.key == pygame.K_p:
pause = not pause
if times >= 100 and alive and (not pause):
....
下面我发一下全部的代码,代码包含上面的优化
↓↓↓↓
完结撒花
import pygame
import sys
import random pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("贪吃蛇") fclock = pygame.time.Clock() food = (4, 5)
body = [(1, 1)]
head = (1, 2)
times = 0
direction = "right"
old_direction = "right"
alive = True
pause = False BLOCK = 0, 0, 0
GREEN = 0, 255, 0
RED = 255, 0, 0
BLUE = 0, 0, 255
WHITE = 255, 255, 255
back_color = WHITE def new_draw_rect(zb, color,screen):
pygame.draw.rect(screen,color,((zb[1]-1)*50+1,(zb[0]-1)*50+1,48,48)) def get_front(head, direction):
x, y = head
if direction == "up":
return x-1, y
elif direction == "left":
return x, y-1
elif direction == "down":
return x+1, y
elif direction == "right":
return x, y+1 def ask_alive(front, body):
x, y = front
if x < 0 or x > 12 or y < 0 or y >16 :
return False
if front in body:
return False
return True def new_food(head, body):
i = 0
while i < 100:
x = random.randint(1, 12)
y = random.randint(1, 16)
if (x, y) != head and (x, y) not in body:
return (x, y), True
i += 1
else:
return (0, 0), False def direction_yes_no(direction, old_direction):
d = {"up": "down", "down": "up", "left": "right", "right": "left"}
if d[direction] == old_direction:
return old_direction
return direction #food = new_food(head,body) while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
direction = "up"
elif event.key == pygame.K_LEFT:
direction = "left"
elif event.key == pygame.K_DOWN:
direction = "down"
elif event.key == pygame.K_RIGHT:
direction = "right"
elif event.key == pygame.K_p:
pause = not pause
if times >= 100 and alive and (not pause):
direction = direction_yes_no(direction, old_direction)
old_direction = direction
front = get_front(head, direction)
alive = ask_alive(front, body)
if alive:
body.append(head)
head = front
if food == head:
food, alive = new_food(head, body)
else:
body.pop(0)
else:
back_color = BLOCK
pygame.display.set_caption("游戏结束")
times = 0
else:
times += 1 screen.fill(back_color)
new_draw_rect(food, RED, screen)
for i in body:
new_draw_rect(i, BLUE, screen)
new_draw_rect(head, GREEN, screen) fclock.tick(100)
pygame.display.update()
↑↑↑↑
↑↑↑↑
↑↑↑↑
4,
Q : 游戏结束或者暂停后因为times >= 100 and alive and (not pause)始终为假
times += 1 一直运行,,,,会不会不太妥当
A : 又不会溢出,,,,,,,,取消暂停时蛇能迅速跑起来要是time==100那问题就大了,,幸亏当初留了些余地写成 >=
(~ ̄▽ ̄)~
5,
Q : 蛇跑的太慢我想加速
A : 上面的times>=100的100随便改一下就行
0->100动一动 变成 0->80 动一动
或者动前面的数
0->100动一动 变成 20->100动一动
或者将它设置成变量让它随蛇的长度变化而变化
6,
打字太累,,
direction那里可以使用0,1,2,3
代替 up left down right
键盘事件和get_front()得用同一套词
7,
Q : 第一个food的位置是固定的,不能"动"吗?
A : 因为我是先定义变量,再定义函数
food = new_food(head,body)
就得写在定义函数后面,我不忍心让它一个变量孤单,,,,怕你看不到(写了但注释掉了)
动手术
前面说过
问题1,什么时候动,这里有两个思路,
- 间隔固定时间(1秒),动一次
- 按一次键动一次,无操作一定时间(1秒)后,重复最后一次操作
看起来第一种方案 简单 不错
当初我以为第二种方案很难,,,
写了这个博客后实力大增ᕦ( ᴼ ڡ ᴼ )ᕤ
现在我不怕了
第一个方法,
处理按键事件后times = 100 ,例
if event.key == pygame.K_UP:
direction = "up"
times = 100
elif event.key == pygame.K_LEFT:
direction = "left"
times = 100
while循环当轮就能动一动
隐藏操作 : 在0.01s内按下两个键,第一个按的不会"生效"
pygame : 我得跑完事件列表的每个元素
这是假的第二方案!!!
第二个方法,
把"动一动"的全部代码,打包成函数或者直接写在按键处理的后面
就是上面times = 100的位置 例
if event.key == pygame.K_UP:
direction = "up"
'动一动'
elif event.key == pygame.K_LEFT:
direction = "left"
'动一动'
记住关键的一点times = 0也算"动一动"的内容,第二方案里,按键后计时需要清零!!!
if times >= 100 and alive and (not pause):
direction = direction_yes_no(direction, old_direction)
old_direction = direction
front = get_front(head, direction)
alive = ask_alive(front, body)
if alive:
body.append(head)
head = front
if food == head:
food, alive = new_food(head, body)
else:
body.pop(0)
else:
back_color = BLOCK
pygame.display.set_caption("游戏结束")
times = 0
else:
times += 1
为了防止"诈尸"
"动一动"前还得加上if alive and (not pause):
times没必要加
我就懒得管了(╥╯^╰╥)这篇文章写了太长时间,现在已经看不懂当初的代码了
Q : 不知道你是不是记得我,优化2就是我问的.....是这样的,我对我的方法还不死心...."动手术"改第二方案后能用吗?
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
direction = "up"
"""改为"""
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
if direction != "down"
direction = "up"
A : 对于第一方法实现的第二方案还是会有小毛病
while循环当轮就能动一动
隐藏操作 : 在0.01s内按下两个键,第一个按的不会"生效"
pygame : 我得跑完事件列表的每个元素
在0.01s内,按下两个键,就可以照旧触发【秘技: 撞脖子自杀】
第二方法实现的应该没问题.......
完结撒花✿✿ヽ(°▽°)ノ✿
Q : 我想在加宽屏幕在右侧显示一下时间分数之类的信息,你还有什么"交待"吗?
A : 在pygame显示字体比较有难度....我只能祝你程序不出BUG...另外点(800,0)到点(800,600)别忘了画到线,提醒玩家"边界"还是存在的,让玩家摔键盘动怒就不好了
Q : 我就是对你的颜色搭配有意见,颜色有点扎眼....
A : 我又不是美工,,,,,,,,,,,,颜色搭配的问题,应该,,,,,,,,,应该可以原谅,,,,,,,,,,
Q : 游戏太没挑战性,加点障碍物呗~~
A : walls 会在ask_alive()和new_food()用到,加个if的问题,,,,另外绘制屏幕那里多加个for,,,,,
我就懒得管了(╥╯^╰╥)这篇文章写了太长时间,现在已经看不懂当初的代码了
完结撒花✿✿ヽ(°▽°)ノ✿
这次真没了..✿✿ヽ(°▽°)ノ✿
如有疏漏,欢迎补充
pygame写贪吃蛇的更多相关文章
- 用python+pygame写贪吃蛇小游戏
因为python语法简单好上手,前两天在想能不能用python写个小游戏出来,就上网搜了一下发现了pygame这个写2D游戏的库.了解了两天再参考了一些资料就开始写贪吃蛇这个小游戏. 毕竟最开始的练手 ...
- pygame编写贪吃蛇
一直想用pygame做一个小游戏的,可是因为拖延症的缘故一直没有动,结果那天看到了一个12岁的国际友人小盆友用pygame做的一款塔防游戏,突然感觉已经落后超级远了,所以心血来潮做小游戏了.高中陪伴我 ...
- C语言用面向对象的思想写贪吃蛇
大概一年前这时候,接触C语言一个月,那时候知之甚少,对面向对象只觉”可远观而不可亵玩“,而且会看到很多言论说C语言就是面向过程的语言,C++就是面向对象的语言.不过,不记得什么时候在网上看到过一篇博文 ...
- 用原生Canvas写贪吃蛇及问题解决
为了学习Canvas,写了这个小游戏贪吃蛇供自己和大家学习 Github: https://github.com/zhiyishou/Gsnake Play On: http://zhiyishou. ...
- 用Js写贪吃蛇
使用Javascript做贪吃蛇小游戏, 1.自定义地图宽高,蛇的初始速度 2.食物随机出现 3.蛇的样式属性 4.贪吃蛇玩法(吃食物,碰到边界,吃食物后加速,计分,) <!DOCTYPE ht ...
- 一步步教你怎么用python写贪吃蛇游戏
目录 0 引言 1 环境 2 需求分析 3 代码实现 4 后记 0 引言 前几天,星球有人提到贪吃蛇,一下子就勾起了我的兴趣,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Pytho ...
- javascript写贪吃蛇游戏(20行代码!)
<!doctype html> <html> <body> <canvas id="can" width="400" ...
- 小项目特供 贪吃蛇游戏(基于C语言)
C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次. 是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第 ...
- pygame试水,写一个贪吃蛇
最近学完python基础知识,就想着做一个游戏玩玩,于是就在https://www.pygame.org/docs/学着做了个贪吃蛇游戏. 首先要导入模块. import pygame import ...
随机推荐
- 【深度学习系列】CNN模型的可视化
前面几篇文章讲到了卷积神经网络CNN,但是对于它在每一层提取到的特征以及训练的过程可能还是不太明白,所以这节主要通过模型的可视化来神经网络在每一层中是如何训练的.我们知道,神经网络本身包含了一系列特征 ...
- 二维数组模拟实现酒店管理系统-java
业务分析 1.需要一个房间类,包含房间的属性,比如房间编号.房间类型.是否占用. 2.需要一个旅馆类,旅馆有房间,提供的方法需要有 预订房间.打印房间信息.初始化房间.退房. 3.测试类,测试预订房间 ...
- 微信小程序 PHP后端form表单提交实例详解
微信小程序php后端form表单 https://www.cnblogs.com/tdalcn/p/7092716.html 1.小程序相对于之前的WEB+PHP建站来说,个人理解为只是将web放到了 ...
- 如何让phpmyadmin输入密码再进入
分类: wamp 对于很多不熟悉PHP环境安装的朋友来说,用集成环境可以更快的上手,更方便的搭建PHP的运行环境,但是,WAMP的集成环境仅仅是将底层基础工作做好了,有些个别关键的配置操作并没有集成到 ...
- Hyperledger Fabric Membership Service Providers (MSP)——成员服务
Membership Service Providers (MSP) 本文将介绍有关MSPs的设置和最佳实践的详细方案. Membership Service Providers (MSP)是一个旨在 ...
- 我看过得最易懂的一段AOP的解释
http://blog.csdn.net/zhangliangzi/article/details/51648032 面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ...
- Overload&Override
Overload&Override overload-–重载 方法的重载就是在一个类中,可以定义多个有相同名字,但参数不同的方法.调用时,会根据不同的参数表选择对应的方法. 规 则:两同 ...
- flask_route错误:AttributeError: 'function' object has no attribute 'route'
问题: 路由完全正确,当只有一个名为home的函数处理这个路由时候,下一个路由处理函数,总是提示没有这个rotue属性 Traceback (most recent call last): File ...
- Java为什么需要保留基本数据类型
基本数据类型对以数值计算为主的应用程序来说是必不可少的. 自从1996年Java发布以来,基本数据类型就是Java语言的一部分.John Moore通过对使用基本类型和不使用基本类型做java基准测试 ...
- 汉诺塔python3函数编写和过程分析
!/usr/bin/env python3 -- coding: utf-8 -- 利用递归函数计算阶乘 N! = 1 * 2 * 3 * ... * N def fact(n): if n == 1 ...