摘要
本项目使用Pygame编写了一款贪吃蛇游戏,实现了键盘对蛇的移动控制、蛇吃下食物成长、食物的随机生成、当前分数和当前剩余目标的显示、最高分的存档记录、暂停游戏和继续游戏、游戏结束提示、重新开始游戏、结束游戏、背景音乐和音效的播放等功能。
引言
背景
Pygame 是一个跨平台 Python 模块,专为电子游戏设计。包含图像、声音。创建在 SDL基础上,允许实时电子游戏研发而无需被低级语言,如 C语言或是更低级的汇编语言束缚。基于这样一个设想,所有需要的游戏功能和理念都完全简化位游戏逻辑本身,所有的资源结构都可以由高级语言提供,如Python。引用自此处
本项目是通过使用Pygame模块,实现一款贪吃蛇游戏。
意义
在现在这种快节奏的生活中,偶尔也需要放松身心,贪吃蛇游戏是一款休闲小游戏,加上本游戏所具有的欢快愉悦的背景音乐和趣味的音效,可以让玩家从紧张的生活节奏中解放,得到放松。
实现的功能
1、使用键盘控制蛇的移动方向,长按按键可以使蛇加速移动
- w键或方向上键 → 蛇向上移动
- s键或方向下键 → 蛇向下移动
- a键或方向左键 → 蛇向左移动
- d键或方向右键 → 蛇向右移动
2、随机位置生成食物
3、蛇吃下食物并成长
4、显示当前分数
5、显示当前剩余目标
6、对最高分的存档记录
7、点击暂停按钮或按下空格键暂停游戏或继续游戏
8、当蛇撞上墙壁或自身任何部位时,游戏失败
9、当达到目标时,游戏胜利
10、显示游戏结束界面
11、点击重新开始按钮或按下r键重新开始游戏
12、点击结束游戏按钮结束游戏
13、游戏进行中时,播放背景音乐
14、蛇吃下食物、蛇死亡或游戏胜利时,播放对应的音效
系统结构
本程序主要由main、block、yard、snake和food五个模块组成。其中main模块中有SnakeGame类,SnakeGame类主要由Yard类、Snake类和Food类组成,分别用于绘画院子、蛇和食物。而Yard、Snake和Food都由Block类组成,Block类用于绘画一个方形色块或圆形色块。
实现代码
block模块
1、创建Block类,并做好初始化操作
import pygame
class Block(pygame.sprite.Sprite):
def __init__(self, surface, rect, fill_color, border_color):
super(Block, self).__init__()
# 要绘制的目标surface
self.__surface = surface
# 要绘制的色块的填充色
self.__fill_color = fill_color
# 要绘制的色块的边框色
self.__border_color = border_color
# 当前色块的矩形信息
self.rect = pygame.Rect(rect)
创建一个Block类,需要传入该色块要绘制的目标surface、要绘制的rect信息,rect信息包括了要绘制的X、Y坐标及绘制的长宽、色块要填充的颜色以及边框颜色。并对该色块的rect信息进行保存。
2、实现绘制方形色块的方法
# 绘制色块
def draw(self):
# 绘制色块
pygame.draw.rect(self.__surface, self.__fill_color, self.rect, 0)
# # 绘制边框所需要的坐标点
points = [
# 上边框直线的首尾坐标点
(self.rect.left, self.rect.top),
(self.rect.left + self.rect.width, self.rect.top),
# 右边框直线的首尾坐标点
(self.rect.left + self.rect.width, self.rect.top),
(self.rect.left + self.rect.width, self.rect.top + self.rect.height),
# 下边框直线的首尾坐标点
(self.rect.left + self.rect.width, self.rect.top + self.rect.height),
(self.rect.left, self.rect.top + self.rect.height)
]
# 绘制边框
pygame.draw.polygon(self.__surface, self.__border_color, points, 1)
先使用pygame中draw模块的rect()函数,通过传入的数据绘制一个填充色的色块,然后通过计算,得到该色块各个点的坐标,使用pygame中draw模块的polygon()函数,绘制一个闭合的四边形,充当该色块的边框。
也可以直接使用rect()函数绘制无填充的矩形,充当该色块的边框,如下:
pygame.draw.rect(self.__surface, self.__border_color, self.rect, 1)
但使用这种方式,重合的边会显得比较粗。
3、实现绘制圆形色块的方法
# 绘制圆形色块
def draw_circle(self):
# 绘制圆形色块
pygame.draw.circle(self.__surface, self.__fill_color, self.rect.center, self.rect.width // 2, 0)
# 绘制圆形色块的边框
pygame.draw.circle(self.__surface, self.__border_color, self.rect.center, self.rect.width // 2, 1)
先使用pygame中draw模块的circle()函数绘制填充色的圆形色块,再绘制无填充的圆形,充当该色块的边框。
yard模块
1、创建Yard类,并初始化
import pygame
import block
class Yard(pygame.sprite.Sprite):
def __init__(self, surface, block_size, rows, cols, position, color, border_color):
super(Yard, self).__init__()
# 院子的矩形信息
self.rect = pygame.Rect(position, (cols * block_size[0], rows * block_size[1]))
# 保存院子里所有的色块
self.blocks = []
# 根据行列数依次创建每个色块并保存
for i in range(rows):
for j in range(cols):
# 计算每个色块的位置
x = position[0] + (j * block_size[0])
y = position[1] + (i * block_size[1])
rect = (x, y), block_size
_block = block.Block(surface, rect, color, border_color)
# 记录每个色块所在的行列号
_block.row = i
_block.col = j
# 保存每个色块
self.blocks.append(_block)
创建一个Yard类,需要传入该院子要绘制的目标surface、要绘制的院子中一个色块的长宽、院子的行数、列数、绘制的X、Y坐标位置、绘制的色块要填充的颜色以及边框颜色。并对该院子的rect信息进行保存。通过传入的数据依次创建所有需要绘制的色块,并为每个色块设置所在的行列号信息,将所有创建的色块进行保存。
2、实现绘制院子的方法
# 绘制院子
def draw(self):
# 绘制每个色块
for _block in self.blocks:
_block.draw()
通过Block类的draw()方法,将创建的所有色块绘制出来。
snake模块
1、创建Snake类,定义表示蛇移动方向的静态变量,并初始化
import pygame
import block
class Snake(pygame.sprite.Sprite):
# 代表向上移动
DIR_UP = 1
# 代表向下移动
DIR_DOWN = 2
# 代表向左移动
DIR_LEFT = 3
# 代表向右移动
DIR_RIGHT = 4
def __init__(self, surface, yard, length, row, col, color):
super(Snake, self).__init__()
# 要绘制的目标surface
self.__surface = surface
# 要放入的院子
self.__yard = yard
# 要绘制的蛇的颜色
self.__color = color
# 蛇的移动方向
self.__dir = Snake.DIR_LEFT
# 绘制蛇身的所有色块
self.blocks = []
# 保存每次移动前的最后一块蛇身
self.tail = None
# 根据行列号找到要绘制的蛇身的坐标点
for _block in yard.blocks:
if _block.row == row and _block.col == col:
# 整个蛇身的色块
for i in range(length):
rect = _block.rect.copy()
rect.left += _block.rect.width * i
# 创建一块蛇身的色块
b = block.Block(surface, rect, color, color)
# 保存蛇身的所有色块
self.blocks.append(b)
break
创建一个Snake类,需要传入该蛇要绘制的目标surface、要放入的院子、蛇身的初始长度,蛇头在院子中的行号和列号、以及要绘制的颜色。并保存蛇当前的移动方向。保存每次移动前的最后一块蛇身的色块信息。通过计算,得到每块蛇身的色块信息,创建所有色块并保存。蛇身默认水平绘制。
2、实现绘制蛇的方法
# 绘制蛇
def draw(self):
# 绘制所有色块
for _block in self.blocks:
_block.draw()
通过Block类的draw()方法,将创建的所有色块绘制出来。
3、实现蛇移动的方法
# 移动蛇
def move(self):
snake_head = self.blocks[0]
# 确定移动后蛇头的下一个位置
rect = snake_head.rect.copy()
if self.__dir == Snake.DIR_UP:
rect.top -= rect.height
elif self.__dir == Snake.DIR_DOWN:
rect.top += rect.height
elif self.__dir == Snake.DIR_LEFT:
rect.left -= rect.width
else:
rect.left += rect.width
# 创建一个新的蛇头
_block = block.Block(self.__surface, rect, self.__color, self.__color)
self.blocks.insert(0, _block)
# 去掉最后一块蛇身,并保存
self.tail = self.blocks.pop()
该方法实现了蛇在当前方向上的一次移动,通过当前蛇头的位置和移动的方向,得到此次移动后,蛇头的下一个位置,并创建新的蛇头,插入到保存蛇身所有色块的列表的第一个位置,使其成为新的蛇头,并去掉最后一块蛇身,以保证蛇的长度不变,对最后一块蛇身的信息进行保存。这样就能在视觉上呈现蛇在当前方向上移动了一个位置的效果。
4、实现蛇向各个方向移动的方法,修改蛇当前的移动方向
# 向上移动
def move_up(self):
self.__dir = Snake.DIR_UP
# 向下移动
def move_down(self):
self.__dir = Snake.DIR_DOWN
# 向左移动
def move_left(self):
self.__dir = Snake.DIR_LEFT
# 向右移动
def move_right(self):
self.__dir = Snake.DIR_RIGHT
5、实现判断蛇是否吃到了食物的方法
# 判断是否吃到了食物
def is_eat(self, _food):
snake_head = self.blocks[0]
if pygame.sprite.collide_rect(snake_head, _food):
self.blocks.append(self.tail)
return True
return False
通过pygame中sprite模块的collide_rect()函数,将蛇头和食物进行碰撞检测,如果碰撞了说明吃到了食物,并将本次移动应该去掉的最后一块蛇身添加上去,使蛇长度增加1。
6、实现判断蛇是否死亡的方法
# 判断是否死亡
def is_dead(self):
snake_head = self.blocks[0]
# 检测是否碰壁
if snake_head.rect.left < self.__yard.rect.left or \
snake_head.rect.right > self.__yard.rect.right or \
snake_head.rect.top < self.__yard.rect.top or \
snake_head.rect.bottom > self.__yard.rect.bottom:
# 碰壁后去掉蛇头,避免显示出蛇头撞出墙壁
# self.blocks.pop(0)
return True
# 深拷贝蛇的色块组
group = self.blocks.copy()
# 取出蛇头
snake_head = group[0]
group.remove(snake_head)
# 检测蛇头与蛇身是否发生碰撞
if pygame.sprite.spritecollide(snake_head, group, False):
return True
return False
先检测蛇头的位置,判断是否超出了院子的范围,如果是,说明蛇撞上了墙壁,然后通过pygame中sprite的spritecollide()函数,检测蛇头是否跟任一蛇身发生碰撞,如果是,说明蛇撞到了自己,以上两种情况,任意一个成立,都判定为蛇死亡,否则没有死亡。
food模块
1、创建Food类,并初始化
import pygame
import block
class Food(pygame.sprite.Sprite):
def __init__(self, surface, yard, row, col, color):
super(Food, self).__init__()
# 根据行列号找到要绘制的食物的坐标点
for _block in yard.blocks:
if _block.row == row and _block.col == col:
# 食物的矩形信息
self.rect = _block.rect.copy()
break
# 创建食物的色块
self.__block = block.Block(surface, self.rect, color, color)
创建一个Food类,需要传入该食物要绘制的目标surface、要放入的院子、在院子中的行号、列号、要绘制的颜色。通过行列号得到该食物色块的信息,并创建色块。
2、实现绘制食物的方法
# 绘制食物
def draw(self):
self.__block.draw_circle()
通过Block类的draw_circle()方法,将色块绘制出来。
3、实现判断当前食物是否位置合法的方法
# 判断当前食物的位置是否非法
def is_invalid(self, _snake):
return pygame.sprite.spritecollide(self, _snake.blocks, False)
将食物与蛇的所有色块进行碰撞检测,如果发生碰撞则说明位置不合法,否则合法。
main模块
1、定义需要用到的颜色信息
import pygame
import sys
from pygame.locals import *
import traceback
import yard
import snake
import food
import random
# 定义颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GRAY = (88, 87, 86)
2、创建SnakeGame类,并定义静态变量
class SnakeGame:
# 游戏界面大小
SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT = (500, 800)
# 背景颜色
BG_COLOR = GRAY
# 块大小
BLOCK_SIZE = (30, 30)
# 块行个数
ROWS = 20
# 块列个数
COLS = 15
# 蛇的初始长度
SNAKE_LENGTH = 3
# 蛇的出生位置(行列号)
snake_row = (ROWS - 1) // 2
snake_col = (COLS - SNAKE_LENGTH) // 2
# 蛇的移动速度(越大越慢)
SPEED = 30
# 吃到一个食物的得分
FOOD_POINT = 10
# 游戏胜利的目标食物个数
TARGET = ROWS * COLS - SNAKE_LENGTH
3、进行初始化操作
def __init__(self):
# 初始化
pygame.init()
pygame.mixer.init()
# 创建界面
self.screen = pygame.display.set_mode(SnakeGame.SCREEN_SIZE)
# 设置标题
pygame.display.set_caption("贪吃蛇")
# 加载音乐音效
# 背景音
pygame.mixer.music.load("sounds/bgm.mp3")
# 死亡音效
self.die_sound = pygame.mixer.Sound("sounds/die.wav")
# 进食音效
self.eat_sound = pygame.mixer.Sound("sounds/eat.wav")
# 胜利音效
self.victory_sound = pygame.mixer.Sound("sounds/victory.wav")
# 加载字体
# 得分提示字体
self.score_font = pygame.font.Font("fonts/font.otf", 36)
# 游戏提示字体
self.game_font = pygame.font.Font("fonts/font.otf", 64)
# 游戏结束后分数提示字体
self.gg_score_font = pygame.font.Font("fonts/font.otf", 52)
# 加载图片
# 未按下时暂停按钮图片
self.pause_nor_image = pygame.image.load("images/pause_nor.png").convert_alpha()
# 按下时暂停按钮图片
self.pause_pressed_image = pygame.image.load("images/pause_pressed.png").convert_alpha()
# 未按下时继续游戏按钮图片
self.resume_nor_image = pygame.image.load("images/resume_nor.png").convert_alpha()
# 按下时继续游戏按钮图片
self.resume_pressed_image = pygame.image.load("images/resume_pressed.png").convert_alpha()
# 重新开始按钮图片
self.restart_image = pygame.image.load("images/restart.png").convert_alpha()
# 结束游戏按钮图片
self.game_over_image = pygame.image.load("images/game_over.png").convert_alpha()
self.clock = pygame.time.Clock()
# 院子位置
yard_position = (SnakeGame.SCREEN_WIDTH - SnakeGame.BLOCK_SIZE[0] * SnakeGame.COLS) // 2, \
(SnakeGame.SCREEN_HEIGHT - SnakeGame.BLOCK_SIZE[1] * SnakeGame.ROWS) // 2 + 30
# 创建院子
self._yard = yard.Yard(self.screen, SnakeGame.BLOCK_SIZE, SnakeGame.ROWS, SnakeGame.COLS, yard_position, RED, WHITE)
self._snake = self._food = None
# 暂停处当前显示的图片
self.paused_image = self.pause_nor_image
# 暂停按钮的位置
self.paused_image_rect = self.paused_image.get_rect()
self.paused_image_rect.left = self._yard.rect.right - self.paused_image_rect.width
self.paused_image_rect.top = self._yard.rect.top - self.paused_image_rect.height * 2
# 重新开始按钮的矩阵信息
self.restart_image_rect = None
# 结束游戏按钮的矩阵信息
self.game_over_image_rect = None
self.step = self.is_gaming = self.is_dead = self.is_win = self.score = self.target = self.recorded = None
先对pygame的模块进行初始化,然后创建游戏窗口,将需要用到的背景音乐和音效文件进行加载,加载需要用到的字体,加载需要用到的图片,并创建院子,设置当前在暂停游戏处要显示的图片。
4、实现生成食物的方法
# 生成食物
def create_food(self):
# 创建食物
self._food = food.Food(self.screen, self._yard, random.randint(0, SnakeGame.ROWS - 1),
random.randint(0, SnakeGame.COLS - 1), WHITE)
# 如果创建的是非法的则继续创建
while self._food.is_invalid(self._snake):
self._food = food.Food(self.screen, self._yard, random.randint(0, SnakeGame.ROWS - 1),
random.randint(0, SnakeGame.COLS - 1), WHITE)
创建Food类的实例,并判断是否位置非法,如果非法则继续创建,直到创建出合法位置的食物为止。
5、实现初始化游戏数据的方法
# 初始化游戏数据
def init_data(self):
# 创建蛇
self._snake = snake.Snake(self.screen, self._yard, SnakeGame.SNAKE_LENGTH,
SnakeGame.snake_row, SnakeGame.snake_col, BLACK)
# 生成食物
self.create_food()
# 标志是否需要进行下一次移动(值为0时进行移动)
self.step = SnakeGame.SPEED
# 标志是否处于游戏中
self.is_gaming = True
# 是否死亡
self.is_dead = False
# 是否游戏胜利
self.is_win = False
# 统计得分
self.score = 0
# 统计剩余目标
self.target = SnakeGame.TARGET
# 标志是否已经记录了得分
self.recorded = False
创建蛇和食物的实例,并初始化一些游戏需要的数据。
6、实现判断是否游戏结束的方法
# 判断是否游戏结束
def is_game_over(self):
return self.is_win or self.is_dead
当玩家达到目标赢了或者蛇死亡输了,均判定为游戏结束。
7、实现暂停或继续游戏的方法
# 暂停或继续游戏
def pause_resume_game(self):
if not self.is_game_over():
self.is_gaming = not self.is_gaming
# 切换暂停游戏处的显示图片
self.paused_image = self.pause_nor_image if self.is_gaming else self.resume_nor_image
# 暂停或继续播放背景音乐
if self.is_gaming:
pygame.mixer.music.unpause()
else:
pygame.mixer.music.pause()
当游戏还未结束时,将判定游戏是否正在进行的标志取反,然后将暂停游戏处的按钮图片修改为对应的图片,并暂停背景音乐或继续播放背景音乐。
8、实现重新开始游戏的方法
# 重新开始游戏
def restart_game(self):
self.init_data()
# 暂停处当前显示的图片
self.paused_image = self.pause_nor_image
# 重新播放背景音乐
pygame.mixer.music.play(-1)
重新初始化游戏数据,设置暂停游戏处的按钮的图片,并重新开始播放背景音乐。
9、实现运行游戏的方法
# 运行游戏
def run(self):
# 播放背景音乐
pygame.mixer.music.play(-1)
# 初始化游戏数据
self.init_data()
while True:
for event in pygame.event.get():
# 监听用户退出操作
if event.type == QUIT:
pygame.quit()
sys.exit()
# 监听用户键盘按下
elif event.type == KEYDOWN:
# 空格按键按下后,暂停游戏或继续游戏
if event.key == K_SPACE:
self.pause_resume_game()
# 按下r键,重新开始游戏
if event.key == K_r:
self.restart_game()
# 监听鼠标按键操作
elif event.type == MOUSEBUTTONDOWN:
# 点击鼠标左键
if event.button == 1:
# 点击暂停按钮时,暂停游戏或继续游戏
if self.paused_image_rect.collidepoint(event.pos):
self.pause_resume_game()
# 点击重新开始按钮
elif self.restart_image_rect and self.restart_image_rect.collidepoint(event.pos):
# 避免游戏中误触
if self.is_game_over():
self.restart_game()
# 点击结束游戏按钮,退出游戏
elif self.game_over_image_rect and self.game_over_image_rect.collidepoint(event.pos):
# 避免游戏中误触
if self.is_game_over():
pygame.quit()
sys.exit()
# 监听鼠标移动操作
elif event.type == MOUSEMOTION:
# 将暂停处的按钮切换成对应的图片
if self.paused_image_rect.collidepoint(event.pos):
if self.is_gaming:
self.paused_image = self.pause_pressed_image
else:
self.paused_image = self.resume_pressed_image
else:
if self.is_gaming:
self.paused_image = self.pause_nor_image
else:
self.paused_image = self.resume_nor_image
if self.is_gaming:
# 监听用户的键盘操作
pressed = pygame.key.get_pressed()
if pressed[K_w] or pressed[K_UP]:
self.step -= 5
self._snake.move_up()
if pressed[K_s] or pressed[K_DOWN]:
self.step -= 5
self._snake.move_down()
if pressed[K_a] or pressed[K_LEFT]:
self.step -= 5
self._snake.move_left()
if pressed[K_d] or pressed[K_RIGHT]:
self.step -= 5
self._snake.move_right()
先播放背景音乐,并初始化游戏数据,然后实现事件监听,对用户的键盘和鼠标操作进行监听,实现使用鼠标控制游戏的暂停和继续、重新开始游戏、结束游戏以及对暂停游戏处按钮图片的切换的操作,实现使用键盘对蛇进行控制、控制游戏的暂停和继续以及重新开始游戏的操作。当按下控制蛇移动的按键时,还会增加step变量的衰减幅度,如果一直按着就会持续对step变量的大幅度衰减,达到加速蛇的移动的效果。
# 填充背景色
self.screen.fill(SnakeGame.BG_COLOR)
# 游戏未结束时绘制
if not self.is_game_over():
# 绘制院子
self._yard.draw()
# 绘制食物
self._food.draw()
# 绘制蛇
self._snake.draw()
# 绘制暂停按钮
self.screen.blit(self.paused_image, self.paused_image_rect)
# 绘制得分
score_text = self.score_font.render("Score : %d" % self.score, True, WHITE)
self.screen.blit(score_text, (self._yard.rect.left, 20))
# 绘制剩余目标提示
target_text = self.score_font.render("Target : %d" % self.target, True, WHITE)
self.screen.blit(target_text, (self._yard.rect.left, score_text.get_rect().height + 40))
然后对游戏的元素进行绘制。
# 移动蛇,每SPEED帧移动一次
if self.is_gaming and self.step <= 0:
# 移动一次蛇的位置
self._snake.move()
# 判断是否吃到食物
if self._snake.is_eat(self._food):
self.create_food()
self.score += SnakeGame.FOOD_POINT
self.target -= 1
# 播放音效
self.eat_sound.play()
# 判断蛇是否死亡
if self._snake.is_dead():
self.is_gaming = False
self.is_dead = True
# 播放音效
self.die_sound.play()
# 淡出结束背景音乐
pygame.mixer.music.fadeout(1000)
# 判断是否游戏胜利
if self.target == 0:
self.is_gaming = False
self.is_win = True
# 播放音效
self.victory_sound.play()
# 淡出结束背景音乐
pygame.mixer.music.fadeout(1000)
self.step = SnakeGame.SPEED
else:
self.step -= 1
然后使蛇进行移动,通过step变量确定是否要让蛇进行一次移动,当step为0的时候进行一次移动,否则使step递减。当蛇发生一次移动时,判断蛇是否吃到食物、是否死亡或者是否游戏胜利,并进行相应的操作,播放对应的音效。
# 绘制游戏提示
if not self.is_gaming:
# 设置游戏提示内容
if self.is_game_over():
game_text = "GAME OVER!" if not self.is_win else "YOU WIN!"
else:
game_text = "PAUSED"
# 创建用于游戏提示的文字surface
game_text_surface = self.game_font.render(game_text, True, WHITE)
# 游戏提示显示位置
game_text_x = self._yard.rect.left + (self._yard.rect.width - game_text_surface.get_rect().width) // 2
game_text_y = self._yard.rect.top + (self._yard.rect.height - game_text_surface.get_rect().height) // 2
# 显示游戏提示
self.screen.blit(game_text_surface, (game_text_x, game_text_y))
绘制游戏提示,包括游戏暂停、游戏失败或游戏胜利时的提示。
# 记录最高分并绘制游戏结束界面
if self.is_game_over():
# 判断是否已经记录了分数
if not self.recorded:
self.recorded = True
# 读取历史最高分数
try:
with open("record.txt", "r") as f:
record_score = int(f.read())
except FileNotFoundError:
record_score = 0
# 如果本局游戏玩家得分超过历史最高分,则存档记录
if self.score >= record_score:
with open("record.txt", "w") as f:
f.write(str(self.score))
f.close()
当游戏结束时,读取最高分记录,如果玩家分数超过历史最高分,则记录本次玩家分数为最高分。
# 绘制历史最高分数
best_text = self.score_font.render("Best : %d" % record_score, True, WHITE)
self.screen.blit(best_text, (self._yard.rect.left, target_text.get_rect().height * 2 + 60))
# 绘制当前得分
your_score_tip_text = self.gg_score_font.render("Your Score", True, WHITE)
your_score_text = self.gg_score_font.render(str(self.score), True, WHITE)
# 当前得分提示信息显示位置
tip_text_x = self._yard.rect.left + (self._yard.rect.width - your_score_tip_text.get_rect().width) // 2
tip_text_y = game_text_y - 180
self.screen.blit(your_score_tip_text, (tip_text_x, tip_text_y))
# 当前得分显示位置
score_text_x = self._yard.rect.left + (
self._yard.rect.width - your_score_text.get_rect().width) // 2
score_text_y = game_text_y - 90
self.screen.blit(your_score_text, (score_text_x, score_text_y))
# 绘制重新开始按钮
self.restart_image_rect = self.restart_image.get_rect()
self.restart_image_rect.left = self._yard.rect.left + (
self._yard.rect.width - self.restart_image_rect.width) // 2
self.restart_image_rect.top = game_text_y + game_text_surface.get_rect().height + 60
self.screen.blit(self.restart_image, self.restart_image_rect)
# 绘制结束游戏按钮
self.game_over_image_rect = self.game_over_image.get_rect()
self.game_over_image_rect.left = self._yard.rect.left + (
self._yard.rect.width - self.game_over_image_rect.width) // 2
self.game_over_image_rect.top = self.restart_image_rect.bottom + 20
self.screen.blit(self.game_over_image, self.game_over_image_rect)
当游戏结束时,绘制游戏结束界面。
pygame.display.flip()
# 设定帧率
self.clock.tick(60)
刷新双缓冲,并设置游戏帧率。
10、运行游戏
if __name__ == '__main__':
try:
snake_game = SnakeGame()
snake_game.run()
except SystemExit:
pass
except:
traceback.print_exc()
input()
当此模块被运行时,创建SnakeGame实例,并调用run()方法运行游戏。
实验结果
1、游戏进行中界面
2、游戏暂停界面
3、游戏结束界面
总结和展望
本次实验实现了一个基本功能比较完善的贪吃蛇游戏,但是还有一些待改进的地方,如:
1、 判断蛇是否吃到食物的逻辑应该跟蛇成长的逻辑分开,当判定蛇吃到了食物之后,再进行蛇的成长。
2、 对文件的操作可以放大到整个程序运行的过程中,而不是结束一局游戏就操作一次。刚开始运行游戏的时候就从文件中读取历史最高分记录,将该记录保存在全局变量中,每次结束一局游戏时,将本次得分跟该全局变量进行比较,将该全局变量的值修改为当前的最高分,作为新的历史最高分记录,在退出游戏时,将该分数保存到文件中。
而且还有一些扩展的功能没有实现,如:
1、 游戏开始界面的显示
2、 游戏难度的选择
3、 声音的打开和关闭
4、 增加一些经典玩法没有的元素,如隔离墙、随机道具等。