美文网首页
python-经典贪吃蛇(pygame)

python-经典贪吃蛇(pygame)

作者: 幽泉流霜 | 来源:发表于2018-12-02 13:01 被阅读0次

    自从开始学习编程后 ,就一直想着能够写一个自己的游戏,这大概是每一个男孩子的梦想吧!
    这次我们使用python语言 ,调用pygame库 来写一个简单的小游戏
    就拿最经典的游戏 贪吃蛇 作为我的第一个游戏!
    看一下最终效果为


    tanchishe.gif

    我们先分一下步骤!

    创建背景--写入蛇头食物--蛇的移动以及控制--蛇体算法--碰撞检测--食物随机生成--死亡判定--细节完善--源码

    着急的小伙伴可以直接拉到最后看源码

    先导入要用的库吧

    import pygame
    import random
    from pygame.locals import *
    

    第一个当然是必须要用到的pygame
    随机生成食物点的时候要用到随机库
    最后一个是导入pygame的上下左右键盘输入

    一 、创建背景
    这个是pygame的一个基本用法
    我们先拿一个简单的例子来说

    pygame.init()
    window = pygame.display.set_mode((640,480),0,32)
    pygame.display.set_caption("标题")
    

    pygame.init()用语pygame的初始化
    set_mode函数会返回一个Surface对象,代表了在桌面上出现的那个窗口,三个参数第一个为元祖,代表分 辨率(必须);第二个是一个标志位,具体意思见下表,如果不用什么特性,就指定0;第三个为色深。
    set_caption顾名思义是用来设置标题的
    这时候我们运行程序就会出现一个一闪而过的黑框
    它就是我们的游戏窗体啦

    那么如果要让黑框一直存在怎么办呢
    肯定要用到一个while 循环

    while True:
          pygame.draw.rect(window,bk_color,(0,0,w,h))
          pygame.display.update()
    

    这样子我们的黑框就会一直存在
    draw.rect是pygame带的一个画矩形的函数它一共有四个参数
    第一个是对象 就是你要画的位置
    第二个是颜色 矩形的色彩
    第三个 是一个tuple 代表矩形的位置和大小
    tuple里面一共四个参数 前面两个代表矩形的左上角坐标,后两个分别是宽和高

    但是现在问题又来我们会发现 无法关闭掉这个窗口 或者说卡主

    我们可以先定义一个

    quit  = True
    
    while quit:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit = False
    

    这里我们引入了pygame的事件概念
    pygame.event.gey()可以获取目前的所有事件

    import pygame
    from pygame.locals import *
    from sys import exit
    
    pygame.init()
    SCREEN_SIZE = (640, 480)
    screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
    
    font = pygame.font.SysFont("arial", 16);
    font_height = font.get_linesize()
    event_text = []
    
    while True:
    
        event = pygame.event.wait()
        event_text.append(str(event))
        #获得时间的名称
        event_text = event_text[-SCREEN_SIZE[1]//font_height:]
        #这个切片操作保证了event_text里面只保留一个屏幕的文字
    
        if event.type == QUIT:
            exit()
    
        screen.fill((255, 255, 255))
    
        y = SCREEN_SIZE[1]-font_height
        #找一个合适的起笔位置,最下面开始但是要留一行的空
        for text in reversed(event_text):
            screen.blit( font.render(text, True, (0, 0, 0)), (0, y) )
            #以后会讲
            y-=font_height
            #把笔提一行
    
        pygame.display.update()
    

    运行以上代码 就可以看到我们的所有事件了
    通过上面的说明我们已经可以让一个黑框完整的出现了!

    为了方便
    我们可以把贪吃蛇的背景看成一个表格
    我们先定义了一个600*800的一个分辨率
    然后分别设置行数和列数
    代码如下

    w = 800
    h = 600
    ROW = 30
    COL = 40
    size = (w, h)
    direct = "left" #我们下面会讲
    window = pygame.display.set_mode(size)
    pygame.display.set_caption("贪吃蛇")
    

    二、写入蛇头、食物
    为了方便坐标的表示
    我们写一个简单的坐标类

    class Point:
        row = 0
        col = 0
        def __init__(self,row,col):
            self.row = row
            self.col = col
    

    我们要画一个蛇头 当然要用到draw.rect的函数
    于是需要一个蛇头的颜色和坐标
    食物也同样如此

    head = Point(ROW / 2,COL / 2)
    food = Point(10,10)
    head_color = (0, 128, 128)
    food_color = (255, 255, 0)
    

    然后再在循环中用rect函数把我们的蛇头和食物画上
    运行后效果如图所示


    Snipaste_2018-12-01_19-17-04.png

    三、蛇的移动以及控制
    如何让蛇自动前进
    这里我们先引入一个时间的概念

    clock =pygame.time.Clock()
    clock.tick(20)
    

    当我们把这个clock.tick(20)加入到游戏的主循环后
    就代表的画面每秒刷新20次 也就是20帧的意思
    我们通过这个刷新次数来控制蛇的移动速度
    当然 这是一个比较low的控制方法..
    因为他并不稳定 和你的电脑配置是有关系的

    那么如何让蛇来自动移动呢
    方法很简单
    那就是每次刷新的时候
    如果方向向左那么让它的列数-1
    同理其他方向

        if direct == "left":
            head.col -= 1
        if direct == "down":
            head.row += 1
        if direct == "up":
            head.row -= 1
        if direct == "right":
            head.col += 1
    
    

    那改变蛇头方向怎么做呢
    当我们按下键盘的时候 pygame会检测到这个事件
    用if来判断不同的事件 同时控制direct的方向

            if event.type == pygame.KEYDOWN:
                print(event)
                if event.key == K_LEFT:
                    direct = "left"
                elif event.key == K_RIGHT:
                    direct = "right"
                elif event.key == K_UP:
                    direct = "up"
                elif event.key == K_DOWN:
                    direct = "down"
    

    四、蛇体算法
    我们先假设蛇头和蛇身体的坐标为:
    head=(14,7)
    l=[
    (14,8)
    (14,9)
    (15,9)
    (15,10)
    (15,11)
    (15,12)
    (16,12)
    (17,12)
    ]

    移动:
    1.把原来的头插到前面
    2.把原来的尾删掉

    head'=(14,6)
    l'=[
    (14,7) <-原来的头

    (14,8)
    (14,9)
    (15,9)
    (15,10)
    (15,11)
    (15,12)
    (16,12)
    
    del (17,12)     <-原来的尾
    

    ]
    所以本质上说一共就两步
    1 把蛇头插入到身体的第一个位置
    2 删除尾巴

    还有一个问题 因为我们需要保存原先的蛇头位置
    因为需要再在类中定义一个 copy

    class Point:
        row = 0
        col = 0
        def __init__(self,row,col):
            self.row = row
            self.col = col
        def copy(self):
            return Point(self.row, self.col)
    
    snakes =[Point(head.row,head.col+1),
             Point(head.row, head.col+2),
             Point(head.row, head.col+3)]
    #在循环上方定义一个身体 snake
     snakes.insert(0,head.copy())  #插入旧蛇头
     snakes.pop() #删除蛇尾
    

    到这一步我们就可以简单的控制小蛇移动啦

    五、碰撞检测
    嘛。。听起来很高级的一个名词
    但是我们这里并不需要很复杂的算法
    只要检测蛇头的位置和食物的位置是否一样
    如果一样
    小蛇身体变长且食物新生成
    所以还是很简单的

     eat =(head.row == food.row and head.col == food.col)
        snakes.insert(0,head.copy())
    
        if eat:
            food = food_exist() #食物生成
        else:
            snakes.pop()
    

    六、食物随机生成
    这一步肯定要用到random库拉

    food = Point(random.randint(0,ROW-1),random.randint(0, COL-1))
    

    这一句话就搞定了

    七、死亡判定
    死亡时无非是两种情况
    1吃到墙了
    2吃到自己
    那么我们只要判断蛇头的位置死亡位置就好了

    dead = False
        if (head.row < 0 or head.col< 0 or head.row>ROW or head.col >COL):
            print("你死了")
            dead = True
        for snake in snakes:
            if(head.row == snake.row and head.col == snake.col):
                print("你死了")
                dead = True
        if dead ==True:
            break
    

    在主循环中加入这段代码即可
    先给予dead ==False 假设它一直不死
    一旦玩家触发死亡 dead变为True 并且输出了一个 你死了
    退出循环

    到这里我们的贪吃蛇已经初步完成了 但是还有一些小小的细节需要优化

    八、细节优化
    1.我们的贪吃蛇是可以直接由左边转向右边 由上面转向下面 ,这显然是非常不科学的。
    2.食物随机生成位置可能出现在蛇的身体或者头部上
    先对第一点解决
    为了避免这个问题 我们可以在转向前进行判定 只有在上下方向感才可以进行左右转
    左右转的时候同理

     if event.key == K_LEFT:
                    if (direct == "up" or direct == "down"):
                        direct = "left"
                elif event.key == K_RIGHT:
                    if (direct == "up" or direct == "down"):
                        direct = "right"
                elif event.key == K_UP:
                    if (direct == "right" or direct == "left"):
                        direct = "up"
                elif event.key == K_DOWN:
                    if (direct == "right" or direct == "left"):
                        direct = "down"
    

    第二点
    也就是需要在生成食物前
    进行判定食物的位置和蛇头位置,蛇身体位置
    如果重复,就重新生成

    def food_exist():
        is_in =False
        while True:
            pos = Point(random.randint(0,ROW-1),random.randint(0, COL-1))
            if (pos.row ==head.row and pos.col == head.col):
                is_in =True
            for snake in snakes:
                if(pos.row == snake.row and pos.col == snake.col):
                  is_in = True
            if is_in == False:
                break
        return pos
    

    和死亡判定差不多
    先定义一个is_in =False假设不会生成到蛇头和蛇身体位置
    一旦 ...就重新生成
    好了 到这里 我们简陋的贪吃蛇就全部完成了

    九、源码

    import pygame
    import random
    from pygame.locals import *
    class Point:
        row = 0
        col = 0
        def __init__(self,row,col):
            self.row = row
            self.col = col
        def copy(self):
            return Point(self.row, self.col)
    
    pygame.init()
    
    w = 800
    h = 600
    ROW = 30
    COL = 40
    size = (w, h)
    direct = "left"
    window = pygame.display.set_mode(size)
    pygame.display.set_caption("贪吃蛇")
    
    def rect(point, color):
        w_cell = w/COL
        h_cell = h/ROW
        left = point.col*w_cell
        top = point.row*h_cell
        pygame.draw.rect(window,color,(left,top,w_cell,h_cell))
    
    def food_exist():
        is_in =False
        while True:
            pos = Point(random.randint(0,ROW-1),random.randint(0, COL-1))
            if (pos.row ==head.row and pos.col == head.col):
                is_in =True
            for snake in snakes:
                if(pos.row == snake.row and pos.col == snake.col):
                  is_in = True
            if is_in == False:
                break
        return pos
    
    
    quit = True
    clock =pygame.time.Clock()
    head = Point(ROW / 2,COL / 2)
    snakes =[Point(head.row,head.col+1),
             Point(head.row, head.col+2),
             Point(head.row, head.col+3)]
    food = food_exist()
    head_color = (0, 128, 128)
    food_color = (255, 255, 0)
    bk_color = (255, 255, 255)
    snake_color = (200,200,200)
    
    while quit:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit = False
            elif event.type == pygame.KEYDOWN:
                print(event)
                if event.key == K_LEFT:
                    if (direct == "up" or direct == "down"):
                        direct = "left"
                elif event.key == K_RIGHT:
                    if (direct == "up" or direct == "down"):
                        direct = "right"
                elif event.key == K_UP:
                    if (direct == "right" or direct == "left"):
                        direct = "up"
                elif event.key == K_DOWN:
                    if (direct == "right" or direct == "left"):
                        direct = "down"
        dead = False
        if (head.row < 0 or head.col< 0 or head.row>ROW or head.col >COL):
            print("你死了")
            dead = True
        for snake in snakes:
            if(head.row == snake.row and head.col == snake.col):
                print("你死了")
                dead = True
        if dead ==True:
            break
    
        eat =(head.row == food.row and head.col == food.col)
        snakes.insert(0,head.copy())
    
        if eat:
            food = food_exist()
        else:
            snakes.pop()
    
        if direct == "left":
            head.col -= 1
        if direct == "down":
            head.row += 1
        if direct == "up":
            head.row -= 1
        if direct == "right":
            head.col += 1
        pygame.draw.rect(window,bk_color,(0,0,w,h))
    
        rect(head,head_color)
        rect(food,food_color)
        #生成蛇身体
    
        for snake in snakes:
            rect(snake,snake_color)
        pygame.display.update()
        clock.tick(20)
    

    感谢大家的阅读。
    第一次写游戏
    也参考了网上的一些教程
    有问题欢迎大家讨论纠正

    相关文章

      网友评论

          本文标题:python-经典贪吃蛇(pygame)

          本文链接:https://www.haomeiwen.com/subject/zsfdcqtx.html