Python贪吃蛇小游戏

需要实现的功能列表:

1.贪吃蛇的控制,通过上下左右方向键;
2.触碰到边缘、墙壁、自身则游戏结束;
3.接触到食物则食物消失,同时根据食物类型身体会变长;
4.目前长度显示;
5.暂停、死亡界面;

我自己最开始的思路(有错误):
首先是界面的绘制,蛇的初始化(小方格,蛇的起始坐标,默认移动方向),食物的位置随机生成
贪吃蛇的上下左右方向,会想到980题.不同路径3用[-1, 0], [1, 0], [0, -1], [0, 1]这种形式代表上下左右四个方向(蛇头根据这四个方向移动,蛇身是后一个小方格移动到前一个小方格的位置(覆盖)),在蛇移动的时候需要设置移动的速度,通过设置时间间隔来实现(之前用labview的时候遇到过蛇身移动速度太快的问题)。蛇往哪个方向移动需要接收来自键盘的指示。
接触到食物:食物的位置坐标和蛇头的位置坐标一致,可用判断逻辑实现,如果一致,蛇身加一,不一致,蛇身继续移动
蛇身长度用一个变量来记录,每吃到一个食物蛇身加一
触碰到边缘、墙壁:需要计算边缘墙壁的坐标,判断蛇头坐标是否与这些坐标一致。是否碰到自身:蛇头与除蛇头外的部位的坐标是否一致。

分步代码:

各种绘制

# 游戏背景以及最下方用于显示文字的背景
def draw_background():
    # 填充白色背景
    screen.fill(COLORS['white'])
    # 绘制底部用于显示文字的黑色矩形区域
    pygame.draw.rect(screen, COLORS['black'], (-100, GAME_SIZE[1], 3000, 200), 0)

# 绘制墙壁
def draw_wall():
    for xy in wall_list:
        # 绘制墙壁,使用灰色矩形表示
        pygame.draw.rect(screen, COLORS['darkgray'], (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT), 0)

# 绘制蛇,包括头和身体
def draw_snake():
    head = snake_list[0]  # 获取蛇头的位置
    # 绘制蛇头,使用深红色圆形表示
    pygame.draw.circle(screen, COLORS['darkred'], (head[0], head[1]), int(SNAKE_WIDTH / 2), 0)
    for xy in snake_list[1:]:
        # 绘制蛇身,使用深红色矩形表示
        pygame.draw.rect(screen, COLORS['darkred'], (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT), 2)

# 绘制食物
def draw_food():
    for xyz in food_list:
        # 绘制食物,使用不同颜色的矩形表示
        pygame.draw.rect(screen, FOOD_COLORS[xyz[2] - 1], (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT), 0)

# 绘制下方的身体长度记录
def draw_context():
    # 显示蛇的长度,使用浅蓝色字体
    txt = FONT_M.render('Snake length: ' + str(len(snake_list) - 1), True, COLORS['lightblue'])
    x, y = 10, GAME_SIZE[1] + (int((SIZE[1] - GAME_SIZE[1]) / 2))  # 确定显示文本的位置
    y = int(y - FONT_M.size('Count')[1] / 2)  # 调整文本的垂直位置
    screen.blit(txt, (x, y))  # 在屏幕上显示文本

# 绘制暂停界面
def draw_pause():
    s = pygame.Surface(SIZE, pygame.SRCALPHA)  # 创建一个具有透明度的表面
    s.fill((255, 255, 255, 220))  # 用带透明度的白色填充表面
    screen.blit(s, (0, 0))  # 将表面覆盖到屏幕上
    txt = FONT_M.render('PAUSE', True, COLORS['darkgray'])  # 显示“PAUSE”文本,使用深灰色字体
    x, y = SIZE[0] / 2, SIZE[1] / 2  # 确定文本显示的中心位置
    x, y = int(x - FONT_M.size('PAUSE')[0] / 2), int(y - FONT_M.size('PAUSE')[1] / 2)  # 调整文本的位置,使其居中
    screen.blit(txt, (x, y))  # 在屏幕上显示文本

# 绘制死亡界面
def draw_dead():
    s = pygame.Surface(SIZE, pygame.SRCALPHA)  # 创建一个具有透明度的表面
    s.fill((255, 255, 255, 240))  # 用带透明度的白色填充表面
    screen.blit(s, (0, 0))  # 将表面覆盖到屏幕上
    txt = FONT_M.render('YOU DEAD', True, COLORS['black'])  # 显示“YOU DEAD”文本,使用黑色字体
    x, y = SIZE[0] / 2, SIZE[1] / 2  # 确定文本显示的中心位置
    x, y = int(x - FONT_M.size('YOU DEAD')[0] / 2), int(y - FONT_M.size('YOU DEAD')[1] / 2)  # 调整文本的位置,使其居中
    screen.blit(txt, (x, y))  # 在屏幕上显示文本

碰撞检查(死亡和食物)

# 矩形覆盖检查作为碰撞检测,思路是取反,即取所有不会覆盖的情况的反即可
def rect_cover(rect1, rect2):
    # 计算第一个矩形的左、右、上、下边界
    left1 = int(rect1[0])
    right1 = int(rect1[0] + rect1[2])
    up1 = int(rect1[1])
    down1 = int(rect1[1] + rect1[3])
    # 计算第二个矩形的左、右、上、下边界
    left2 = int(rect2[0])
    right2 = int(rect2[0] + rect2[2])
    up2 = int(rect2[1])
    down2 = int(rect2[1] + rect2[3])

    # 检查两个矩形是否覆盖,判断条件是两个矩形不相交的情况的取反
    if not (right2 <= left1 or left2 >= right1 or down2 <= up1 or up2 >= down1):
        return True  # 如果两个矩形相交,返回True
    return False  # 否则,返回False


# 检查是否碰到食物
def check_food():
    # 获取蛇头位置
    first = snake_list[0]
    # 计算蛇头的矩形边界
    snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)

    for i in range(len(food_list)):
        xyz = food_list[i]
        # 计算食物的矩形边界
        food_rect = (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT)

        # 检查蛇头是否与当前食物碰撞
        if rect_cover(snake_head_rect, food_rect):
            add_body(xyz[2])  # 增加蛇的长度
            del food_list[i]  # 删除被吃掉的食物
            return True  # 返回True,表示吃到了食物
    return False  # 返回False,表示没有吃到食物


# 检查是否碰到边缘、墙壁或者自己的身体
def check_dead():
    first = snake_list[0]  # 获取蛇头位置
    # 计算蛇头的矩形边界
    snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)

    # 检查蛇头是否碰到游戏边缘
    if first[0] < 0 or first[0] > GAME_SIZE[0] or first[1] < 0 or first[1] > GAME_SIZE[1]:
        return True  # 如果碰到边缘,返回True

    # 检查蛇头是否碰到墙壁
    for xy in wall_list:
        # 计算墙壁的矩形边界
        wall_rect = (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT)
        if rect_cover(snake_head_rect, wall_rect):
            return True  # 如果碰到墙壁,返回True

    # 检查蛇头是否碰到自己的身体
    for xy in snake_list[1:]:
        # 计算身体的矩形边界
        body_rect = (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
        if rect_cover(snake_head_rect, body_rect):
            return True  # 如果碰到自己的身体,返回True

    return False  # 返回False,表示没有发生碰撞

更新食物和增加蛇的身体长度

# 添加食物
def add_food():
    while (True):
        # 随机生成食物的位置和类型
        xyz = [random.choice(X_LIST), random.choice(Y_LIST), random.choice([1, 2, 3, 4])]

        # 检查生成的食物位置是否在墙壁列表中
        if xyz not in wall_list:
            food_list.append(xyz)  # 如果不在墙壁中,添加到食物列表中
            break  # 结束循环


# 增加蛇的身体长度
def add_body(length=1):
    for c in range(length):
        # 获取蛇的最后两节的位置
        last2, last1 = snake_list[-2], snake_list[-1]

        # 如果最后两节的x坐标相同,说明蛇身是竖着的
        if last2[0] == last1[0]:  # 竖着的两段
            if last2[1] > last1[1]:  # 如果前一节在后一节的上方,说明蛇身朝下
                snake_list.append([last1[0], last1[1] - SNAKE_WIDTH])  # 在蛇的尾部加上一节,向上延伸
            else:  # 否则,蛇身朝上
                snake_list.append([last1[0], last1[1] + SNAKE_WIDTH])  # 在蛇的尾部加上一节,向下延伸
        else:  # 如果x坐标不同,说明蛇身是横着的
            if last2[0] > last1[0]:  # 如果前一节在后一节的右侧,说明蛇身朝左
                snake_list.append([last1[0] - SNAKE_WIDTH, last1[1]])  # 在蛇的尾部加上一节,向左延伸
            else:  # 否则,蛇身朝右
                snake_list.append([last1[0] + SNAKE_WIDTH, last1[1]])  # 在蛇的尾部加上一节,向右延伸

蛇自身的移动

# 通过按键判断当前蛇的朝向
if event.key == K_LEFT:
    if head in ['up','down']:
        head = 'left'
elif event.key == K_RIGHT:
    if head in ['up','down']:
        head = 'right'
elif event.key == K_UP:
    if head in ['left','right']:
        head = 'up'
elif event.key == K_DOWN:
    if head in ['left','right']:
        head = 'down'

# 通过朝向判断蛇的下一个位置
first = snake_list[0]
snake_list[1:] = snake_list[:-1]
if head == 'up':
    snake_list[0] = [first[0],first[1]-SNAKE_WIDTH]
elif head == 'down':
    snake_list[0] = [first[0],first[1]+SNAKE_WIDTH]
elif head == 'left':
    snake_list[0] = [first[0]-SNAKE_WIDTH,first[1]]
elif head == 'right':
    snake_list[0] = [first[0]+SNAKE_WIDTH,first[1]]

完整代码

import random

import pygame
from pygame.color import THECOLORS as COLORS
from pygame.locals import *

# 游戏背景以及最下方用于显示文字的背景
def draw_background():
    # 填充白色背景
    screen.fill(COLORS['white'])
    # 绘制底部用于显示文字的黑色矩形区域
    pygame.draw.rect(screen, COLORS['black'], (-100, GAME_SIZE[1], 3000, 200), 0)

# 绘制墙壁
def draw_wall():
    for xy in wall_list:
        # 绘制墙壁,使用灰色矩形表示
        pygame.draw.rect(screen, COLORS['darkgray'], (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT), 0)

# 绘制蛇,包括头和身体
def draw_snake():
    head = snake_list[0]  # 获取蛇头的位置
    # 绘制蛇头,使用深红色圆形表示
    pygame.draw.circle(screen, COLORS['darkred'], (head[0], head[1]), int(SNAKE_WIDTH / 2), 0)
    for xy in snake_list[1:]:
        # 绘制蛇身,使用深红色矩形表示
        pygame.draw.rect(screen, COLORS['darkred'], (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT), 2)

# 绘制食物
def draw_food():
    for xyz in food_list:
        # 绘制食物,使用不同颜色的矩形表示
        pygame.draw.rect(screen, FOOD_COLORS[xyz[2] - 1], (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT), 0)

# 绘制下方的身体长度记录
def draw_context():
    # 显示蛇的长度,使用浅蓝色字体
    txt = FONT_M.render('Snake length: ' + str(len(snake_list) - 1), True, COLORS['lightblue'])
    x, y = 10, GAME_SIZE[1] + (int((SIZE[1] - GAME_SIZE[1]) / 2))  # 确定显示文本的位置
    y = int(y - FONT_M.size('Count')[1] / 2)  # 调整文本的垂直位置
    screen.blit(txt, (x, y))  # 在屏幕上显示文本

# 绘制暂停界面
def draw_pause():
    s = pygame.Surface(SIZE, pygame.SRCALPHA)  # 创建一个具有透明度的表面
    s.fill((255, 255, 255, 220))  # 用带透明度的白色填充表面
    screen.blit(s, (0, 0))  # 将表面覆盖到屏幕上
    txt = FONT_M.render('PAUSE', True, COLORS['darkgray'])  # 显示“PAUSE”文本,使用深灰色字体
    x, y = SIZE[0] / 2, SIZE[1] / 2  # 确定文本显示的中心位置
    x, y = int(x - FONT_M.size('PAUSE')[0] / 2), int(y - FONT_M.size('PAUSE')[1] / 2)  # 调整文本的位置,使其居中
    screen.blit(txt, (x, y))  # 在屏幕上显示文本

# 绘制死亡界面
def draw_dead():
    s = pygame.Surface(SIZE, pygame.SRCALPHA)  # 创建一个具有透明度的表面
    s.fill((255, 255, 255, 240))  # 用带透明度的白色填充表面
    screen.blit(s, (0, 0))  # 将表面覆盖到屏幕上
    txt = FONT_M.render('YOU DEAD', True, COLORS['black'])  # 显示“YOU DEAD”文本,使用黑色字体
    x, y = SIZE[0] / 2, SIZE[1] / 2  # 确定文本显示的中心位置
    x, y = int(x - FONT_M.size('YOU DEAD')[0] / 2), int(y - FONT_M.size('YOU DEAD')[1] / 2)  # 调整文本的位置,使其居中
    screen.blit(txt, (x, y))  # 在屏幕上显示文本


# 矩形覆盖检查作为碰撞检测,思路是取反,即取所有不会覆盖的情况的反即可
def rect_cover(rect1, rect2):
    # 计算第一个矩形的左、右、上、下边界
    left1 = int(rect1[0])
    right1 = int(rect1[0] + rect1[2])
    up1 = int(rect1[1])
    down1 = int(rect1[1] + rect1[3])
    # 计算第二个矩形的左、右、上、下边界
    left2 = int(rect2[0])
    right2 = int(rect2[0] + rect2[2])
    up2 = int(rect2[1])
    down2 = int(rect2[1] + rect2[3])

    # 检查两个矩形是否覆盖,判断条件是两个矩形不相交的情况的取反
    if not (right2 <= left1 or left2 >= right1 or down2 <= up1 or up2 >= down1):
        return True  # 如果两个矩形相交,返回True
    return False  # 否则,返回False


# 检查是否碰到食物
def check_food():
    # 获取蛇头位置
    first = snake_list[0]
    # 计算蛇头的矩形边界
    snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)

    for i in range(len(food_list)):
        xyz = food_list[i]
        # 计算食物的矩形边界
        food_rect = (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT)

        # 检查蛇头是否与当前食物碰撞
        if rect_cover(snake_head_rect, food_rect):
            add_body(xyz[2])  # 增加蛇的长度
            del food_list[i]  # 删除被吃掉的食物
            return True  # 返回True,表示吃到了食物
    return False  # 返回False,表示没有吃到食物


# 检查是否碰到边缘、墙壁或者自己的身体
def check_dead():
    first = snake_list[0]  # 获取蛇头位置
    # 计算蛇头的矩形边界
    snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)

    # 检查蛇头是否碰到游戏边缘
    if first[0] < 0 or first[0] > GAME_SIZE[0] or first[1] < 0 or first[1] > GAME_SIZE[1]:
        return True  # 如果碰到边缘,返回True

    # 检查蛇头是否碰到墙壁
    for xy in wall_list:
        # 计算墙壁的矩形边界
        wall_rect = (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT)
        if rect_cover(snake_head_rect, wall_rect):
            return True  # 如果碰到墙壁,返回True

    # 检查蛇头是否碰到自己的身体
    for xy in snake_list[1:]:
        # 计算身体的矩形边界
        body_rect = (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
        if rect_cover(snake_head_rect, body_rect):
            return True  # 如果碰到自己的身体,返回True

    return False  # 返回False,表示没有发生碰撞

# 添加食物
def add_food():
    while (True):
        # 随机生成食物的位置和类型
        xyz = [random.choice(X_LIST), random.choice(Y_LIST), random.choice([1, 2, 3, 4])]

        # 检查生成的食物位置是否在墙壁列表中
        if xyz not in wall_list:
            food_list.append(xyz)  # 如果不在墙壁中,添加到食物列表中
            break  # 结束循环

# 增加蛇的身体长度
def add_body(length=1):
    for c in range(length):
        # 获取蛇的最后两节的位置
        last2, last1 = snake_list[-2], snake_list[-1]

        # 如果最后两节的x坐标相同,说明蛇身是竖着的
        if last2[0] == last1[0]:  # 竖着的两段
            if last2[1] > last1[1]:  # 如果前一节在后一节的上方,说明蛇身朝下
                snake_list.append([last1[0], last1[1] - SNAKE_WIDTH])  # 在蛇的尾部加上一节,向上延伸
            else:  # 否则,蛇身朝上
                snake_list.append([last1[0], last1[1] + SNAKE_WIDTH])  # 在蛇的尾部加上一节,向下延伸
        else:  # 如果x坐标不同,说明蛇身是横着的
            if last2[0] > last1[0]:  # 如果前一节在后一节的右侧,说明蛇身朝左
                snake_list.append([last1[0] - SNAKE_WIDTH, last1[1]])  # 在蛇的尾部加上一节,向左延伸
            else:  # 否则,蛇身朝右
                snake_list.append([last1[0] + SNAKE_WIDTH, last1[1]])  # 在蛇的尾部加上一节,向右延伸

if __name__ == "__main__":
    # 初始化pygame
    pygame.init()

    # 常量设置
    GAME_SIZE = [900, 900]  # 游戏区大小
    SIZE = [GAME_SIZE[0], GAME_SIZE[1] + 100]  # 窗口大小,包括下方显示区域
    FONT_S = pygame.font.SysFont('Times', 50)  # 小号字体
    FONT_M = pygame.font.SysFont('Times', 90)  # 中号字体
    DIRECTION = ['up', 'right', 'down', 'left']  # 方向列表
    X_LIST = [x for x in range(GAME_SIZE[0])]  # x坐标范围列表
    Y_LIST = [y for y in range(GAME_SIZE[1])]  # y坐标范围列表
    FOOD_COLORS = ((46, 139, 87), (199, 21, 133), (25, 25, 112), (255, 215, 0))  # 食物颜色

    # 墙壁
    wall_list = [[100, 200], [600, 500], [350, 200], [500, 800]]  # 墙壁位置列表
    WALL_WIDTH, WALL_HEIGHT = 30, 30  # 墙壁宽度和高度

    # 食物
    food_list = [(150, 200, 1), (300, 500, 1), (740, 542, 1), (300, 600, 1), (700, 600, 1)]  # 食物位置和类型
    FOOD_WIDTH, FOOD_HEIGHT = 14, 14  # 食物的宽度和高度

    # 创建窗口,大小为SIZE
    screen = pygame.display.set_mode(SIZE)

    # 变量参数
    snake_list = [[100 + 12 * 4, 100], [100 + 12 * 3, 100], [100 + 12 * 2, 100], [100 + 12 * 1, 100],
                  [100, 100]]  # 初始化蛇的位置列表
    SNAKE_WIDTH, SNAKE_HEIGHT = 12, 12  # 蛇的宽度和高度
    snake_v = 0  # 蛇的速度
    count_time = 0  # 时间计数器

    # 游戏等级
    frame = 0.05  # 帧数
    level = 1  # 等级

    # 主循环控制变量
    running = True  # 游戏是否运行
    pause = False  # 游戏是否暂停
    dead = False  # 游戏是否结束
    head = 'right'  # 初始蛇的方向为向右

    # 游戏主循环
    while running:
        # 事件处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 如果点击关闭按钮,退出游戏
                running = False
                break
            elif event.type == pygame.MOUSEBUTTONDOWN:  # 鼠标按下,切换暂停状态
                pause = not pause
            elif event.type == pygame.KEYUP:  # 键盘松开,改变蛇的方向
                if event.key == K_LEFT:
                    if head in ['up', 'down']:
                        head = 'left'
                elif event.key == K_RIGHT:
                    if head in ['up', 'down']:
                        head = 'right'
                elif event.key == K_UP:
                    if head in ['left', 'right']:
                        head = 'up'
                elif event.key == K_DOWN:
                    if head in ['left', 'right']:
                        head = 'down'

        # 更新数据
        if not pause and not dead:
            count_time += frame * level  # 时间计数增加
            first = snake_list[0]  # 获取蛇头的位置
            snake_list[1:] = snake_list[:-1]  # 蛇身每一节的位置往前移动一格
            if head == 'up':  # 根据蛇的当前方向更新蛇头的位置
                snake_list[0] = [first[0], first[1] - SNAKE_WIDTH]
            elif head == 'down':
                snake_list[0] = [first[0], first[1] + SNAKE_WIDTH]
            elif head == 'left':
                snake_list[0] = [first[0] - SNAKE_WIDTH, first[1]]
            elif head == 'right':
                snake_list[0] = [first[0] + SNAKE_WIDTH, first[1]]

        # 绘制背景
        draw_background()
        # 绘制墙壁
        draw_wall()
        # 绘制蛇
        draw_snake()
        # 绘制食物
        draw_food()
        # 绘制下方的身体长度记录
        draw_context()
        # 绘制暂停界面
        if not dead and pause:
            draw_pause()
        # 绘制死亡界面
        if dead:
            draw_dead()
        # 更新屏幕显示
        pygame.display.flip()

        # 暂停20毫秒
        pygame.time.delay(int(frame / level * 1000))

        # 检查是否死亡
        dead = check_dead()

        # 检查是否吃到食物
        if check_food():
            add_food()  # 添加新食物

    # 退出pygame
    pygame.quit()

运行图:(由于我不知道怎么放动画,只能贴一张截图)


运行

死亡情况的图

写代码过程中我遇到的各种问题的解答如下

一、FONT_M.size('YOU DEAD')[0] / 2 是什么意思?

FONT_M.size('YOU DEAD')[0] / 2 是用于计算文本 "YOU DEAD" 的宽度一半的代码。

  1. FONT_M 是一个字体对象(pygame.font.SysFont('Times', 90)),用于渲染特定大小和样式的字体。
  2. FONT_M.size('YOU DEAD') 调用 size() 方法来获取字符串 "YOU DEAD" 在使用此字体对象渲染时的宽度和高度。size() 方法返回一个元组,格式为 (width, height),其中:
    • width 是文本的像素宽度。
    • height 是文本的像素高度。
  3. FONT_M.size('YOU DEAD')[0] 取元组的第一个元素,即文本的宽度。
  4. FONT_M.size('YOU DEAD')[0] / 2 将文本的宽度除以 2,得到文本宽度的一半。
    在游戏中,这段代码用于将 "YOU DEAD" 文本水平居中显示在屏幕的中间:
x = SIZE[0] / 2 - FONT_M.size('YOU DEAD')[0] / 2
  • SIZE[0] / 2 计算出屏幕的水平中心位置。
  • FONT_M.size('YOU DEAD')[0] / 2 计算出文本宽度的一半。
    通过从屏幕中心位置减去文本宽度的一半,x 位置就是文本左上角的坐标,使得文本 "YOU DEAD" 水平居中显示在屏幕上。

二、snake_list = [[100 + 12 * 4, 100], [100 + 12 * 3, 100], [100 + 12 * 2, 100], [100 + 12 * 1, 100], [100, 100]]这个初始化是什么意思?

snake_list 中的每个元素都是蛇身体部分的坐标,snake_list 初始化的是蛇身体每一节的中心点坐标,这些坐标让蛇在游戏屏幕上水平地从右向左排列,开始时长度为 5 个单位。蛇的每一节之间的距离是蛇的宽度 SNAKE_WIDTH(即 12 个像素)。
此游戏代码中的所有坐标位置初始化都是类似意思(表示中心点)。

三、right2 <= left1 or left2 >= right1 or down2 <= up1 or up2 >= down1为什么不相交的情况只有这四种?

建议画图辅助理解!!!

  1. rect2rect1 的左侧:
    right2 <= left1
    这意味着 rect2 的右边界在 rect1 的左边界的左侧或正好重合,因此两个矩形不重叠。
  2. rect2rect1 的右侧:
    left2 >= right1
    这意味着 rect2 的左边界在 rect1 的右边界的右侧或正好重合,因此两个矩形不重叠。
  3. rect2rect1 的上方:
    down2 <= up1
    这意味着 rect2 的下边界在 rect1 的上边界的上方或正好重合,因此两个矩形不重叠。
  4. rect2rect1 的下方:
    up2 >= down1
    这意味着 rect2 的上边界在 rect1 的下边界的下方或正好重合,因此两个矩形不重叠。
    为什么只有这四种情况?
    矩形是二维的封闭形状,如果两个矩形不重叠,则意味着它们在水平方向上没有交集,或者在垂直方向上没有交集。上面的四种情况涵盖了这两种可能性:
  • 水平方向: right2 <= left1left2 >= right1
  • 垂直方向: down2 <= up1up2 >= down1
    只要在水平方向或垂直方向上有一种情况成立,两个矩形就不会重叠。

四、add_body(xyz[2]) 为什么传入的参数是xyz[2]

add_body(xyz[2]) 的作用是根据食物的类型增加相应数量的蛇的身体节数。
food_list 是一个包含多个元组的列表。每个元组代表一个食物的信息,其中:

  • xyz[0]xyz[1] 表示食物在屏幕上的坐标位置 (x, y)
  • xyz[2] 表示食物的类型(或等级),例如,1, 2, 3, 4 等。每种类型可能对应不同的效果,例如类型为 1 的食物可能增加蛇 1 节,类型为 2 的食物可能增加蛇 2 节,依此类推。

五、为什么这里还要判断一下if head in ['up', 'down']:

                if event.key == K_LEFT:
                    if head in ['up', 'down']:
                        head = 'left'

这里的条件判断 if head in ['up', 'down']: 是为了避免蛇在相反方向上进行急转弯(即180度转弯),从而防止蛇直接撞到自己。
在游戏中,蛇只能沿着四个方向移动:'up'(向上)、'down'(向下)、'left'(向左)、'right'(向右)。为了防止蛇在移动时发生180度转弯,导致立即撞上自己的身体,代码中加入了一个检查条件来限制方向的改变。

  • 180度转弯会导致游戏失败:如果允许蛇进行180度转弯(例如,从'left'直接转为'right',或者从'up'直接转为'down'),蛇的头部会立即与其身体的第二节相撞,导致游戏失败。
  • 合理的控制逻辑:限制蛇只能转向左或右,而不能直接转向相反方向,是游戏控制中的一个合理设计。这样可以避免无意中的错误操作导致游戏过早结束。
  • 当用户按下 左键 (K_LEFT):
    • 代码检查当前蛇的移动方向是否为 'up''down'
    • 如果蛇正在向上或向下移动(head in ['up', 'down']),这意味着蛇可以转向左边('left')。因此,将蛇的方向设置为 'left'
    • 如果蛇正在向左或向右移动(head in ['left', 'right']),则不会改变蛇的方向,以避免180度转弯。
      其他情况同理。

六、snake_list[1:] = snake_list[:-1]详细解释?

此句为关键代码!!!!!
snake_list[1:] = snake_list[:-1] 是用于更新蛇的位置的一行代码。这个操作会让蛇的身体(除头部外的部分)跟随头部移动。
假设:

snake_list = [[100, 100], [88, 100], [76, 100], [64, 100], [52, 100]]

每个子列表 [x, y] 代表蛇的一节(身体部分)的坐标。上面的例子中,蛇的头在 [100, 100] 位置,其身体依次在 [88, 100], [76, 100], [64, 100], [52, 100] 位置。

  1. snake_list[:-1]: 这个切片操作表示从 snake_list 的开始到倒数第二个元素(不包括最后一个元素)的所有元素。因此,snake_list[:-1] 将返回蛇的身体部分(不包括尾巴)。在上面的例子中,snake_list[:-1] 结果是:
    [[100, 100], [88, 100], [76, 100], [64, 100]]
    
  2. snake_list[1:]: 这个切片操作表示从 snake_list 的第二个元素开始到最后一个元素的所有元素。因此,snake_list[1:] 将返回蛇的所有部分(不包括头部)。在上面的例子中,snake_list[1:] 结果是:
    [[88, 100], [76, 100], [64, 100], [52, 100]]
    
  3. 赋值操作 snake_list[1:] = snake_list[:-1]: 这个操作的目的是将蛇的每一节身体部分(不包括头)向前移动一格,继承前一节的坐标位置。
    • 在赋值操作之后,snake_list[1:] 的每个元素都将被更新为 snake_list[:-1] 的对应位置。
    • 结果是,蛇的身体的每一节都会移动到它的前一节的位置上。例如,赋值后 snake_list 变为:
    snake_list = [[100, 100], [100, 100], [88, 100], [76, 100], [64, 100]]
    
    注意,这里蛇的头部还是 [100, 100](它会在后续代码中更新),而原来的第二节身体([88, 100])现在变成了新的第二节位置,原来的第三节身体([76, 100])变成了新的第三节位置,依此类推。

七、Pygame屏幕的坐标系是什么样的?

这个问题很关键!!!!因为代码中存在大量坐标修改的式子,这个问题是修改坐标的前提,加减很容易出错,需要多在纸上模拟。
使用 Pygame 进行开发时,屏幕的坐标系通常如下:

  • X 轴: 从左向右增加。
  • Y 轴: 从上向下增加。
    这意味着:
  • x 坐标增加时,物体向右移动。
  • y 坐标增加时,物体向下移动。

八、怎么样让贪吃蛇移动的速度变慢,需要修改哪几个变量的数值?

  1. frame: 这是控制游戏循环间隔时间的变量。增大 frame 的值会增加每次循环之间的时间间隔,从而降低蛇的移动速度。
  2. level: 这是控制游戏速度的一个变量。减少 level 的值会减慢蛇的移动速度。
  3. pygame.time.delay(): 这个函数控制每次游戏循环后的等待时间。增大其参数值会让每次循环等待更长时间,从而使蛇的移动速度变慢。
    例如:
# 控制蛇移动的速度
frame = 0.05  # 增大这个值,比如改为 0.1,会让蛇的速度变慢
level = 1  # 减小这个值,比如改为 0.5,会让蛇的速度变慢

# 在主循环的末尾,控制每帧之间的延迟
pygame.time.delay(int(frame / level * 1000))

我认为此游戏还能改进的地方,待以后代码功力提高再实现

  1. 比如可以增加游戏难度,如随着时间的推移,逐渐增加蛇的速度或减少食物出现的间隔等等。添加不同的关卡,每个关卡有不同的障碍物布局或增加新的挑战(此代码只能玩一局,也没有一个最终胜利的机制)。
  2. 引入不同类型的食物,每种食物有不同的效果。虽然有draw_food()函数,且在这个函数中食物颜色有改变的公式,但是在主函数中food_list的z都为1,实际运行的时候食物只有(46, 139, 87)这一种颜色,有待改进。
  3. 设置更炫酷的界面(可以点击开始、暂停和游戏结束等,添加分数系统,根据蛇吃到的食物数量或游戏时间来计算分数)和图形,还可以添加背景音乐和游戏音效,如吃到食物、碰撞、游戏结束等音效。
    4.可以设置排行榜,记录和显示玩家的最高分数或游戏记录。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容