【py】python-经典贪吃蛇(pygame)

python-经典贪吃蛇(pygame)

幽泉流霜关注

0.1942018.12.02 13:01:02字数 1,656阅读 1,375

自从开始学习编程后 ,就一直想着能够写一个自己的游戏,这大概是每一个男孩子的梦想吧!
这次我们使用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)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值