


pygame介绍
Pygame 是一个专门用来开发游戏的 Python 模块,主要为开发、设计 2D 电子游戏而生,它是一个免费、开源的第三方软件包,支持多种操作系统,具有良好的跨平台性(比如 Windows、Linux、Mac 等)。Pygame 是 Pete Shinners 在 SDL(Simple DirectMedia Layer,一套开源的跨平台多媒体开发库)基础上开发而来,其目的是取代 PySDL。
截止到 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)
三:绘制背景,让窗体变成白色背景
绘制背景,让窗体变成白色背景。其实就是画一个矩形覆盖整个窗体即可。其实就一句话
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)
四:绘制格子,也就是绘制蛇身,食物等
贪吃蛇的地图很简单,我们可以看成一个一个的格子,比如蛇身是一个一个的格子,食物也是一个一个的格子,障碍物也可以是一个一个的格子。格子的多少也就是地图可以操控空间有多少。
绘制一个任意大小的格子,其实也是使用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))
封装一个画格子的方法
因为画格子也是一个需要重复使用的方法,所以我们需要封装一下。方法也很简单就是把前面写的计算格子宽高,根据格子行列计算距离左边与上边的距离等提到一个方法中。其实计算格子宽高的可以单独提出来写,我们暂时把这个放到这里吧。
方法有两个参数一个是坐标也就是格子所在的行列,还有一个是颜色
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。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)