python贪吃蛇小游戏制作思路详解

很多时候,游戏都是一种可以发泄自己内心情绪的工具,在游戏中,我们可以忘记经历过的很多不快。如今呢,随着软硬件的不断提高,游戏市场越来越繁华红火,很多游戏都动辄好几个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()

嘶嘶嘶~~~小蛇出动~

发布了21 篇原创文章 · 获赞 41 · 访问量 3105

猜你喜欢

转载自blog.csdn.net/the_sangzi_home/article/details/104622381