很多时候,游戏都是一种可以发泄自己内心情绪的工具,在游戏中,我们可以忘记经历过的很多不快。如今呢,随着软硬件的不断提高,游戏市场越来越繁华红火,很多游戏都动辄好几个G。让人不得不感叹啊,以前那种玩贪吃蛇,俄罗斯方块,是男人就下一百层这些游戏都能玩好久都不腻的时光一去不复返了。
今天呢,就编写了一个独家的贪吃蛇小游戏和大家一起分享曾经的快乐~
来,咱们先上图为敬,给大家看看这个贪吃蛇有多么的有趣!(Boring):
至于其他的功能,各位看官自己弄回家试试吧。
下面就是我准备的代码们
开始之前
运行之前得保证你的电脑上拥有pygame这个包,如果没有的话,用pip下载就行:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame
下载好pygame,那就可以开始我们的小蛇了。
两个必须得创建的类
首先,我们得创建两个类来存储我们的相关信息,这两个类分别用于存放食物和小蛇:
class Food():
def __init__(self,xpos,ypos,iseaten): #位置和是否被吃,iseaten是False时代表被吃了
self.xpos = xpos
self.ypos = ypos
self.iseaten = iseaten
def changestatus(self):
self.iseaten = False
class Snake():
def __init__(self,xpos,ypos,ishead): #ishead用于判断是否是蛇头
self.xpos = xpos
self.ypos = ypos
self.ishead = ishead
为了保证我们代码的可阅读性,在开始之前我们创建一系列的常量用于以后代码的使用
#定义窗口等参数
FPS = 5 #游戏帧数
WIDTH = 600 #窗口宽度
HEIGHT = 500 #窗口高度
GAMEPIX = 20 #游戏像素大小
FOODCOUNT = 5 #食物数量
FONTSIZE = 30
#定义颜色
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
ORANGE = (255,97,0)
GRAY = (125,125,125)
#定义方向
RIGHT = 'right'
LEFT = 'left'
UP = 'up'
DOWN = 'down'
首先,我们得考虑的是将整个游戏背景给画出来,让我们的小蛇能够按照像素格的大小来运动,所以就定义一个函数专门用来画背景图吧
bgdraw(screen)
def bgdraw(screen): #绘制背景函数
pixObj = pygame.PixelArray (screen)
i = GAMEPIX
while i < WIDTH: #画竖线
j = GAMEPIX
while j < (HEIGHT - GAMEPIX):
pixObj[i][j] = BLACK
j += int(GAMEPIX/4)
i += GAMEPIX
i = GAMEPIX
while i < HEIGHT: #画横线
j = GAMEPIX
while j < (WIDTH - GAMEPIX):
pixObj[j][i] = BLACK
j += int(GAMEPIX/4)
i += GAMEPIX
LW = 2 #边框的厚度
# 画边框
pygame.draw.line(screen,BLACK,(GAMEPIX-LW,GAMEPIX-LW),(GAMEPIX-LW,HEIGHT - GAMEPIX),LW)
pygame.draw.line (screen, BLACK, (GAMEPIX-LW, GAMEPIX-LW), (WIDTH - GAMEPIX, GAMEPIX-LW), LW)
pygame.draw.line (screen, BLACK, (WIDTH - GAMEPIX +LW, GAMEPIX), (WIDTH - GAMEPIX +LW, HEIGHT - GAMEPIX), LW)
pygame.draw.line (screen, BLACK, (GAMEPIX, HEIGHT - GAMEPIX + LW), (WIDTH - GAMEPIX, HEIGHT - GAMEPIX + LW), LW)
好了,整个背景能够出来了,接下来呢,我们就可以考虑让小蛇吃的食物能够顺利的出现啦。
食物要怎么出来?首先呢,这就用到我们之前定义的Food类了,我们创建很多很多的Food的实例对象,变成一个个的小食物。如果每次食物都是同一个位置那多无趣呀,所以我们要保证食物的随机出现,就用到了random。
我们将整个窗口当作矩阵,随机给食物横坐标和纵坐标,形成它的(x,y),然后再以(x,y)为食物的左上点画出方框的食物。
所以就有了以下代码:
foodcreate(count,foodlist) 参数是食物总数和用于存储食物的列表
fooddraw(screen,color,foodlist) 参数是游戏窗口,食物颜色和食物列表
def foodcreate(count,foodlist): #随机创建食物位置
for i in range(count):
x = random.randint(1,WIDTH//GAMEPIX-2)
y = random.randint(1,HEIGHT//GAMEPIX-2)
x = x*GAMEPIX
y = y*GAMEPIX
food = Food(x,y,True)
foodlist.append(food)
def fooddraw(screen,color,foodlist): #绘制食物函数
rs = (GAMEPIX-2,GAMEPIX-2)
for f in foodlist:
if f.iseaten: #判断该食物是否被吃了
rp = (f.xpos+1,f.ypos+1)
pygame.draw.rect(screen,color,Rect(rp,rs)) #画出食物方框
这下有背景有食物,就差我们的小蛇了,因为我们整个游戏是在一个while循环里面运行,所以我们得判断各种状态是否满足。
作为一个简单版的贪吃蛇,在这个循环里面我们仅仅只需要判断
是否撞墙,
是否撞到了自己,
是否吃完食物
小蛇飞行方向控制
是否吃了食物
当然,撞墙和吃了自己那肯定就飞Over了~,吃完了所有的食物就达成成就完美结束,实现蛟龙入海,蛇入苍穹的梦想了
所有,对于上面的所有条件,我们都可以用函数进行封装起来。
撞墙和撞到自己,我们可以直接判断蛇头和边界的位置关系,撞到自己,我们可以判断蛇身和蛇头的关系。
总而言之,蛇头很重要!撞不得
所以两个函数就可以直接出来咯:
collisionwall(direction,snakehead) 参数是小蛇飞行方向和小蛇头位置
def collisionwall(direction,snakehead): #判断是否碰撞墙壁
TopBorder = GAMEPIX
DownBorder = HEIGHT - GAMEPIX
RightBorder = WIDTH - GAMEPIX
LeftBorder = GAMEPIX
x = snakehead[0]
y = snakehead[1]
radius = 0.5*GAMEPIX
if direction == RIGHT and (x+radius)==RightBorder:
return False
elif direction == LEFT and (x-radius)==LeftBorder:
return False
elif direction == UP and (y-radius)==TopBorder:
return False
elif direction == DOWN and (y+radius)==DownBorder:
return False
return True
collisionbody(snakelist,snakehead) 参数是小蛇所有身体列表,和蛇头位置
def collisionbody(snakelist,snakehead): #判断是否碰撞身体
x = snakehead[0]
y = snakehead[1]
count = 0
for i in snakelist:
if i.xpos == x and i.ypos == y:
count += 1
if count > 1:
return False
return True
好了,这下就差小蛇自己是否达成目标吃完所有食物了,这我们仅需增加一个SCORE变量用于存放吃了多少食物,当SCORE和FOODCOUNT相等的时候,就胜利了。
接下来,就是判断按键用于控制小蛇往哪跑了,用几个条件语句判断按了什么键之后改变方向就行
for event in pygame.event.get():
if event.type == QUIT: #退出游戏
exit()
elif event.type == KEYUP: #响应按键
if event.key in (K_UP,K_w):
direction = UP
elif event.key in (K_DOWN,K_s):
direction = DOWN
elif event.key in (K_RIGHT,K_d):
direction = RIGHT
elif event.key in (K_LEFT,K_a):
direction = LEFT
现在,就差让小蛇动起来动起来了~~~
怎么动起来呢,我们在画小蛇的时候,就是将snakelist里面所有的小蛇球画到对应位置,如果要小蛇动起来,就得改变snakelist里面的信息。
对此,我们可以将蛇尾最后一个小球在列表中删去,同时加入小蛇新到的位置。
snakedraw(screen,snakelist) 参数是窗口和小蛇列表
def snakedraw(screen,snakelist): #绘制蛇函数,用圆表示蛇
for s in snakelist:
if s.ishead:
pygame.draw.circle(screen,RED,(int(s.xpos),int(s.ypos)),int(0.5*GAMEPIX)) #画蛇头
else:
pygame.draw.circle(screen,GRAY,(int(s.xpos),int(s.ypos)),int(0.5*GAMEPIX-1)) #画蛇身子
snakemove(snakelist,direction) 参数是小蛇列表和移动方向
def snakemove(snakelist,direction): #更新蛇位置
head = (snakelist[0].xpos, snakelist[0].ypos)
s = Snake(snakelist[0].xpos,snakelist[0].ypos,True)
if direction == RIGHT:
s.xpos += GAMEPIX
elif direction == LEFT:
s.xpos -= GAMEPIX
elif direction == DOWN:
s.ypos += GAMEPIX
elif direction == UP:
s.ypos -= GAMEPIX
del snakelist[len(snakelist)-1] #删除最后的球
snakelist.insert(0,s) #插入头
snakelist[1].ishead = False #将原来的头变为身子
return head
完成以上工作,我们就几乎成功一大半了,现在这条小蛇已经能迈开腿了,唯一的不足就是张不开嘴,吃不了东西。
怎么办呢?我们就继续判断小蛇和食物的位置吧,之前食物的类里面不是设立了一个iseaten么,现在它终于派上用场了,当小蛇和食物碰到了时候,将这个食物的iseaten属性变为False就OK了
这当然就算吃下去了,吃下去之后我们再让它长到小蛇的尾巴上就完成了它华丽的蜕变。
eatfood(head,foodlist) 参数是蛇头位置和食物列表
def eatfood(head,foodlist): #吃食物
x = head[0] - 0.5*GAMEPIX
y = head[1] - 0.5*GAMEPIX
eatrange = 0.1*GAMEPIX
for i in foodlist:
if i.iseaten and (i.xpos<=x+eatrange and i.xpos>=x-eatrange) and (i.ypos<=y+eatrange and i.ypos>=y-eatrange):
i.changestatus()
return True
return False
addbody(snakelist) 参数是小蛇列表
def addbody(snakelist): #吃了食物之后增加蛇长度
lenght = len(snakelist)
newx = snakelist[lenght-1].xpos #获取尾巴位置
newy = snakelist[lenght-1].ypos
newbody = Snake(newx,newy,False)
snakelist.append(newbody) #小蛇新长出了身体
最后,就是我们游戏的gameover了,当触发到上面的前两种条件的时候,就触发我们的gameover函数就可以结束这场战斗了。
gameover(screen,text,score) 参数是窗口,输出结束的话,游戏得分
def gameover(screen,text,score): #游戏结束画面
BASICFONT = pygame.font.Font ('freesansbold.ttf', FONTSIZE)
overText = BASICFONT.render (text, True, RED)
scoreText = BASICFONT.render('LENGTH '+str(score+2),True,RED)
scoreObj = scoreText.get_rect()
textObj = overText.get_rect ()
scoreObj.center = (WIDTH / 2, HEIGHT / 2 - FONTSIZE*2)
textObj.center = (WIDTH / 2, HEIGHT / 2)
screen.blit (overText, textObj)
screen.blit(scoreText,scoreObj)
pygame.display.update ()
time.sleep(2)
exit()
OK了,完成了以上的工作,我们就将小蛇打造成了能吃能跑会爬树会打架的小蛟龙了。
剩下的,就是将整个系统再稍微完善一下,给窗口增加一些结束字体了。
最终我们的代码如下(直接运行就可以啦):
import pygame,sys,random,time
from pygame.locals import *
#定义窗口等参数
FPS = 5
WIDTH = 600 #窗口宽度
HEIGHT = 500 #窗口高度
GAMEPIX = 20 #游戏像素大小
FOODCOUNT = 5 #食物数量
FONTSIZE = 30
#定义颜色
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
ORANGE = (255,97,0)
GRAY = (125,125,125)
#定义方向
RIGHT = 'right'
LEFT = 'left'
UP = 'up'
DOWN = 'down'
class Food():
def __init__(self,xpos,ypos,iseaten): #位置和是否被吃,iseaten是False时代表被吃了
self.xpos = xpos
self.ypos = ypos
self.iseaten = iseaten
def changestatus(self):
self.iseaten = False
class Snake():
def __init__(self,xpos,ypos,ishead): #ishead用于判断是否是蛇头
self.xpos = xpos
self.ypos = ypos
self.ishead = ishead
def main():
fpsClock = pygame.time.Clock()
pygame.init()
DISPLAYSURF = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Snake Game~')
DISPLAYSURF.fill(WHITE)
foodlist = []
snakelist = [Snake(3.5*GAMEPIX,1.5*GAMEPIX,True),Snake(2.5*GAMEPIX,1.5*GAMEPIX,False)]#初始化蛇身子
foodcreate(FOODCOUNT,foodlist)
SCORE = 0
BASICFONT = pygame.font.Font ('freesansbold.ttf', GAMEPIX)
direction = RIGHT #初始方向
while True:
DISPLAYSURF.fill(WHITE)
soreText = BASICFONT.render ('SCORE ' + str (SCORE), True, BLACK)
textObj = soreText.get_rect ()
textObj.center = (GAMEPIX*3, GAMEPIX / 2)
DISPLAYSURF.blit(soreText,textObj) #显示得分
bgdraw(DISPLAYSURF) #绘制背景
snakedraw (DISPLAYSURF, snakelist) # 绘制蛇
fooddraw(DISPLAYSURF,ORANGE,foodlist) #绘画食物
if SCORE == FOODCOUNT:
winText = BASICFONT.render('You Win!',True,BLUE)
textObj = winText.get_rect()
textObj.center = (WIDTH/2,HEIGHT/2)
DISPLAYSURF.blit(winText,textObj)
pygame.display.update()
time.sleep(3)
exit()
for event in pygame.event.get():
if event.type == QUIT: #退出游戏
exit()
elif event.type == KEYUP: #响应按键
if event.key in (K_UP,K_w):
direction = UP
elif event.key in (K_DOWN,K_s):
direction = DOWN
elif event.key in (K_RIGHT,K_d):
direction = RIGHT
elif event.key in (K_LEFT,K_a):
direction = LEFT
snakehead = snakemove(snakelist,direction)
if not collisionbody(snakelist,snakehead): #判断是否碰撞身体
gameover(DISPLAYSURF,'COLLISION BODY,OVER~',SCORE)
if eatfood(snakehead,foodlist): #如果吃了食物就增加身体并且使吃了的食物消失
addbody(snakelist)
SCORE += 1
fooddraw(DISPLAYSURF,ORANGE,foodlist)
if not collisionwall(direction,snakehead): #判断是否产生碰撞
gameover(DISPLAYSURF,'COLLISION WALL,OVER~',SCORE)
pygame.display.update()
fpsClock.tick(FPS)
def collisionbody(snakelist,snakehead): #判断是否碰撞身体
x = snakehead[0]
y = snakehead[1]
count = 0
for i in snakelist:
if i.xpos == x and i.ypos == y:
count += 1
if count > 1:
return False
return True
def addbody(snakelist): #吃了食物之后增加蛇长度
lenght = len(snakelist)
newx = snakelist[lenght-1].xpos
newy = snakelist[lenght-1].ypos
newbody = Snake(newx,newy,False)
snakelist.append(newbody)
def eatfood(head,foodlist): #吃食物
x = head[0] - 0.5*GAMEPIX
y = head[1] - 0.5*GAMEPIX
eatrange = 0.1*GAMEPIX
for i in foodlist:
if i.iseaten and (i.xpos<=x+eatrange and i.xpos>=x-eatrange) and (i.ypos<=y+eatrange and i.ypos>=y-eatrange):
i.changestatus()
return True
return False
def gameover(screen,text,score): #游戏结束画面
BASICFONT = pygame.font.Font ('freesansbold.ttf', FONTSIZE)
overText = BASICFONT.render (text, True, RED)
scoreText = BASICFONT.render('LENGTH '+str(score+2),True,RED)
scoreObj = scoreText.get_rect()
textObj = overText.get_rect ()
scoreObj.center = (WIDTH / 2, HEIGHT / 2 - FONTSIZE*2)
textObj.center = (WIDTH / 2, HEIGHT / 2)
screen.blit (overText, textObj)
screen.blit(scoreText,scoreObj)
pygame.display.update ()
time.sleep(2)
exit()
def snakemove(snakelist,direction): #更新蛇位置
head = (snakelist[0].xpos, snakelist[0].ypos)
s = Snake(snakelist[0].xpos,snakelist[0].ypos,True)
if direction == RIGHT:
s.xpos += GAMEPIX
elif direction == LEFT:
s.xpos -= GAMEPIX
elif direction == DOWN:
s.ypos += GAMEPIX
elif direction == UP:
s.ypos -= GAMEPIX
del snakelist[len(snakelist)-1] #删除最后的球
snakelist.insert(0,s) #插入头
snakelist[1].ishead = False #将原来的头变为身子
return head
def collisionwall(direction,snakehead): #判断是否碰撞墙壁
TopBorder = GAMEPIX
DownBorder = HEIGHT - GAMEPIX
RightBorder = WIDTH - GAMEPIX
LeftBorder = GAMEPIX
x = snakehead[0]
y = snakehead[1]
radius = 0.5*GAMEPIX
if direction == RIGHT and (x+radius)==RightBorder:
return False
elif direction == LEFT and (x-radius)==LeftBorder:
return False
elif direction == UP and (y-radius)==TopBorder:
return False
elif direction == DOWN and (y+radius)==DownBorder:
return False
return True
def snakedraw(screen,snakelist): #绘制蛇函数,用圆表示蛇
for s in snakelist:
if s.ishead:
pygame.draw.circle(screen,RED,(int(s.xpos),int(s.ypos)),int(0.5*GAMEPIX)) #画蛇头
else:
pygame.draw.circle(screen,GRAY,(int(s.xpos),int(s.ypos)),int(0.5*GAMEPIX-1)) #画蛇身子
def foodcreate(count,foodlist): #随机创建食物位置
for i in range(count):
x = random.randint(1,WIDTH//GAMEPIX-2)
y = random.randint(1,HEIGHT//GAMEPIX-2)
x = x*GAMEPIX
y = y*GAMEPIX
food = Food(x,y,True)
foodlist.append(food)
def fooddraw(screen,color,foodlist): #绘制食物函数
rs = (GAMEPIX-2,GAMEPIX-2)
for f in foodlist:
if f.iseaten:
rp = (f.xpos+1,f.ypos+1)
pygame.draw.rect(screen,color,Rect(rp,rs))
def bgdraw(screen): #绘制背景函数
pixObj = pygame.PixelArray (screen)
i = GAMEPIX
while i < WIDTH: #画竖线
j = GAMEPIX
while j < (HEIGHT - GAMEPIX):
pixObj[i][j] = BLACK
j += int(GAMEPIX/4)
i += GAMEPIX
i = GAMEPIX
while i < HEIGHT: #画横线
j = GAMEPIX
while j < (WIDTH - GAMEPIX):
pixObj[j][i] = BLACK
j += int(GAMEPIX/4)
i += GAMEPIX
LW = 2
# 画边框
pygame.draw.line(screen,BLACK,(GAMEPIX-LW,GAMEPIX-LW),(GAMEPIX-LW,HEIGHT - GAMEPIX),LW)
pygame.draw.line (screen, BLACK, (GAMEPIX-LW, GAMEPIX-LW), (WIDTH - GAMEPIX, GAMEPIX-LW), LW)
pygame.draw.line (screen, BLACK, (WIDTH - GAMEPIX +LW, GAMEPIX), (WIDTH - GAMEPIX +LW, HEIGHT - GAMEPIX), LW)
pygame.draw.line (screen, BLACK, (GAMEPIX, HEIGHT - GAMEPIX + LW), (WIDTH - GAMEPIX, HEIGHT - GAMEPIX + LW), LW)
if __name__ == '__main__':
main()
嘶嘶嘶~~~小蛇出动~