TNBLOG
首页
博客
视频
资源
问答
猿趣
手机
关于
搜索
收藏
便签
笔记
消息
创作
登录
剑轩
人生没有办法假设,我们不能站在后来的角度去责备当时无望、甚至是怯懦、犹豫的自己
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
CSS
21篇
微服务
41篇
Git
14篇
.NET
102篇
移动开发
32篇
软件架构
23篇
.NET Core
120篇
.NET MVC
11篇
英语
3篇
随笔
115篇
Bootstrap
3篇
Redis
22篇
编辑器
10篇
Js相关
15篇
虚拟化
8篇
更多
Oracle
7篇
Python
14篇
数据库
26篇
EF
17篇
微信
3篇
前端
151篇
消息队列
7篇
docker
39篇
多线程
1篇
Java
4篇
软件基础
2篇
C++
2篇
WCF
7篇
Linux
7篇
nginx
5篇
K8S
9篇
ABP
2篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2023
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术
原
python游戏开发库pygame。pygame实现贪吃蛇
5006
人阅读
2022/7/25 14:06
总访问:
3336480
评论:
0
收藏:
0
手机
分类:
Python
 [TOC] ### pygame介绍 Pygame 是一个专门用来开发游戏的 Python 模块,主要为开发、设计 2D 电子游戏而生,它是一个免费、开源的第三方软件包,支持多种操作系统,具有良好的跨平台性(比如 Windows、Linux、Mac 等)。Pygame 是 Pete Shinners 在 SDL(Simple DirectMedia Layer,一套开源的跨平台多媒体开发库)基础上开发而来,其目的是取代 PySDL。 tn2> 截止到 2020 年 10 月 28 日,Pygame 已经诞生 20 周年。 SDL 是一套开放源代码的跨平台多媒体开发库,使用 C语言编写,它提供了多种控制图像、声音、输入/输出的函数,Pygame 可以看做是对 SDL 的封装,在 SDL 库基础上提供了各种 Python 的 API接口。目前 SDL 主要用于多媒体领域,比如开发游戏、模拟器、媒体播放器等。 通过 Pygame 我们能够创建各种各样的游戏和多媒体程序,但相比于开发大型 3D 游戏来说,它更擅长与开发 2D 游戏,比如扫雷、纸牌游戏、贪吃蛇、超级马里奥、飞机大战等,如果是 3D 游戏,可以选择一些功能更为全面的 Python 游戏开发库,比如 Panda3D(迪士尼开发的3D游戏引擎),PyOgre(Ogre 3D渲染引擎)等。 Python 作为一门解释型语言并不适合开发大型的 3D 游戏,但 Python 通过对其他语言的接口封装,使自身具备了开发大型 3D 游戏的能力,例如 Panda3D 的底层是用 C++ 语言编写的。一些较为知名的 3D 游戏,比如魔兽世界、文明帝国4、战地风云2,这些游戏都是使用 Python 语言开发的,而国内较为知名的“阴阳师”手游,也是由 Python 语言开发而成。 Pygame 官方网站(https://www.pygame.org/tags/all)提供许多丰富的游戏案例,它们全部使用 Pygame 开发 ### pygame安装 ``` pip install pygame ``` 安装完成后使用命令查看版本 ``` python -m pygame --version ``` 或者,输入:python -m pygame.examples.aliens,若能弹出游戏页面,则安装成功 python的常用对象 ``` pygame.display 显示 pygame.time 时间 pygame.event 事件 pygame.draw 绘制 ``` ## pygame实现贪吃蛇 ### 一:能打开游戏窗体,点击x退出 ``` ## 引入所需要的模块 import sys import pygame ## 使用pygame之前必须初始化 pygame.init() ## 定义一个宽高 width = 800 height = 600 size = (width,height) # 设置主屏窗口大小 screen = pygame.display.set_mode(size) # 设置窗口的标题,即游戏名称 pygame.display.set_caption('hello world') # 固定代码段,实现点击"X"号退出界面的功能,几乎所有的pygame都会使用该段代码 while True: # 循环获取事件,监听事件状态 for event in pygame.event.get(): # 判断用户是否点了"X"关闭按钮,并执行if代码段 if event.type == pygame.QUIT: #卸载所有模块 pygame.quit() #终止程序,确保退出程序 sys.exit() pygame.display.flip() #更新屏幕内容 ``` 默认就是一个黑色的窗体:  ### 二:可以设置一下帧率,降低一下循环刷新的频率 不设置的话它是没有间隔的一直刷新 ``` ## 获取pygame的Clock对象 clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() pygame.display.flip() ## 设置一下帧率 clock.tick(60) ``` ### 三:绘制背景,让窗体变成白色背景 tn2>绘制背景,让窗体变成白色背景。其实就是画一个矩形覆盖整个窗体即可。其实就一句话 pygame.draw.rect(screen,(255,255,255),(0,0,width,height)) - 参数1:需要绘制到的窗体对象 - 参数2:绘制的颜色 - 参数3:矩形的坐标,左上坐标-长-宽 **主要代码如下:** ``` # 设置主屏窗口大小 screen = pygame.display.set_mode(size) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() ## 绘制背景,让窗体变成白色背景。其实就是画一个矩形覆盖整个窗体即可 pygame.draw.rect(screen,(255,255,255),(0,0,width,height)) pygame.display.flip() clock.tick(60) ``` ### 四:绘制格子,也就是绘制蛇身,食物等 tn2>贪吃蛇的地图很简单,我们可以看成一个一个的格子,比如蛇身是一个一个的格子,食物也是一个一个的格子,障碍物也可以是一个一个的格子。格子的多少也就是地图可以操控空间有多少。 绘制一个任意大小的格子,其实也是使用draw.rect方法比如绘制一个正方形格子 ``` ## 绘制一个起始坐标点为0,0,宽度为50的正方形 pygame.draw.rect(screen,(226, 163, 29),(0,0,50,50)) ```  如果想要长一点,修改一下长宽即可,如下 ``` ## 绘制一个起始坐标点为0,0,宽度为130,高度为30的矩形 pygame.draw.rect(screen,(226, 163, 29),(0,0,130,30)) ``` 颜色选取的话,可以参考这个网站 https://htmlcolorcodes.com/zh/yanse-xuanze-qi/ #### 上面我们是直接写死位置以及写死高宽的,实际情况下,这些都应该动态计算得来。我们先动态的画出来定义任意位置的格子 比如我定义地图的宽度是600*400,最开始也是定义了的 ``` ## 定义地图的宽度是600*400 width = 600 height = 400 ``` 我们定义地图的行列数是10*15,注意为了保证我们我们每个格子是一个正方形,我们行列数要按照同一个比例来。当然如果你的游戏格子不是正方形可以不管这个。 ``` ## 定义地图的行列数是10*15 row = 10 col = 15 ``` 比如我们蛇头的位置是在5行7列,或者修改成其他任意合理的参数,怎么根据这些变量画出来蛇头的位置呢 ``` ## 定义蛇头的位置 snake_head_row = 5 snake_head_col = 7 ``` 其实很简单,首先蛇头所在格子的宽高,就是等于每个格子的宽度,我们先计算出来 ``` ## 格子的高度 = 地图的高度/行数 boxheight = height/row ## 格子的宽度 = 地图的高度/列数 boxwidth = width/col ``` 然后宽高有了,就是起始的位置坐标了,也很简单。左边的距离就是列等于列宽*所在列,距离上边的位置=行高*所在行。然后画出来即可 ``` ## 格子的高度 = 地图的高度/行数 boxheight = height/row ## 格子的宽度 = 地图的高度/列数 boxwidth = width/col ## 定义蛇头的位置 snake_head_row = 5 snake_head_col = 7 ## 距离左边的位置=列宽*所在列 snake_head_left = snake_head_col*boxwidth ## 距离上边的位置=行高*所在行 snake_head_top = snake_head_row * boxheight ## 根据定义的行列位置画出来格子所在的位置 pygame.draw.rect(screen,(226, 163, 29),(snake_head_left,snake_head_top,boxwidth,boxheight)) ``` 蛇头位置定义在第5行,第7列的效果如下:  蛇头位置定义在5行,0列的位置如下:  完全没有问题,现在我们就可以动态的根据行列来了。 #### 封装一个类表示格子的位置 上面格子的位置,也就是所在的行列,很多元素都需要使用到它,为了重复使用,也为了方便管理,最好封装一下,比如封装成一个类,更方便操作一点。很简单的一个类,就一个行列属性,然后一个初始方法用于赋值。 ``` ## 定义一个类表示格式的位置 class Point: row = 0 col = 0 def __init__(self,row,col) : self.row = row self.col = col ``` 然后蛇头的位置我们就可以这样定义,比如定义一个大概中间的位置 ``` ## 定义蛇头的坐标位置 head = Point(int(row/2),int(col/2)) ``` 计算位置的时候,就可以使用对象的方式 ``` ## 距离左边的位置=列宽*所在列 snake_head_left = head.col*boxwidth ## 距离上边的位置=行高*所在行 snake_head_top = head.row * boxheight ## 根据定义的行列位置画出来格子所在的位置 pygame.draw.rect(screen,(226, 163, 29),(snake_head_left,snake_head_top,boxwidth,boxheight)) ``` #### 封装一个画格子的方法 因为画格子也是一个需要重复使用的方法,所以我们需要封装一下。方法也很简单就是把前面写的计算格子宽高,根据格子行列计算距离左边与上边的距离等提到一个方法中。其实计算格子宽高的可以单独提出来写,我们暂时把这个放到这里吧。 tn2>方法有两个参数一个是坐标也就是格子所在的行列,还有一个是颜色 ``` def drawRect(point,color): ## 格子的高度 = 地图的高度/行数 boxheight = height/row ## 格子的宽度 = 地图的高度/列数 boxwidth = width/col ## 距离左边的位置=列宽*所在列 box_head_left = point.col*boxwidth ## 距离上边的位置=行高*所在行 box_head_top = point.row * boxheight ## 根据定义的行列位置画出来格子所在的位置 pygame.draw.rect(screen,color,(box_head_left,box_head_top,boxwidth,boxheight)) ``` 现在我们要画格子就可以直接调用方法了。(蛇的颜色最好单独定义一个变量,后面要换成的时候随时可以换,这里为了演示没有使用变量) ``` ## 调用封装方法绘制蛇身 drawRect(headPoint,(226, 163, 29)) ``` 现在如果我们要在画一个其他元素,比如食物,就非常简单了,定义一下食物出现的位置和颜色,在调用一次方法就行。核心代码如下: ``` ## 定义食物格子的位置 foodPoint = Point(3,3) ## 定义一个食物格子的颜色 foodColor = (226, 64, 29) ## 调用封装方法绘制食物 drawRect(foodPoint,foodColor) ```  ### 五:控制蛇的移动 #### 让蛇可以自己移动 要让蛇移动非常简单,比如要让蛇左边这一步其实就是移动一格,就是让蛇头的位置让左边移动一格,也就是让蛇头的对象的列让左边-1就行了。向右移动就是让列+1,同理上下移动就是对行的加减。 先定义一个默认移动的方向,比如向左边吧。 ``` snakeDirect = "left" ``` 然后封装一个蛇移动的方法 ``` ## 封装一个蛇移动的方法 def snakeRun(snakeDirect): ## 向左边移动就是让列减去1即可。其他同理 if snakeDirect == "left": headPoint.col -=1 if snakeDirect == "right": headPoint.col +=1 if snakeDirect == "up": headPoint.row -=1 if snakeDirect == "down": headPoint.row +=1 ``` 然后在主循环里边调用一下即可 ``` while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() ## 让地图是白色背景 pygame.draw.rect(screen,(255,255,255),(0,0,width,height)) ## ----------------调用蛇移动的方法----------------- snakeRun(snakeDirect) ## 调用封装方法绘制蛇身 drawRect(headPoint,snakeColor) ## 调用封装方法绘制食物 drawRect(foodPoint,foodColor) #更新屏幕内容 pygame.display.flip() ## 设置一下帧率 clock.tick(5) ``` 然后我们就可以看到蛇头的移动了。注意帧率哦,上面修改成5做测试了,如果是以前这种的60就会移动得非常快。60对于贪吃蛇这个游戏来吃帧率太高了-。-根本操作不过来。但是帧率的控制如果太低了,看着会很卡  #### 控制蛇的方向 很简单,监听按键事件,然后根据按键改变移动的方向即可。比如wsad对应上下左右 ``` for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() ## 监听按键事件 if event.type == pygame.KEYDOWN: if event.key == 119: snakeDirect = "up" if event.key == 115: snakeDirect = "down" if event.key == 97: snakeDirect = "left" if event.key == 100: snakeDirect = "right" print(event) print(snakeDirect) ``` 对应的key可以print输出看就行了  效果如下:  ### 六:绘制蛇身 就是让蛇的格子多一点而已,其实也非常简单,可以用一个对象集合来表示蛇身。 ``` ## 定义一个蛇身的集合 snakeBody = [ Point(row=headPoint.row,col=headPoint.col+1), Point(row=headPoint.row,col=headPoint.col+2), Point(row=headPoint.row,col=headPoint.col+3), ] ## 随便定义一个蛇身的颜色 snakeBodyColor = (200,200,200) ``` 然后在主循环里边绘制出来,就是循环调用封装的方法而已。 ``` ## 蛇身绘制出来 for item in snakeBody: drawRect(item,snakeBodyColor) ``` **让蛇身跟着蛇头动。其实也非常简单,蛇身的移动就是两步。** 第1步就是让蛇身向前一步,也就是前面蛇头的位置,代码表示就是向蛇身的集合里边添加一条记录而已 ``` ## 蛇身添加一条记录,也就是向前走一步,向蛇头位置走一步 snakeBody.insert(0,Point(headPoint.row,headPoint.col)) ``` 第2步就是把蛇尾最后一格去掉,直接pop方法搞定 ``` 去掉蛇尾 snakeBody.pop() ``` 效果如下:  当然如果我们不区分蛇头与蛇身可以全部使用一个list表示即可。 ### 七:随机产生食物与吃食物 随机产生食物使用一个随机数就行 ``` import random foodPoint = Point(random.randint(1,row-2),random.randint(1,row-2)) ``` 判断是否吃到了食物,只需要判断蛇头和食物重叠就行,如果重叠了说明吃到了食物。而判断是否重叠只需要判断蛇头的行列是否与食物的行列相同。 ``` ## 蛇头和食物重叠说明吃到了食物 if headPoint.row == foodPoint.row and headPoint.col == foodPoint.col: ## 重新产生食物 foodPoint = Point(random.randint(0,row-1),random.randint(0,row-1)) else: ##没有吃掉食物才去掉蛇尾 snakeBody.pop() ```  ### 八:封装产生食物的方法,防止随机产生的食物与蛇身重叠了 我们产生食物的时候要判断一下,是否和蛇头,蛇身重叠了,如果重叠了要重新产生一次。封装的方式有很多种,下面是我封装的一种。 ``` def createFood(_headPoint,_snakeBody): while True: rfoodPoint = Point(random.randint(1,row-2),random.randint(1,row-2)) isRepater = False ## 判断食物是否与蛇头重叠 if _headPoint.row == rfoodPoint.row and _headPoint.col == rfoodPoint.col: isRepater=True continue ## 判断食物是否与蛇身重叠 for item in _snakeBody: if item.col == rfoodPoint.col and item.row == rfoodPoint.row: isRepater=True continue ## 都没有碰上说明这次产生的食物是正常的可以正常返回 if isRepater == False: return rfoodPoint ``` 然后产生食物的地方都可以使用这个方法了,初始化食物的时候 ``` foodPoint = createFood(headPoint,snakeBody) ``` 吃了食物后的时候: ``` ## 蛇头和食物重叠说明吃到了食物 if headPoint.row == foodPoint.row and headPoint.col == foodPoint.col: ## 重新产生食物 foodPoint = createFood(headPoint,snakeBody) ##foodPoint = Point(random.randint(1,row-2),random.randint(1,row-2)) else: ##没有吃掉食物才去掉蛇尾 snakeBody.pop() ``` ### 九:游戏结束检查,game over 贪吃蛇的游戏结束检查很简单,基本就是两点,一个是撞墙死了,一个是撞自己死了。当然规则自己定嘛,也可以撞自己不用死。判断也是非常简单,如下 ``` ## 游戏是否结束 isdead = False ## 装边界死了 if headPoint.row<0 or headPoint.row>=row or headPoint.col<0 or headPoint.col>=col: print("装边界死了") isdead = True ## 撞自己死了 for body in snakeBody: if headPoint.row == body.row and headPoint.col == body.col: print("撞自己死了") isdead=True ## 最简单的处理,直接退出游戏 if isdead == True: break ## 蛇身添加一条记录,也就是向前走一步,向蛇头位置走一步 snakeBody.insert(0,Point(headPoint.row,headPoint.col)) ## ----------------蛇的移动----------------- snakeRun(snakeDirect) ``` 我们这里处理游戏结束非常简单,直接退出游戏了。应该还是要有点动画效果,至少也有个game over的展示界面撒,还能再次开始什么的。下次再说了。 未完待续....
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}