小时候就总想着自己能造一个游戏出来,然后给自己全套神装,吊打全服(狂笑)。虽然现在距离能写大型游戏还有亿点点距离,但学了pygame的我终于能写一些简单的游戏了。千里之行始于足下,话不多说,现在开始。(今天是学pygame的第三天,再加上完全是自己想的思路,水平肯定不高,也因为现在电脑性能够高就没考虑性能的问题。有别的问题欢迎在评论区指出来。)
贪吃蛇这个游戏相信绝大多数人都玩过,注意观察的话会发现,只有蛇头和蛇尾在动,身体部分是不动的。由于pygame是不会自动填充背景的,也就是说,只需要让蛇头走过去,就会留下痕迹,再把蛇尾设置为背景色,在一段距离后跟着,中间的部分自然会形成蛇身。就像一个在前面洒水,一个在后面擦掉,留在中间的水不就像是动起来了吗?
想好这条小蛇怎么实现了, 就可以敲代码了。我这里用的是像素当年诺基亚的感觉,屏幕共16*16个格,每个格是边长20像素,然后实例方格(蛇头、蛇尾、蛋)边长为18,每次前进20像素,每隔半秒刷新一次。喜欢连贯流畅的朋友可以设置每次前进1像素然后time.delay()里的参数调低。
from pygame import *
init()
size = 320, 320#屏幕大小
bg = 125, 125, 125#背景的RGB颜色
#进行初始化
screen = display.set_mode(size)
display.set_caption("贪吃蛇")
screen.fill(bg)
body = [[161, 161]]*2#身体的方格个数
speed = [-1, 0]#前进方向,初始向左
position = [161, 161]#初始蛇头位置(方格左上角的绝对坐标)
while True:
#接收用户行为并处理
for eve in event.get():#点击右上角的退出
if eve.type == QUIT:
exit()
if eve.type == KEYDOWN:#从键盘输入
if eve.key == 27:#esc
exit()
if not speed[0]:#这个if和下面那个是防止直接调头操作
if eve.key == K_LEFT:
speed[0] = -1
speed[1] = 0
if eve.key == K_RIGHT:
speed[0] = 1
speed[1] = 0
if not speed[1]:
if eve.key == K_UP:
speed[1] = -1
speed[0] = 0
if eve.key == K_DOWN:
speed[1] = 1
speed[0] = 0
for i in [0, 1]:
position[i] += speed[i] * 20
body = [position[:]] + body[:]#将蛇头的位置添加到身体里
#标记*
draw.rect(screen, (0,0,0), tuple(position) + (18, 18), 0)
draw.rect(screen, (bg), tuple(body[-1]) + (18, 18), 0)
body.pop()
display.flip()
time.delay(500)#延时半秒
效果就是这样
但这时出现问题了:小蛇走出窗口就不见了,但按方向键抢救一下还能回来。原来pygame是支持在窗口外“活动”的,这时坐标为负或者大于窗口的值。我们希望小蛇从左边出就从右边进,所以需要改一下代码
for p in [0, 1]:
if position[p] < 0:
position[p] = 320 + position[p]
if position[p] > 320:
position[p] = position[p] - 320
在上文标记*处插入这些代码就能将小蛇牢牢关在窗口里了。
这个没什么难理解的,import random生成个随机数安排上就行了。值得注意的是不要把蛋正好弄在蛇的身体上,不然会显示异常。
from random import randint
def create_egg():
if len(body) == 195:
pass#这里要写一个结束的函数,后面讲。
while True:
egg = [randint(0,15)*20+1, randint(0,15)*20+1]
for b in body:
if egg == b:
break
else:#如果for循环没有被打断(证明生成的坐标不在蛇身上)会
break
draw.rect(screen, (0,255,255), tuple(egg) + (18, 18), 0)#这里设置的蛋的颜色为蓝绿色
return egg#返回蛋的坐标
在初始化和蛇头吃到蛋的时候创建新的蛋
score = 0
if position == egg:
score += 1
display.set_caption(f"贪吃蛇 分数:{score}")#因为没地方写分数了,直接写到标题栏里
egg = create_egg()
body += [body[-1]]#复制最后一节以达到延长蛇身的目的
到目前为止,我们的游戏还不能结束,这也是游戏十分重要的一环。我用了一个函数把结束的代码封装起来,一个是用在蛇头与蛇身相碰的时候,另一个是在蛇已经布满全屏的时候(就是上面的pass)。
def over():
draw.rect(screen, (255,0,0), tuple(position) + (18, 18), 0)#在结束的方块标红提示
my_font = font.SysFont("microsoft Yahei", 30)#字体是微软雅黑
textSurfaceObj = my_font.render("Game Over!", True, (255,255,255))
screen.blit(textSurfaceObj, (100, 130))
display.flip()
while True:
for eve in event.get():
if eve.type == QUIT:
exit()
time.delay(30)
然后写一个碰撞检测
for b in body:
if position == b:
over()
虽然在监听键盘操作的时候就加入了if判断防止小蛇做出直接调头的操作,但是用户仍然还可以快速按下两个方向键达到相同效果。比如小蛇正在向左前进,用户如果快速按下↑→就可以直接向右走。这显然是不合逻辑的,也会让游戏暴毙,影响体验。所以我设置了一个变量move_flag,判断键盘输入时写为if eve.type == KEYDOWN and move_flag:
。当用户按下键盘的时候move_flag被设置为False锁定,当移动完刷新屏幕的时候设置为True释放,这个问题就解决了。
from pygame import *
from random import randint
def create_egg():
if len(body) == 195:
over()
while True:
egg = [randint(0,15)*20+1, randint(0,15)*20+1]
for b in body:
if egg == b:
break
else:
break
draw.rect(screen, (0,255,255), tuple(egg) + (18, 18), 0)
return egg
def over():
draw.rect(screen, (255,0,0), tuple(position) + (18, 18), 0)
my_font = font.SysFont("microsoft Yahei", 30)
textSurfaceObj = my_font.render("Game Over!", True, (255,255,255))
screen.blit(textSurfaceObj, (100, 130))
display.flip()
while True:
for eve in event.get():
if eve.type == QUIT:
exit()
time.delay(30)
init()
size = 320, 320
bg = 125, 125, 125
screen = display.set_mode(size)
display.set_caption("贪吃蛇")
screen.fill(bg)
body = [[161, 161]]*3
speed = [-1, 0]
position = [161, 161]
move_flag = True
egg = create_egg()
score = 0
while True:
for eve in event.get():
if eve.type == QUIT:
exit()
if eve.type == KEYDOWN and move_flag:
move_flag = False
if eve.key == 27:
exit()
if not speed[0]:
if eve.key == K_LEFT:
speed[0] = -1
speed[1] = 0
if eve.key == K_RIGHT:
speed[0] = 1
speed[1] = 0
if not speed[1]:
if eve.key == K_UP:
speed[1] = -1
speed[0] = 0
if eve.key == K_DOWN:
speed[1] = 1
speed[0] = 0
for i in [0, 1]:
position[i] += speed[i] * 20
for p in [0, 1]:
if position[p] < 0:
position[p] = 320 + position[p]
if position[p] > 320:
position[p] = position[p] - 320
for b in body:
if position == b:
over()
body = [position[:]] + body[:]
if position == egg:
score += 1
display.set_caption(f"贪吃蛇 分数:{score}")
egg = create_egg()
body += [body[-1]]
draw.rect(screen, (0,0,0), tuple(position) + (18, 18), 0)
draw.rect(screen, (bg), tuple(body[-1]) + (18, 18), 0)
body.pop()
move_flag = True
display.flip()
time.delay(500)
不算空行的话数了一下是82行。
目前我想到的有开始画面和暂停功能,日后可能会继续写完,也可能不写了,因为到这里已经实现贪吃蛇的基本功能,再写下去意义不大。
——————————————————分割线——————————————————
首先感谢你能看到这里吧。本人大学生一枚,非计算机相关专业。这是我第二篇博客,可能写得有点烂。以后我会继续写博客跟踪我的学习状态,同时跟大家分享一些我的学习心得。有问题欢迎下方留言!