使用Pygame实现简单的贪吃蛇游戏

摘要

本项目使用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、 增加一些经典玩法没有的元素,如隔离墙、随机道具等。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值