云寺手游网:做最专业最放心的安全下载站!
您当前所在位置:首页 > 游戏资讯 >

如何用Python10分钟绘制贪吃蛇小游戏?

时间:2022-03-13 22:00:10 来源:云寺手游网

贪吃蛇是一款经典的益智游戏,有PC和手机等多种版本,既简单又耐玩。玩家通过上下左右键控制蛇的方向,寻找食物,每吃到一次食物,就能得到一定的积分,而且蛇的身体会越来越长。随着蛇的身体变长,游戏的难度就会变大。当蛇碰到四周的墙壁,或者碰到自己的身体的某一个部位的时候,游戏就结束了。

我们来看一下用Python编写这款游戏的主要思路。带上娃一起编程既好玩又增长知识。


地图

我们将整个游戏界面看成是由许多个小方块组成的,每个方块代表一个单位。这样一来,游戏界面就由若干个小方块形成一个地图,地图上的每个位置都可以表示为小方块的整数倍,如图1所示。


图1

贪吃蛇的长度也用这个小方块来表示,每次吃到食物,蛇身的长度就会增加一个单位。

程序界面

这是一款完整的游戏,所以我们一共为其设计了3个界面,除了游戏界面以外,还包括游戏开始界面和游戏结束界面。

自定义函数

我们要创建的函数包括:main(主程序)、startGame(游戏开始)、runGame(运行游戏)、drawFood(绘制食物)、drawSnake(绘制贪吃蛇)、drawScore(绘制成绩)、moveSnake(移动贪吃蛇)、isEattingFood(是否吃到食物)、isAlive(判断贪吃蛇是否死掉了)、gameOver(游戏结束)和terminate(终止游戏)。

事件

我们要用到的事件是键盘事件。键盘事件是玩家操控贪吃蛇移动的时候发生的事件。我们会在后面介绍的startGame()函数和gameOver()函数中监听键盘事件,并且根据事件类型,来做相应的处理。

声音

我们会在游戏开始后,调用Sound对象的play()方法,播放的背景音乐。

导入模块

首先,将在程序中用到的模块都导入。我们要使用pygame的函数,因此需要导入Pygame模块。除此之外,我们还会用到sys模块和random模块。sys模块负责程序与Python解释器的交互,用于操控Python运行时的环境,程序要使用sys模块的exit()函数来退出游戏。random模块用于生成随机数。导入这3个模块的语句如下所示:

初始化设置

为了进行游戏启动和运行前的准备工作,程序需要做一些初始化设置工作,包括定义程序要用到的颜色、方向变量,确定游戏窗口和地图的大小,定义游戏需要用到的一些其他变量等。先来看看这部分初始化代码。

定义颜色变量

游戏中要用到的颜色主要包括如下几种。

定义方向变量

为了能够让玩家能够操控贪吃蛇的方向,我们在程序中定义了4个方向变量,分别和玩家操控贪吃蛇移动的上下左右相对应。

定义窗口大小

我们要为贪吃蛇游戏定义一个窗口,让贪吃蛇在这个窗口中移动。我们通过两个变量来定义窗口的宽和高,这是一个宽800像素、高600像素的矩形窗口。

定义地图大小

我们使用变量mapWidth 和mapHeight 来表示地图的宽和高。需要注意的是,地图的宽和高都是基础单位cellSize的整数倍。

其他变量

程序还会用到如下两个变量:

基础函数

程序最终要以通过函数来定义所要执行的功能,并且通过函数调用来完成和实现这些功能。前面介绍了贪吃蛇这款游戏需要定义的函数,这些函数是游戏程序的核心代码,接下来,我们依次来看看这些函数是如何实现的。

main()函数

main()函数是程序执行的入口。先来看一下main()函数的详细代码。

首先,初始化Pygame,调用pygame.init()函数进行模块初始化。然后调用了pygame.display.set_mode()函数,创建了一个宽800像素、高600像素的显示窗口,返回了用于该窗口的pygame.Surface 对象并将其存储在名为screen的变量中。接下来,调用pygame.display.set_caption()函数来设置窗口的标题,这里给游戏窗口起名为“贪吃蛇”。调用screen.fill()函数,将窗口用白色填充。然后创建了一个时钟(Clock)对象,将其赋值给snakeSpeedClock变量,用它来控制帧速率。

然后,调用startGame这个自定义函数,这是负责启动游戏的函数,给它传递的参数是变量screen,后面的小节会详细介绍这个函数。

接下来,为了保持Pygame事件循环一直运行,我们使用while循环,并且循环条件就是布尔值True,这表示循环会一直进行,而退出这个循环的唯一方式是程序终止。在本书前面的示例中,我们通常都会使用一个变量作为循环条件,当程序退出时,修改这个变量来结束循环。这两种做法的效果是相同的,相比之下,这里的用法要更加简单一些。

在循环体中,我们先初始化混合器,然后将一段背景音乐snake.wav加载到一个Sound对象中,并且将其存储到变量music中。接下来调用play()函数播放音乐,参数−1表示会一直循环播放。通过上述代码,我们就为游戏添加了背景音乐。然后调用runGame函数,传递给它的参数是变量screen和snakeSpeedClock,这个函数负责游戏运行,后面的小节还会详细介绍这个函数。当这个函数执行完后,就会调用music.stop()函数来停止背景音乐播放。然后,调用gameOver函数结束游戏,传递给它的参数是变量screen。

startGame()函数

这个函数负责控制我们的程序启动,它接收的参数是窗口的pygame.Surface对象。我们来看一下该函数的代码。

首先,调用image()函数在Pygame窗口中加载“gameStart.png”图片,并创建一个名为gameStart的Surface对象。然后,调用blit()函数,将像素从一个Surface复制到另一个Surface之上。就是把gameStart对象复制到Screen这个Surface上。通过blit()将gameStart复制到指定位置(70, 30)。

然后,调用pygame.font.SysFont函数来创建Font对象,并将其赋值给名为font的变量,这个对象允许我们以40点的SimHei字体绘制到Pygame的Surface上。接下来,在所创建的font对象上使用render()命令,把字符串“按任意键开始游戏”绘制到Surface上。然后,调用blit()函数,将像素从一个Surface复制到另一个Surface之上。screen.blit(tip, (240, 550))负责把tip对象复制到screen这个Surface上的指定位置。

调用pygame.display.update()函数,把绘制到Surface对象上的所有内容都显示到窗口上。

接下来,为了保持Pygame事件循环一直运行,我们使用while循环。在事件循环中,判断事件类型如果是QUIT(关闭窗口),就调用terminate()函数终止程序,我们稍后会介绍terminate()函数。否则,如果事件类型是KEYDOWN,那么事件对象将有一个key属性来识别按下的是哪个键。如果key属性等于K_ESCAPE,表示用户按下的是Esc键,意味着玩家希望结束程序,那么程序的处理方式和点击关闭窗口一样,调用terminate()函数终止程序。否则,表示用户按下的是其他键,退出这个函数,表示游戏开始运行。

当这个函数执行后,会出现如图1所示的游戏界面,这个时候,玩家可以按Esc键关闭程序,如果按其他的任意键则会开始玩游戏。

runGame()函数

这个函数控制着游戏程序运行,它接受的参数是窗口的pygame.Surface对象和Pygame的时钟对象。runGame()函数的代码如下所示:

首先使用random.randint()函数在3到mapWidth−8之间选取一个随机整数赋值给变量startX,在3到mapHeight−8之间选取一个随机整数赋值给变量startY,这两个变量分别表示贪吃蛇初始的x坐标和y坐标。选取随机数的目的是让贪吃蛇出现的位置不是固定的,这样就增加了游戏的不确定性。

然后,用嵌套字典的一个列表来表示贪吃蛇。每个字典表示地图上的一个坐标,{'x': startX, 'y': startY}表示蛇头的位置,{'x': startX - 1, 'y': startY}和{'x': startX - 2, 'y': startY}表示蛇的身体。这是一条水平放置的蛇,蛇头靠右,有两节蛇身。

将direction设置为RIGHT,表示方向向右。RIGHT是我们前面定义过的方向变量。

然后使用random.randint()函数在0到mapWidth−1之间选取一个随机整数作为字典中x键的值,在0到mapHeight−1之间选取一个随机整数作为字典中y键的值,将字典赋值给变量food。用这个变量表示食物的坐标位置。

接下来,为了保持Pygame事件循环一直运行,我们使用while循环。在事件循环中,判断事件类型,如果是QUIT,那么调用terminate()函数终止程序。否则,如果事件类型是KEYDOWN,那么事件对象将有一个key属性来识别按下的是哪个键。

如果key属性等于K_LEFT,表示用户按下的是向左方向键,并且direction != RIGHT,那么将变量direction设置为LEFT,也就是将方向设置为向左。这里,direction != RIGHT的含义是蛇头不向右,因为蛇头向右的话,是没有办法再将方向设置为向左的(因为要避免贪吃蛇直接掉头导致头部和身体相碰撞的情况),只有蛇头向上、向下或向左的时候,我们才可以将蛇头方向设置为向左。

如果key属性等于K_RIGHT,并且direction != LEFT,那么将变量direction设置为RIGHT;如果key属性等于K_UP,并且direction != DOWN,那么将变量direction设置为UP;如果key属性等于K_DOWN,并且direction !=UP,那么将变量direction设置为DOWN。

如果key属性等于K_ESCAPE,表示用户按下的是Esc 键,意味着玩家希望结束程序,那么处理方式和玩家点击关闭窗口是一样的,调用terminate()函数终止程序。

然后,调用moveSnake()函数移动贪吃蛇,该函数的参数是变量direction和snakeCoords,稍后我们还会详细介绍moveSnake()函数。

接下来调用isEattingFood()函数,判断贪吃蛇是否吃到食物,该函数参数是变量snakeCoords和 food,稍后我们还会详细介绍isEattingFood()函数。

然后调用isAlive()函数,判断贪吃蛇是否死亡,该函数的参数是变量snakeCoords,并且将它的返回结果赋值给变量ret,稍后会详细介绍isAlive()函数。判断变量ret是否是True,如果不是,跳出while循环,表示贪吃蛇已经死了,游戏结束。

调用image.load()函数加载游戏背景图片,并创建一个名为gameRun的Surface对象。调用blit()函数,把gameRun对象复制到screen这个Surface上,指定位置是左上角。

然后调用drawFood()函数绘制食物,该函数的参数是变量screen和food,稍后我们还会介绍drawFood()函数。

然后调用drawSnake()函数绘制贪吃蛇,该函数的参数是变量screen和snakeCoords,稍后我们还会详细介绍drawFood()函数。

接下来调用drawScore()函数绘制分数,该函数的参数是变量screen和len(snakeCoords) – 3的结果。len(snakeCoords)表示贪吃蛇的长度。减去3,是因为贪吃蛇最初有一个蛇头和两节身体,也就是snakeCoords初始有3个元素,减去3就是新增的身体部分,也就是相应的得分。

调用pygame.display.update()函数,把绘制到Surface对象上的所有内容,都显示到窗口上。调用时钟对象的tick()方法,表示游戏运行的帧速率是snakeSpeed FPS,即每秒snakeSpeed次。

当runGame()函数执行后,会出现如图3所示的游戏界面。


图3

下面分别介绍一下runGame()函数中用到的其他的自定义函数。

drawFood()函数

drawFood()函数用来绘制食物,它接受的参数是窗口的pygame.Surface对象和表示坐标的字典对象。drawFood()函数的代码如下所示。

将字典food的键“x”对应的值乘以变量cellSize的结果赋值给变量x,将字典food的键“y”对应的值乘以变量cellSize的结果赋值给变量y。因为food的坐标是相对于地图上的坐标,而不是真正窗口的坐标,只有在乘以cellSize后才能够得到窗口上对应的像素位置。然后,调用pygame.draw.rect()函数绘制用黄色填充的一个小方块。

drawSnake()函数

drawSnake()函数用来绘制贪吃蛇,它接受的参数是窗口的pygame.Surface对象和表示贪吃蛇的列表。drawSnake()函数的代码如下所示。

用一个for循环来遍历snakeCoords列表中所有的元素,把每个元素赋值给变量coord。

在每个循环体中,将字典coord的键“x”对应的值乘以变量cellSize,再把结果赋值给变量x,将字典coord的键“y”对应的值乘以变量cellSize,再把结果赋值给变量y。变量x和y对应的是窗口上的像素位置。然后调用pygame.draw.rect()函数绘制一个用深绿色填充的小方块,再次调用pygame.draw.rect()函数在深绿色方块中绘制一个浅绿色的小方块。一个大方块和一个小方块,一起构成了蛇的一节身体。

drawScore()函数

drawScore()函数用来绘制分数,它接受的参数是窗口的pygame.Surface对象和表示分数的变量。drawScore()函数的代码如下所示。

调用pygame.font.SysFont()函数,把字符串"得分:"以及变量score的值绘制到界面上,以抗锯齿方式绘制,文本颜色为白色,将生成的这个Font对象赋值给变量scoreSurf。然后获取scoreSurf的矩形对象并将其赋值给变量scoreRect。指定scoreRect的左上角的坐标为(windowsWidth - 200, 50)。然后,调用blit()函数,把scoreSurf对象复制到screen这个Surface上。

moveSnake()函数

moveSnake()函数用来移动贪吃蛇,它接受的参数是表示方向的变量和表示贪吃蛇的列表。moveSnake()函数会根据方向,来增加一个蛇头的元素到列表中。moveSnake()函数的代码如下所示。

如果变量direction等于UP,表示贪吃蛇的方向是向上,那么创建一个新的蛇头元素,“x”键的值是原来蛇头的“x”键的值不变,“y”键的值是原来蛇头的“y”键的值减去1个单位。

否则,如果变量direction等于DOWN,表示贪吃蛇的方向是向下,那么创建一个新的蛇头元素,“x”键的值是原来蛇头的“x”键的值不变,“y”键的值是原来蛇头的“y”键的值加上1个单位。

否则,如果变量direction等于LEFT,表示贪吃蛇的方向是向左,那么创建一个新的蛇头元素,“x”键的值是原来蛇头的“x”键的值减去1个单位,“y”键的值是原来蛇头的“y”键的值不变。

否则,如果变量direction等于RIGHT,表示贪吃蛇的方向是向右,那么创建一个新的蛇头元素,“x”键的值是原来蛇头的“x”键的值加上1个单位,“y”键的值是原来蛇头的“y”键的值不变。

然后,把这个新创建的字典元素插入到贪吃蛇列表的第一个位置。

isEattingFood()函数

isEattingFood()函数用来判断贪吃蛇是否吃到了食物,它接受的参数是表示贪吃蛇的列表和表示食物位置的变量。isEattingFood()函数的代码如下所示。

首先判断列表snakeCoords的第一个元素的“x”键和“y”键的值是否等于变量food的“x”键和“y”键的值。变量HEAD等于0。

如果相等,表示蛇头碰到了食物。那么重新设置变量food的“x”键和“y”键的值。请注意,对于列表或字典,在函数内修改参数的内容,会影响到函数之外的对象。

如果不相等,删除snakeCoords列表中最后一个元素。在介绍moveSnake()函数的时候提到过,移动贪吃蛇,其实就是增加一个新的元素。例如,最初是3个元素,向右移动一步,就变成了4个元素。如果这个时候没有吃到食物,那么为了保证元素数量不变,就要删除最后一个元素,这样才能确保snakeCoords列表中的元素数量没有变化,仍然是3个元素。

isAlive()函数

isAive()函数用来判断贪吃蛇是否死亡,它接受的参数是表示贪吃蛇的列表。isAive()函数的代码如下所示。

首先,将变量tag设置为True。然后,判断在地图上的蛇头的x坐标是否等于−1,或者蛇头的x坐标是否等于mapWidth,或者蛇头的y坐标是否等于−1,或者蛇头的y坐标是否等于mapHeight,只要满足其中的任何一个条件,就表示蛇头碰到了墙壁,那么就将变量tag设置为False。

然后用一个for循环,来遍历snakeCoords列表中的第2个元素以及之后的元素,把每个元素赋值给变量snake_body,表示蛇的身体。

在循环体内,判断字典snake_body的键“x”和“y”对应的值是否等于snakeCoords列表第一个元素,也就是蛇头的键“x”和“y”对应的值,如果相等,表示蛇头碰到了蛇的身体,那么就将变量tag设置为False。

最后,该函数返回了变量tag。如果tag是True,表示蛇还活着;如果tag是False,表示蛇死掉了。

gameOver()函数

gameOver()函数控制整个程序的结束,它接受的参数是窗口的pygame.Surface对象。gameOver()函数的代码如下所示。

首先调用screen.fill()函数,用白色填充窗口。

然后调用image()函数在Pygame窗口中加载“gameover.png”图片,并创建一个名为gameOver的Surface对象。然后我们调用blit()函数,把gameOver对象复制到screen这个Surface上。通过blit()将gameOver复制到指定左上角位置(0, 0)。

然后调用pygame.font.SysFont函数,把字符串"按Q或者ESC退出游戏,按其他键重新开始游戏"绘制到界面上。然后我们调用blit()函数把tip对象复制到screen这个Surface上。

调用pygame .display.update()函数,把绘制到Surface对象上的所有内容,都显示到窗口上。

接下来,使用while循环监听键盘事件。在事件循环中,判断事件类型,如果是QUIT,那么调用terminate函数终止程序。否则,如果事件类型是KEYDOWN,那么事件对象将有一个key属性来识别按下的是哪个键。如果key属性等于K_ESCAPE或K_q,表示用户按下的是Esc或Q键,意味着玩家希望结束程序,那么处理方式和点击关闭窗口是一样的,调用terminate()函数终止程序。否则,表示用户按下的是其他键,结束这个函数,重新开始游戏。

当这个函数执行后,会出现如图4所示的游戏界面,这个时候,可以按Q键和Esc键结束程序,也可以按任意键重新开始一局游戏。


图4

terminate()函数

terminate()函数终止程序。我们来看一下该函数的代码。

调用pygame.quit()函数,它是和init()相对应的一个函数。在退出程序之前,需要调用它。然后才会退出Pygame。调用sys.exit()函数,退出主程序退。

调用入口函数

最后,我们只要调用入口函数main(),程序就可以开始运行了。

到这里,我们的贪吃蛇游戏就完成了。这是一个真正意义上的完整游戏,有开始界面和结束界面,有游戏背景,还有背景音乐。尝试着玩一玩,然后再回过头来看看游戏的程序代码,这样会更有助于对代码的理解。

《Python少儿趣味编程》

李强 李若瑜 著


编辑推荐

● 少儿编程畅销图书作者精心编写 。

●全彩印刷, 提供代码和素材下载,方便亲子互动和少儿自学 。

Python简单易学,功能强大,是少儿学习编程的首选语言。本书是少儿学习Python编程的趣味指南,全书共17章,按照由简到难、逐步深入的方式组织各章内容。

本书精心选取内容,注重难易适度和趣味性,语言通俗易懂,代码示例丰富。在多章的末尾,还给出了一些练习题并给出了解答。本书适合想要学习Python编程基础的少儿(尤其是10岁以上的孩子)及想要教孩子学习编程的家长阅读,也适合少儿编程培训班的老师用作少儿编程培训的教材。