5.贪吃蛇功能的实现
在这一小节,我们就以完成的游戏框架为基础,加入贪吃蛇的功能,完成整个游戏的开发
本小节的知识重点包括:
贪吃蛇类的设计
定义贪吃蛇类
添加蛇身体
绘制和移动身体
改变贪吃蛇的方向
贪吃蛇吃食物
贪吃蛇的死亡
5.1贪吃蛇类的设计
我们首先来明确一下在贪吃蛇类中应该封装哪些属性和方法, 这些内容的确定,会为后续的开发做好准备和铺垫。
按照贪吃蛇的游戏规则描述:
贪吃蛇初始出现在游戏窗口的左上角位置,体长共有3节
贪吃蛇初始沿屏幕水平方向,向右侧运动,每隔0.5秒移动一节身体
使用键盘的方向键(↑、 ↓、←、→)可以改变蛇头的运动方向
当蛇头与食物相遇时,代表蛇吃到了食物,游戏分数增加10分
5.1.1属性设计
运动方向属性
要描述贪吃蛇的运动方向,我们可以使用pygame预置的方向键常量,分别是:
pygame.K_UP上
pygame.K_DWON下
pygame.K_LEFT左
pygame.K_RIGHT右
提示:在后续监听键盘按键事件以及修改蛇的运动方向时,还会使用到这些常量
时间间隔属性
在初始状态时,贪吃蛇是每隔0.5秒移动一节身体的,而随着吃到的食物增加,这个时间间隔会不断缩小。因此我们还需要考虑使用一个属性来记录这个时间间隔
身体列表属性
如果把贪吃蛇的每节身体看成屏幕上的一个小格子,那么我们就可以使用一个Rect对象,来表示贪吃蛇的一节身体。这样,要记录整个身体,只需要定义一个body _list的列表属性,用来记录从蛇头到蛇尾的每一身体的Rect对象即可。
游戏得分属性
每一轮新游戏开始之后,游戏中只有一条贪吃蛇, 并且只有在这条蛇吃到食物之后,游戏分数才会增加,一旦贪吃蛇在运动过程中,蛇头撞到了窗口的边缘或者身体的其他部位,则判定为死亡。而再次开启新一轮游戏后,游戏分数会归零重新计算。
因此,我们可以定义一个游戏分数的属性,用来记录当前这条贪吃蛇对象的累计得分。
5.1.2方法设计
重置贪食蛇属性
按照游戏规则描述--每一 轮游戏结束后,用户按下空格键会开启新一轮游戏。这就意味着,开启新一轮游戏需要把贪吃蛇对象的属性设置为初始值。我们可以定义1个reset_snake的方法,专门负责来重设贪吃蛇的属性值。
添加身体
除了在初始化贪吃蛇时,我们要在水平向右的方向(蛇的初始运动方向)上增加三节身体之外,在游戏过程中,如果贪吃蛇再吃到食物,身体长度同样也要增加。
因此,我们可以定义1个add_node方法,专门负责向贪吃蛇的身体列表中增加一节身体。
绘制身体和更新位置
为了方便游戏循环中的代码调用,我们可以定义2个方法来负责绘制身体和更新位置:
draw方法负责在游戏窗口中绘制贪吃蛇的身体
update方法负责移动蛇的身体
改变蛇的方向
按照游戏规则描述--使用键盘的方向键 (↑、↓、←、→) 可以改变蛇头的运动方向。为了方便游戏循环中的代码调用,我们可以定义1个change_dir 方法,专门负责根据用户按键改变蛇的运动方向。
判断死亡和吃到食物
为了方便游戏循环中的代码调用,我们可以定义2个方法来负责判断蛇的死亡和是否吃到食物
is_dead方法返回是否死亡
has_eat方法返回是否吃到指定的食物
5.1.3贪吃蛇的类图
综上所述,我们可以得到贪吃蛇的类图如下:
5.2定义贪吃蛇类
在这一小节,我们先做一些准备工作,其中包括:
定义贪吃蛇类并准备reset_snake方法
使用贪吃蛇的分数属性调整游戏中的得分显示
5.2.1定义贪吃蛇类
首先,在game_items.py中定义Snake类,并且实现初始化方法和reset_snake方法,代码如下:
class Snake(object):"""蛇类"""def __init__(self):"""初始化蛇的数据"""self.dir = pygame.K_RIGHTself.time_interval = 500 # 运动时间间隔self.score = 0 # 游戏得分self.color = (64, 64, 64) # 身体颜色 - 深灰色body_list = [] # 身体列表def reset_snake(self):"""重置蛇的数据"""self.dir = pygame.K_RIGHTself.time_interval = 500self.score = 0self.body_list = []
然后,在game.py中的Game类中添加reset_game方法
def reset_game(self):"""重置游戏参数"""self.score = 0self.is_game_over = Falseself.is_pause = False# 重置蛇的数据self.snake.reset_snake()# 重置食物位置self.food.random_rect()
接着,修改game.py中的Game类中的绘制得分代码。得分是贪吃蛇的分数
# 绘制得分
self.score_label.draw(self.main_window, "DeFen: %d" % self.snake.score)# 绘制暂停、游戏结束的标签
if self.is_game_over:self.tip_label.draw(self.main_window, "Youxi Jiesu anKonggeJian KaiQi xinyouxi")
elif self.is_pause:self.tip_label.draw(self.main_window, "Youxi Zanting KonggejianJixu...")
else:self.snake.score += 1# 绘制食物
self.food.draw(self.main_window)
5.3添加蛇身体
添加蛇身体也就是add_ node这个方法是贪吃蛇游戏中非常关键的一个方法!
按照之前的分析,我们已经知道,在游戏中要添加一节身体一共有两种情况:
初始化贪吃蛇时,在屏幕的左上角,沿水平向右方向,依次添加3节身体
在游戏过程中,吃到食物后,同样需要增加1节身体
我们先看一下如下示意图,分析一下在吃到食物后,增加身体的处理方式
观察上图不难发现,当吃到食物时,我们只需要在蛇头的前方位置,也就是沿着蛇当前的运动方向偏移一个小格子,再增加一节身体作为蛇头就可以了。
这种处理方式最大的好处是:增加身体和向前移动可以一步到位!而不需要先在蛇尾添加一节身体,再向前依次移动每一节蛇的身体
接下来,再看一下添加蛇身体的流程图,进一步确认一下添加蛇身体的处理方式
接下来,按照流程图,来实现一下在game_items.py的Snake类中添加add_node方法,代码如下:
def add_node(self):"""添加一节身体"""# 生成新的矩形对象if self.body_list:# 已经有身体了head = self.body_list[0].copy()else:# 还没有身体head = pygame.Rect(-CELL_SIZE, 0, CELL_SIZE, CELL_SIZE)# 根据移动方向,把新生成的头部放到恰当的位置if self.dir == pygame.K_RIGHT:head.x += CELL_SIZEelif self.dir == pygame.K_LEFT:head.x -= CELL_SIZEelif self.dir == pygame.K_UP:head.y -= CELL_SIZEelif self.dir == pygame.K_DOWN:head.y += CELL_SIZE# 把新生成头部放到列表的最前面self.body_list.insert(0, head)
5.4绘制和移动身体
在这一小节,要完成两个任务:
利用draw模块提供的方法绘制蛇的身体
利用定时器事件实现蛇的移动
5.4.1绘制身体
在game_items.py的Snake类中定义draw方法,并实现如下代码:
def draw(self,window):"""绘制蛇的每一节身体"""for idx, rect in enumerate(self.body_list):pygame.draw.rect(window, self.color, rect.inflate(-2, -2), idx==0)
在game.py的Game类的游戏循环中,让蛇对象调用draw方法,代码如下:
# 绘制食物
self.food.draw(self.main_window)# 绘制贪吃蛇
self.snake.draw(self.main_window)# 刷新窗口内容
pygame.display.update()
未完待续
内容总结于:https://space.bilibili.com/441640380