Pygame 贪吃蛇
Python贪吃蛇
第一次使用 Markdown编辑器 。如果有什么问题请留言。这是学习传智播客博学谷Python项目实战贪吃蛇小游戏时写的笔记,看的时候没有问老师要课件和源码,为了方便记录,自己跟着视频写了一遍,我的程序中间出现了问题,修改了原视频的代码,会和源码不一样。
1. 规则
开始和结束
- 贪吃蛇初始出现在游戏窗口的左上角位置,体长共有3节
- 游戏过程中,一旦蛇头撞到了窗口的边缘或者身体的其他部位,游戏结束
- 游戏过程中,点击游戏窗口的关闭按钮,或者按下ESC键可以直接退出游戏
- 一局游戏结束后,按下空格键可以重新开启一局新戏
运动和控制
1.贪吃蛇初始沿屏幕水平方向,向右侧运动,每隔0.5秒移动一节身体
2.使用键盘的方向键(↑、↓、←、→)可以改变蛇头的运动方向,不过需要注意:
- 当蛇头沿水平方向运动时,只能使用垂直方向的方向键将蛇头的运动方向修改为垂直方向
- 当蛇头沿垂直方向运动时,只能使用水平方向的方向键将蛇头的运动方向修改为水平方向
3.游戏过程中按下空格键,可以暂停游戏;再次按下空格键,可以继续游戏
食物和得分
1.游戏开始后,会在游戏窗口的任一随机位置出现食物
- 注意:食物不会与蛇身体重叠
2.当蛇头与食物相遇时,代表蛇吃到了食物,此时:
- 被吃掉的食物从屏幕上消失
- 蛇的身体会增加一节
- 游戏得分增加10分
- 在游戏窗口的其他任—随机位置再次出现新的食物,食物同样不会与蛇身体重叠
3.如果食物出现的30秒内,贪吃蛇没有吃到食物,那么:
- 没有被吃到的食物从屏幕上消失
- 在游戏窗口的其他任一随机位置再次出现新的食物,食物同样不会与蛇身体重叠
4.每吃掉1颗食物,贪吃蛇的运动速度会加快:
- 初始每隔0.5秒移动—节身体
- 每吃掉1颗食物,移动的时间间隔缩短50ms,也就是:0.45s、0.40s、0.35s等依次类推
- 最短的移动时间间隔是0.1秒
2. 类的设计和模块准备
类的设计
根据游戏规则,要实现贪吃蛇游戏,一共需要4种对象,分别是:游戏对象、蛇对象、食物对象和文字标签对象。
除游戏对象外,其他3种对象在游戏窗口中的展现效果示意图如下:
因此我们可以考虑设计4个类,分别承担不同的职责:
1.游戏类(Game):负责提供整个游戏流程控制的相关操作,包括:
- 创建游戏主窗口和游戏时钟
- 创建并显示游戏元素:蛇、食物、得分标签、提示标签。监听用户交互并做出相应处理
2.标签类(Label) :负责提供文本提示信息的相关操作
3.食物类(Food) :负责提供食物对象的相关操作
4.蛇类(Snake) :负责提供蛇对象相关操作
类图如下:
模块的确定和装备
根据类的设计,我们可以考虑在贪吃蛇游戏项目中设计2个模块,分别是:
- game.py 游戏主模块,封装Game类并负责启动游戏
- game_items.py 游戏元素模块,封装Label、Food、Snake类,并定义全局变量
新建项目及文件准备
1.使用PyCharm新建─个项目:
- 项目名称: 贪吃蛇
- 解释器: Python 3.6
2.在项目中依次建立2个模块: game.py 和 game_items.py
3. 游戏框架搭建
pygame 的初始化和退出
pygame是专为开发电子游戏而设计的跨平台的 Python包。而包中又针对不同的开发需求提供有不同的模块,例如:显示模块、字体模块、混音器模块等等。
注意:在这些模块中,有一部分模块是必须要初始化之后,才能够正常使用的,例如:字体模块等。
pygame 为了程序员更加方便地使用包中的模块,提供有两个方法——init和quit,其中:
- init 方法可以一次性初始化 pygame的所有模块,这样在后续开发中,程序员可以直接使用这些模块,而不必再单独为某一个模块调用对应的初始化方法
- quit 方法可以取消初始化之前已经初始化过的模块
提示,由于Python解释器在退出之前会释放所有的模块,所以quit方法不是必须要调用
要实现 pygame的初始化和退出,需要在game.py模块中实现以下代码:
import pygame # 导入pygame
from game_items import* # 导入游戏元素模块中的所有类和全局变量
if _name___ -- '_main__':
pygame.init() #初始化所有模块
#游戏代码
pygame.quit() #取消初始化所有模块
游戏主窗口
贪吃蛇游戏是一款图形界面游戏,而所谓的图形界面就是指——程序启动时,首先应该呈现一个图形化的窗口,所有游戏元素(例如:蛇、食物、得分等)都显示在这个窗口的内部。
pygame的display模块提供的一系列方法可以用于创建游戏窗口以及更新窗口显示内容等操作。常用方法如下:
序号 | 方法 | 说明 |
---|---|---|
1 | pygame.display.set_mode() | 初始化游戏窗口 |
2 | pygame.display.set_caption() | 设计窗口 |
3 | pygame.display.update() | 更新屏幕显示内容 |
创建游戏主窗口方法
使用set_mode方法,可以非常方便地创建一个游戏主窗口,语法格式如下:
set_mode( resolution=(0, 0),flags=0,depth=0) -> Surface
其中:
- 参数
- resolution 指定屏幕的宽和高的元组,默认创建的窗口大小和屏幕大小―致
- flags 参数指定屏幕的附加选项,例如是否全屏等等,默认使用resolution指定的窗口大小
- depth 参数表示颜色的位数,默认自动匹配
- 返回
- Surface 对象
- 可以把一个Surface对象被看作是一个油画的画布,我们可以在这个画布上作画,例如:绘制贪吃蛇、绘制食物、绘制分数文字等
- 所有游戏元素绘制在游戏的主窗口之后,就是用户看到的游戏画面
- Surface 对象
注意:必须记录 set_mode方法的返回结果! 因为后续其他的游戏元素都需要绘制在游戏主窗口上。
设置窗口标题方法
使用set_caption方法,可以设置窗口标题,语法格式如下:
set_caption(title,icontitle = None) -> None
其中:
- 参数
- title指定显示在标题栏上的窗口标题
- icontitle在某些操作系统中,可以指定显示在任务栏上的程序标题,默认与title一致
要创建游戏主窗口并设置窗口标题,在game.py 中定义Game类,并且实现初始化方法。
游戏循环和游戏时钟
游戏循环
要做到游戏程序启动执行之后,不会立即退出,需要在游戏程序中增加一个游戏循环。所谓游戏循环就是个无限循环。
要想让贪吃蛇游戏启动执行之后,不会立即退出,可以在Game类中增加start方法,并且实现如下代码:
def stdrt(self):
"""
开始贪吃蛇游戏
"""
while True :
pass
在game.py的末尾修改代码如:
#游戏代码
Game().start() #创建游戏对象并且启动游戏
游戏中的动画实现原理
在一款图形界面的游戏中,通常游戏的画面是不断变化的,也就是我们常说的动画。例如:贪吃蛇的运动、食物的出现与消失以及分数值的变化等等。
那么游戏中的动画效果是怎样实现的呢?跟电影的原理类似,游戏中的动画效果,本质上是快速地在屏幕上绘制图像。
- 电影是将多张静止的电影胶片连续、快速地播放,产生连贯的视觉效果!
- —般在电脑上每秒绘制60次,就能够达到非常连续、高品质的动画效果
- 每次绘制的结果被称为帧Frame
因此,在我们刚刚完成的代码中,无限循环的执行频率(刷新帧率)只要能达到每秒60帧,就能够达到我们预期的动画效果了。
游戏时钟
pygame 的time模块中专门提供了一个Clock类,可以非常方便地设置游戏循环的执行频率——刷新帧率。
以60帧秒举例,当循环体代码执行一遍之后,我们让程序休息1/60秒,然后再次执行循环体代码,这样就能够做到循环体内部的代码,每秒只会被执行60次了,同时CPU的负荷也能够大大降低。
要设置刷新帧率需要2个步骤:
- 在游戏循环的循环体上方创建一个时钟对象
- 在游戏循环的循环体的末尾让时钟对象调用tick(帧率)方法来设置刷新帧率
提示: tick方法会根据上次被调用的时间,自动设置游戏循环中的延时
要想设置刷新帧率,可以对start方法的代码调整如下:
def start(self):
"""
开始贪吃蛇游戏
"""
clock = pygame.time.clock() # 游戏时钟
i = 0
while True:
print(i)
i += 1
clock.tick(60) # 刷新帧率
主窗口背景颜色
pygame的颜色定义
pygame 的颜色使用的是RGB色彩模式,即通过对 红(R )、绿(G)、蓝(B) 三个颜色相互之间的叠加来得到各种各样的颜色。
在pygame中,使用一个 (R, G,B) 格式的元组来定义一个颜色,其中,红®、绿(G)、蓝(B)的数值是0~ 255之间的一个整数。数值对应颜色的亮级,数值越大亮级就越大。
常用的颜色定义如下:
(255,255,255) #白色
(0, 0, 0) #黑色
(255, 0, 0) #红色
(0,255, 0) #绿色
(0, 0, 255) #蓝色
(255, 255,0) #黄色
(255,128,0) #橘色
(128,128,128) #灰色
定义并绘制窗口背景颜色
首先,在game_item.py模块中定义一个主窗口背景颜色的全局常量,代码如下:
import pygame
#全局变量定义
BACKGROUND_COLOR = (232,232,232) #主窗口背景颜色
- Python中没有变量和常量的区别
- 所谓常量就是定义之后,只允许访问,但是不允许修改
- Python中,如果要定义常量,可以使用全大写命名,单词之间使用 _ 分隔
然后,修改Game类的start方法,填充窗口颜色并更新显示.
填充了主窗口背景颜色之后,务必要使用pygame.display .update() 方法更新屏幕的内容!
update方法的作用就是将在游戏主窗口中绘制的最终结果—次性显示在屏幕上。
阶段性小结
- 颜色使用(R,G,B)格式的元组定义
- 常量命名使用全大写,单词之间使用 _ 分隔
- self.main_window.fill(背景颜色)使用背景颜色填充窗口
- 要在屏幕上看到最终绘制结果,需要调用pygame.display. update() 方法
绘制文本
pygame的font模块中专门提供了一个SysFont类,可以创建系统字体对象,从而能够实现在游戏窗口中绘制文字内容。
要想在游戏窗口中绘制文本内容,需要执行以下3个步骤:
- 创建字体对象
- 用字体渲染指定的文本内容,并生成一张图像(surface对象)
- 将生成的图像绘制在游戏主窗口的指定位置
以上三个步骤的示意图如下:
创建字体对象
SysFont的初始化方法的语法如下:
SysFont(name, size, bold=False, italic=False) -> Font
其中:
- 参数
- name系统字体的名称
- 可以设置的字体与操作系统有关,通过pygame.font.get_fonts() 可以获取当前系统的所有可用字体列表
- size字体大小
- bold是否粗体,默认为否
- italic是否斜体,默认为否
- name系统字体的名称
- 返回
- Font对象
代码实现
观察备课代码的运行效果,可以发现贪吃蛇游戏中的文字一共有2种颜色,分别是:
- 分数文字颜色——浅灰色
- 游戏暂停和结束时的提示文字颜色——深灰色
为了方便后续代码的编写,我们首先在game_item.py模块的顶部先定义两个字体颜色常量,代码如下:
SCORE_TEXT_COLOR =(192,192,192) # 分数文字颜色
TIP_TEXT_COLOR = (64,64,64) # 提示文字颜色
然后在game_item.py中定义Label类,并且实现初始化方法.
目前代码为:
# game.py
import pygame # 导入 pygame
from game_items import * # 导入游戏袁术模块
class Game(object):
def __init__(self):
self.main_window = pygame.display.set_mode((640, 480)) # 窗口创建
pygame.display.set_caption('贪吃蛇') # 标题创建
def start(self):
"""
启动并控制游戏
"""
clock = pygame.time.Clock() # 游戏时钟
while True:
# 依次绘制游戏元素
self.main_window.fill(BACKGROUND_COLOR)
# 更新显示,刷新背景颜色
pygame.display.update()
clock.tick(60) # 刷新帧率
if __name__ == '__main__':
pygame.init() # 初始化所有模块
# 游戏代码
Game().start()
pygame.quit() # 取消初始化所有模块
# game_items.py
import pygame # 导入 pygame
# 全局变量定义
BACKGROUND_COLOR = (232, 232, 232) # 窗口背景色
SCORE_TEXT_COLOR = (192, 192, 192) # 分数文字颜色
TIP_TEXT_COLOR = (64, 64, 64) # 提示文字颜色
class Lable(object):
"""
标签文本类
"""
def __init__(self, size=48, is_score=True):
"""
初始化标签信息
:param size :字体大小
:param is_score : 是否是显示得分的对象
"""
self.font = pygame.font.SysFont("simhei", size) # 黑体字
self.is_score = is_score
渲染文本内容
使用创建的字体对象调用render方法,渲染生成