代码改变世界

Python实战练习_贪吃蛇 (pygame的初次使用)

2019-09-15 18:23  鲁尧尧  阅读(2379)  评论(2编辑  收藏  举报

正如标题所写的那样,我将一步步的完成本次实战练习——贪吃蛇。废话不多说,感兴趣的伙伴可以一同挑战一下。

首先说明本次实战中我的配备:

开发环境:python 3.7;

开发工具:pycharm2019.2.2;

开发模块:pygame;

首先在本次开发中,我们要借用一下pygame模块,那么问题来了,什么是pygame模块?有什么用?

pygame 是包含图像、声音建立在SDL基础上,允许实时电子游戏研发而无需被低级语言(如机器语言和汇编语言)束缚。

基于这样一个设想,所有需要的游戏功能和理念都(主要是图像方面)都完全简化为游戏逻辑本身,所有的资源结构都可以由高级语言提供,如Python。

我个人理解 pygame是一个简化2d游戏开发的开发模块。

本次开发的第一步便是装载pygame,具体方法如下

1.通过在python文件中 打开命令提示 输入对应代码

 

2.pycharm软件 可以通过设置内,添加新模块

确保安装完成可以在命令提示符中打开python  输入 import pygame检测 如下所示

有需要的话,可以通过留言,我在下一期博客中专门做一个pygame安装教程。

接下来便是我们的开发之旅:

首先展示一下我们的效果图,由一个个大小为20*20的方块儿组成的贪吃蛇游戏

1.调用python开发环境,包括pygame、sys、random。

# python 运行时的环境
import pygame, sys, random

 

2.调用pygame相关常量、还有配置颜色。

# 这个模块包含所有pygame所使用的常量
from pygame.locals import *
# 定义颜色变量
# 目标方块儿红颜色
redColor = pygame.Color(255, 0, 0)
# 背景颜色为黑色
blackColor = pygame.Color(0, 0, 0)
# 蛇颜色为白色
whiteColor = pygame.Color(255, 255, 255)

 

3.定义游戏结束的函数  主要包含两部分操作,第一部分为pygame结束,第二部分为sys结束。

# 定义游戏结束
def gameOver():
    pygame.quit()
    sys.exit()

 

4.有了前面的铺垫,我们可以正式开始设计我们的游戏,我将游戏内容存放到main函数中。

4.1初始化pygame,设定变量来控制蛇的速度,然后制作一个界面,界面的大小为640*480,界面标题为贪吃蛇。

其中界面大小是必须设定的,贪吃蛇游戏的规则之一便是不能触及边界,但界面的标题是可以不写的,界面标题默认为python文件名

特别注意pygame.display.set_mode()函数中需要插入为两项序列而不是整数型  所以参数应为(640, 480)

具体代码如下

def main():
    # 初始化pygame
    pygame.init()
    # 定义一个变量 控制速度
    fpsClock = pygame.time.Clock()
    # 创建一个窗口界面,界面大小为640*480
    playSurface = pygame.display.set_mode((640, 480))
# 界面标题为“贪吃蛇” 可不写,默认为python文件名 pygame.display.set_caption(
'贪吃蛇')

 

4.2设定目标方块

目标方块是由计算机随机生成的,但在那之前需要我们手动设定一个初值,同时设定一个target_flag来判断目标方块是否被贪吃蛇 “吃” 掉

    # 初始化目标方块的位置
    targetPosition = [300, 300]
    # 目标方块标记 判断贪吃蛇是否吃掉目标方块 1为没吃掉 0为吃掉
    targetFlag = 1

 

4.3设定贪吃蛇的相关属性

初始化蛇需要注意什么?首先蛇的位置,其次蛇的长度,最后便是蛇的方向。那么一步步来实现

我们可以将蛇看做两部分组成组成,蛇头和蛇身。蛇身只要不断跟随蛇头便可,蛇头需要用来判断贪吃蛇的前进方向、是否结束游戏、是否 “吃” 掉目标方块儿等等

我们设定蛇头以(100, 100)为基准,蛇为水平方向放置,并且向右前进,所以蛇身的第一部分 应等于蛇头的’“Y”值,而“X”应当减去一个方块儿的位置,也就是减少20,贪吃蛇第一块应当是(80, 100)

设定存在方向的变量direction,初始方向向右,也需要设定改变方向的变量changedirection,令changedirection == direction

所写代码如下

    # 初始化贪吃蛇的位置 (100,100)为基准
    # 初始化贪吃蛇长度 列表中有几个元素 就有几个身体
    snake_head = [100, 100]
    snake_body = [[80, 100], [60, 100]]


    # 初始化方向 默认为右
    direction = 'right'
    # 定义一个认为控制的方向的变量
    changedirection = direction

 

4.4需要贪吃蛇动起来

特别说明 pygame的所有事件全都在一个实时循环中

由于操作比较简单,鼠标操作的便只有一个,点击右上角的叉叉关闭游戏,但键盘上的操作相对比较多 包括↑↓←→,控制蛇的移动,esc退出游戏

所以首先要判断发生的事件类型是点击QUIT ,又或者是KEYDOWN

QUIT不必说,游戏结束之前上面也有描述 pygame.quit() 与sys.exit()

KEYDOWN 就需要判断是上下左右的哪一个,发生按键要使得蛇的移动方向发生改变,所以changedirection 需要发生变动,更改为按键方向。

 

# pygame 所有事件全部放到一个实时循环中
    while True:
        # 从队列中获取事件
        for event in pygame.event.get():

            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN:
                if event.key == K_RIGHT:
                    changedirection = 'right'
                if event.key == K_LEFT:
                    changedirection = 'left'
                if event.key == K_UP:
                    changedirection = 'up'
                if event.key == K_DOWN:
                    changedirection = 'down'
                if event.key == K_ESCAPE:
                    pygame.event.post(pygame.event.Event(QUIT))

 

蛇的移动方向由direction决定,按键改变的是changedirection,所以我们需要将changedirection的值赋予direction

众所周知,贪吃蛇是不可以走回头路的,也就是说当贪吃蛇向右移动,按左键是没哟效果的

所以在将changedirection的值赋予direction 之前 还需要判断两个值是否相反

 

# 确定方向
        if changedirection == 'left' and not direction == 'right':
            direction = changedirection
        if changedirection == 'right' and not direction == 'left':
            direction = changedirection
        if changedirection == 'up' and not direction == 'down':
            direction = changedirection
        if changedirection == 'down' and not direction == 'up':
            direction = changedirection

 

 

 

4.5贪吃蛇转弯

就像上文提到的那样,蛇身是跟着蛇头运动,所以转弯实际上是改变蛇头的位置。

将蛇身与蛇头分开来写相对来说这时候比较占优势,只要改变蛇头snake_head的位置信息就可以。

并不是说蛇头和蛇身必须分开写,也可以放在一起,我们只需要多写一步 提取蛇的第一个方块信息就可以了

那么问题来了,怎么改变蛇头位置信息才能达到转弯的效果?

我们以蛇在向右移动,这时按↓键为例

蛇头这时候应当向下移动一个方块的位置,也就是说这时候蛇头的“X”不发生改变,“Y”值增加一个方块的数值,也就是20

同理可得蛇头向上等操作的方法,注意snake_head是一个数组 ,snake_head[0]即为“X”  snake_head[1]即为“Y”

代码如下

# 根据方向移动蛇头
        if direction == 'right':
            snake_head[0] += 20
        if direction == 'left':
            snake_head[0] -= 20
        if direction == 'up':
            snake_head[1] -= 20
        if direction == 'down':
            snake_head[1] += 20

 

4.5贪吃蛇吃方块

贪吃蛇游戏的目的便是控制贪吃蛇“吃”掉方块儿,那么怎样来实现呢?

首先蛇的嘴巴长在蛇头上,所以蛇吃掉方块需要用到蛇头的位置信息。

其次目标方块儿存放在指定位置,所以当蛇头的位置信息等于目标方块儿的位置信息即可

位置信息包含x与y,所以当蛇头x=目标方块儿x,并且蛇头y=目标方块儿y时,我们便认定贪吃蛇吃了方块

之前设定判断目标方块的状态变量 targetflag便起到作用,1表示没吃,0表示吃了,所以targetflag从1变成了0

# 如果贪吃蛇位置和目标方块位置重合
        if snake_head[0] == targetPosition[0] and 
        snake_head[1] == targetPosition[1]:
            
            targetFlag = 0

        else:
            snake_body.pop()            

 

4.6重新生成新的目标方块

当判断目标方块儿被吃了,也就是说targetflag=0时,系统需要重新生成一个新的目标方块。

目标方块的设定相对来说比较简单,只需要生成一个新的位置信息并将targetflag=1即可。

目标方块将会出现在一个随机的位置,所以我们在这里需要引入一个随机数,这也是为什么刚开始开发游戏时,需要import random

由于一个方块的大小为20*20,游戏界面为640*480,所以x的取值范围只有(20/20, 640/20)即为(1, 32)

同理可得y的取值范围应当是(1,24)  所以随机生成的目标方块儿位置信息应当为(x*20, y*20)

输入的位置信息应当为整数类型,所以还需要加强转,代码如下

     if targetFlag == 0:
            x = random.randrange(1, 32)
            y = random.randrange(1, 24)
            targetPosition = [int(x * 20), int(y * 20)]
            targetFlag = 1

 

5.界面生成

写到这里,游戏的规则基本完成,接下来我们需要“画”出我们的界面

首先给自己的游戏界面填充为黑色,借用playSurface.fill()函数,将黑色的参数传入

之后就是画我们的贪吃蛇,用for循环画每一部分的身子,利用pygame中draw所带的Rect函数来绘画方块   rect函数需要传四个参数

第一个为surface,需要制定一个surface编辑区域

第二个为color,即绘画的颜色,这个很好理解

第三个为rect,rect为绘画矩形,需要提供矩形的位置,长度,宽度

第四个为width,这并不代表宽度,而是特指线条的粗细, width=0实心,width=1空心 ,一般可以不传入,默认为0

然后便是画目标方块,原理如上所示,但不需要用到for循环,毕竟就一个目标方块儿

最后开启实时更新界面

 # 填充背景颜色
        playSurface.fill(blackColor)

        for position in snake_body:
            # rect函数内
            # 第一个参数surface  指定一个surface编辑区
            # 第二个参数color    指定颜色
            # 第三个参数rect     返回一个矩形包含位置信息(x,y),(width,height)
            # 第四个参数width    表示线条的粗细 width=0 实心  width=1 空心
            # 画蛇
            pygame.draw.rect(playSurface, whiteColor, Rect(position[0], position[1], 20, 20))
            # 画目标方块儿
            pygame.draw.rect(playSurface, redColor, Rect(targetPosition[0], targetPosition[1], 20, 20))

        # 更新显示到屏幕
        pygame.display.flip()

 

6.游戏结束条件

当贪吃蛇撞击到边界时便会触发游戏结束,也就是蛇头的位置超过了预定的640*480的界面,这时候游戏结束

# 判断游戏结束
        if snake_head[0] > 620 or snake_head[0] < 0:
            gameOver()
        if snake_head[1] > 460 or snake_head[1] < 0:
            gameOver()

 

7.调控难度

调控难度主要是更改贪吃蛇的速度,之前有设定贪吃蛇速度的变量,这时候我们只要加以修改即可,个人比较手残,开三挡即可,有兴趣的小伙伴可以尝试下更高的难度

# 控制游戏速度
        fpsClock.tick(3)

 

8.开启游戏,启动main函数

# 启动入口 main函数
if __name__ == '__main__':
    main()

 

至此就开发完成了一个简单的像素贪吃蛇,下面便是游戏最终效果

由于种种原因,最后有些地方没来得及改进,所以也算给诸位留下一些难题。

过段时间我会重新更新更加完善、更加丰富的贪吃蛇项目

改进方案:

1.可以加入分数体系,在每次吃到目标方块儿后分数加一,最终在结束后呈现给玩家

2.贪吃蛇游戏结束条件:蛇头碰到自己身体,游戏结束未实现

3.界面过于简单,可以制作一个简单的游戏开始界面,或者在游戏结束后创建一个排名榜,重来一次等

4.部分代码跨度较大 显得冗长,建议精简重新构建框架

最后显示源代码,以供各位参考

# python 运行时的环境
import pygame, sys, random

# 这个模块包含所有pygame所使用的常量
from pygame.locals import *

# 定义颜色变量

# 目标方块儿红颜色
redColor = pygame.Color(255, 0, 0)
# 背景颜色为黑色
blackColor = pygame.Color(0, 0, 0)
# 蛇颜色为白色
whiteColor = pygame.Color(255, 255, 255)


# 定义游戏结束
def gameOver():
    pygame.quit()
    sys.exit()


# 定义main函数 定义入口函数
def main():
    # 初始化pygame
    pygame.init()
    # 定义一个变量 控制速度
    fpsClock = pygame.time.Clock()
    # 创建一个窗口界面
    playSurface = pygame.display.set_mode((640, 480))
    pygame.display.set_caption('贪吃蛇')

    # 初始化目标方块的位置
    targetPosition = [300, 300]
    # 目标方块标记 判断贪吃蛇是否吃掉目标方块 1为没吃掉 0为吃掉
    targetFlag = 1


    # 初始化贪吃蛇的位置 (100,100)为基准
    # 初始化贪吃蛇长度 列表中有几个元素 就有几个身体
    snake_head = [100, 100]
    snake_body = [[80, 100], [60, 100]]


    # 初始化方向 默认为右
    direction = 'right'
    # 定义一个认为控制的方向的变量
    changedirection = direction

    # pygame 所有事件全部放到一个实时循环中
    while True:
        # 从队列中获取事件
        for event in pygame.event.get():

            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN:
                if event.key == K_RIGHT:
                    changedirection = 'right'
                if event.key == K_LEFT:
                    changedirection = 'left'
                if event.key == K_UP:
                    changedirection = 'up'
                if event.key == K_DOWN:
                    changedirection = 'down'
                if event.key == K_ESCAPE:
                    pygame.event.post(pygame.event.Event(QUIT))
        # 确定方向
        if changedirection == 'left' and not direction == 'right':
            direction = changedirection
        if changedirection == 'right' and not direction == 'left':
            direction = changedirection
        if changedirection == 'up' and not direction == 'down':
            direction = changedirection
        if changedirection == 'down' and not direction == 'up':
            direction = changedirection

        # 根据方向移动蛇头
        if direction == 'right':
            snake_head[0] += 20
        if direction == 'left':
            snake_head[0] -= 20
        if direction == 'up':
            snake_head[1] -= 20
        if direction == 'down':
            snake_head[1] += 20

        # 增加蛇的长度
        snake_body.insert(0, list(snake_head))

        # 如果贪吃蛇位置和目标方块位置重合
        if snake_head[0] == targetPosition[0] and snake_head[1] == targetPosition[1]:
            targetFlag = 0

        else:
            snake_body.pop()

        if targetFlag == 0:
            x = random.randrange(1, 32)
            y = random.randrange(1, 24)
            targetPosition = [int(x * 20), int(y * 20)]
            targetFlag = 1

        # 填充背景颜色
        playSurface.fill(blackColor)

        for position in snake_body:
            # rect函数内
            # 第一个参数surface  指定一个surface编辑区
            # 第二个参数color    指定颜色
            # 第三个参数rect     返回一个矩形包含位置信息(x,y),(width,height)
            # 第四个参数width    表示线条的粗细 width=0 实心  width=1 空心
            # 画蛇
            pygame.draw.rect(playSurface, whiteColor, Rect(position[0], position[1], 20, 20))
            # 画目标方块儿
            pygame.draw.rect(playSurface, redColor, Rect(targetPosition[0], targetPosition[1], 20, 20))

        # 更新显示到屏幕
        pygame.display.flip()

        # 判断游戏结束
        if snake_head[0] > 620 or snake_head[0] < 0:
            gameOver()
        if snake_head[1] > 460 or snake_head[1] < 0:
            gameOver()
        # 控制游戏速度
        fpsClock.tick(3)


# 启动入口 main函数
if __name__ == '__main__':
    main()