一、引言

贪吃蛇作为一款风靡全球的经典小游戏,是许多人接触电子游戏的启蒙作品。其核心玩法简单却富有挑战性 —— 控制蛇不断吃掉食物增长身体,同时避免撞到边界或自身。本文将基于 Python 的 Pygame 库,详细解析如何实现一个完整的贪吃蛇游戏,适合初学者学习游戏开发的基础逻辑与面向对象编程思想。

二、技术选型与环境准备

(一)技术栈选择

  • Pygame:Python 专用的游戏开发库,提供图形渲染、事件处理、音频支持等功能,适合 2D 游戏开发

  • 面向对象编程:通过类封装蛇和食物的行为,提高代码可维护性

  • 网格系统:基于固定格子大小(20x20 像素)设计,简化位置计算与碰撞检测

(二)环境配置

  1. 安装依赖:

    bash

    pip install pygame
    

  2. 开发工具:

    • Python 3.6+

    • Pygame 2.0+

    • 任意文本编辑器(推荐 VS Code/PyCharm)

三、核心游戏逻辑解析

(一)游戏初始化与界面设置

python

# 定义游戏基本参数
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600  # 屏幕尺寸
GRID_SIZE = 20  # 网格大小(蛇和食物的移动单位)
FPS = 10  # 游戏帧率

# 初始化Pygame并创建窗口
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("经典贪吃蛇")
clock = pygame.time.Clock()  # 用于控制帧率


  • 网格系统设计:所有元素(蛇、食物)的位置均基于网格坐标,确保移动时不会出现像素级偏差

  • 帧率控制:通过clock.tick(FPS)保证游戏运行速度稳定,避免过快或过慢

(二)蛇类(Snake Class):游戏核心角色

1. 初始化与属性

python

class Snake:
    def __init__(self, screen):
        self.screen = screen
        self.direction = "right"  # 初始方向向右
        self.body = [(400, 300), (380, 300), (360, 300)]  # 初始身体(3个格子)
        self.length = 3  # 初始长度


  • 身体表示:使用坐标元组列表(x, y)存储蛇的每个身体段位置,头部始终在列表开头

  • 方向控制:限制蛇不能反向移动(如右→左),避免穿模问题

2. 移动逻辑

python

def move(self):
    # 获取当前头部位置
    head_x, head_y = self.body[0]
    # 根据方向计算新头部位置
    if self.direction == "right":
        new_head = (head_x + GRID_SIZE, head_y)
    elif self.direction == "left":
        new_head = (head_x - GRID_SIZE, head_y)
    elif self.direction == "up":
        new_head = (head_x, head_y - GRID_SIZE)
    elif self.direction == "down":
        new_head = (head_x, head_y + GRID_SIZE)
    # 更新身体:新头部插入列表,尾部移除
    self.body.insert(0, new_head)
    self.body.pop()


  • 动画实现:通过不断更新头部位置并移除尾部,实现蛇的移动效果

  • 方向限制:在change_direction方法中检查是否为反向操作,确保逻辑合理

3. 碰撞检测

python

def check_collision(self):
    head = self.body[0]
    # 边界碰撞检测(触碰到屏幕边缘)
    if (head[0] < 0 or head[0] >= SCREEN_WIDTH or
        head[1] < 0 or head[1] >= SCREEN_HEIGHT):
        return True
    # 自碰撞检测(头部与身体其他部分重叠)
    if head in self.body[1:]:
        return True
    return False


  • 双重检测机制:同时检查边界碰撞和身体碰撞,触发游戏结束条件

  • 性能优化:通过切片self.body[1:]排除头部本身,提高检测效率

(三)食物类(Food Class):游戏目标物

1. 随机生成与位置校验

python

class Food:
    def __init__(self, screen, snake):
        self.screen = screen
        self.snake = snake
        # 生成不与蛇身重叠的初始位置
        self.x, self.y = self.generate_position()
    
    def generate_position(self):
        while True:
            x = random.randrange(0, SCREEN_WIDTH, GRID_SIZE)
            y = random.randrange(0, SCREEN_HEIGHT, GRID_SIZE)
            if (x, y) not in self.snake.body:
                return (x, y)


  • 避免重叠逻辑:通过循环检查生成的坐标是否在蛇的身体列表中,确保食物出现在合法位置

  • 网格对齐:利用randrange步长参数,保证食物位置与网格完全对齐

2. 视觉呈现

python

def draw(self):
    pygame.draw.rect(
        self.screen,  # 绘制目标
        FOOD_COLOR,  # 颜色(红色)
        (self.x, self.y, GRID_SIZE, GRID_SIZE)  # 位置与大小
    )


  • 图形绘制:使用 Pygame 的矩形绘制函数,实现食物的视觉化展示

(四)游戏主循环:事件处理与状态更新

python

game_over = False
while not game_over:
    # 事件处理(按键响应)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_over = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                snake.change_direction("right")
            # 其他方向键处理...
    
    # 游戏逻辑更新
    snake.move()
    if snake.check_collision():
        game_over = True
    # 食物碰撞检测
    if snake.body[0] == (food.x, food.y):
        snake.grow()  # 蛇身增长
        food = Food(screen, snake)  # 重新生成食物
    
    # 画面渲染
    screen.fill(BACKGROUND_COLOR)
    snake.draw()
    food.draw()
    pygame.display.flip()
    
    # 帧率控制
    clock.tick(FPS)


  • 三阶段循环

    1. 事件处理:捕获用户输入(方向键控制、退出操作)

    2. 逻辑更新:处理蛇的移动、碰撞检测、食物交互

    3. 画面渲染:清空屏幕→绘制元素→更新显示

  • 性能与流畅度:通过固定帧率(10FPS)平衡游戏难度与运行效率

四、技术亮点与优化方向

(一)当前实现亮点

  1. 面向对象设计:将蛇和食物的行为封装为独立类,代码结构清晰易扩展

  2. 网格对齐机制:避免浮点运算,简化位置计算与碰撞检测逻辑

  3. 边界与自碰撞检测:完整实现游戏结束条件,增强玩法挑战性

(二)可扩展功能建议

  1. 分数系统:每次吃掉食物增加分数,显示在屏幕顶部

    python

    score = 0
    # 食物碰撞时加分
    if snake.body[0] == (food.x, food.y):
        score += 10  # 每次加10分
        # ...
    

  2. 难度调节:通过增加帧率或缩小格子大小提升游戏速度

  3. 音效与界面美化

    • 添加吃食物音效和碰撞音效

    • 使用图片代替纯色矩形,设计更美观的 UI

  4. 游戏结束界面:显示最终得分,提供重新开始选项

五、总结与学习价值

(一)项目价值

  • 入门游戏开发:通过简单案例掌握 Pygame 的基本用法(事件处理、图形渲染、帧率控制)

  • 理解游戏循环:掌握 “事件处理→逻辑更新→画面渲染” 的核心游戏架构

  • 实践 OOP 思想:学会用类封装对象行为,理解数据与方法的绑定关系

(二)代码优化建议

  1. 代码重构:将颜色、尺寸等常量提取到独立配置模块

  2. 异常处理:添加初始化失败或资源加载错误的异常捕获

  3. 性能优化:使用更高效的数据结构(如双端队列)存储蛇的身体,提升移动操作效率

(三)运行效果

  • 操作说明:方向键控制蛇的移动方向,吃掉红色食物增长身体,避免触碰边界或自身

  • 游戏结束:碰撞发生时窗口关闭,可通过修改主循环添加重试机制


通过这个项目,开发者可以深入理解 2D 游戏的基本开发流程,同时为后续开发更复杂的游戏(如俄罗斯方块、飞机大战)打下坚实基础。完整代码已在 GitHub 开源(链接待补充),欢迎下载运行并尝试扩展功能!


python

# -*- coding: UTF-8 -*-
# @Author  :小王不会摆烂
# 导入pygame模块
import pygame
# 导入random模块
import random

# 定义屏幕的宽度和高度
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
# 定义格子的大小
GRID_SIZE = 20
# 定义蛇的颜色
SNAKE_COLOR = (0, 255, 0)
# 定义食物的颜色
FOOD_COLOR = (255, 0, 0)
# 定义背景的颜色
BACKGROUND_COLOR = (0, 0, 0)

# 初始化pygame
pygame.init()
# 创建一个窗口对象
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 设置窗口的标题
pygame.display.set_caption('贪吃蛇')
# 创建一个时钟对象,用于控制帧率
clock = pygame.time.Clock()

# 定义一个蛇类
class Snake:
    # 初始化方法,接受一个窗口对象作为参数
    def __init__(self, screen):
        # 设置窗口对象为属性
        self.screen = screen
        # 设置蛇的初始位置为屏幕中央
        self.x = SCREEN_WIDTH // 2
        self.y = SCREEN_HEIGHT // 2
        # 设置蛇的初始方向为右
        self.direction = 'right'
        # 设置蛇的初始长度为3个格子
        self.length = 3
        # 设置蛇的身体为一个列表,每个元素是一个(x, y)坐标对
        self.body = [(self.x - i * GRID_SIZE, self.y) for i in range(self.length)]

    # 定义一个移动方法,根据方向更新蛇的位置和身体
    def move(self):
        # 如果方向是右,那么x坐标增加一个格子大小,y坐标不变
        if self.direction == 'right':
            self.x += GRID_SIZE
            self.y += 0
        # 如果方向是左,那么x坐标减少一个格子大小,y坐标不变
        elif self.direction == 'left':
            self.x -= GRID_SIZE
            self.y += 0
        # 如果方向是上,那么x坐标不变,y坐标减少一个格子大小
        elif self.direction == 'up':
            self.x += 0
            self.y -= GRID_SIZE
        # 如果方向是下,那么x坐标不变,y坐标增加一个格子大小
        elif self.direction == 'down':
            self.x += 0
            self.y += GRID_SIZE

        # 将新的位置添加到身体列表的开头,表示蛇头移动到了这个位置
        self.body.insert(0, (self.x, self.y))
        # 删除身体列表的最后一个元素,表示蛇尾离开了这个位置
        self.body.pop()

    # 定义一个改变方向的方法,接受一个新的方向作为参数,并检查是否合法(不能反向)
    def change_direction(self, new_direction):
        # 如果新的方向是右,并且当前方向不是左,那么更新方向为右
        if new_direction == 'right' and self.direction != 'left':
            self.direction = new_direction
        # 如果新的方向是左,并且当前方向不是右,那么更新方向为左
        elif new_direction == 'left' and self.direction != 'right':
            self.direction = new_direction
            # 如果新的方向是上,并且当前方向不是下,那么更新方向为上
        elif new_direction == 'up' and self.direction != 'down':
            self.direction = new_direction
        # 如果新的方向是下,并且当前方向不是上,那么更新方向为下
        elif new_direction == 'down' and self.direction != 'up':
            self.direction = new_direction

    # 定义一个绘制方法,用于在窗口上画出蛇的身体
    def draw(self):
        # 遍历身体列表中的每个元素,也就是每个格子的位置
        for x, y in self.body:
            # 在对应的位置画一个矩形,颜色为蛇的颜色,宽度为0表示填充
            pygame.draw.rect(self.screen, SNAKE_COLOR, (x, y, GRID_SIZE, GRID_SIZE), 0)

    # 定义一个增长方法,用于在吃到食物后增加蛇的长度
    def grow(self):
        # 在身体列表的末尾添加一个元素,位置和最后一个元素相同,表示蛇尾增加了一个格子
        self.body.append(self.body[-1])
        # 蛇的长度属性加一
        self.length += 1

    # 定义一个检查碰撞的方法,用于判断蛇是否撞到了自己或者边界
    def check_collision(self):
        # 如果蛇头的x坐标小于0或者大于等于屏幕宽度,表示撞到了左右边界,返回True
        if self.x < 0 or self.x >= SCREEN_WIDTH:
            return True
        # 如果蛇头的y坐标小于0或者大于等于屏幕高度,表示撞到了上下边界,返回True
        if self.y < 0 or self.y >= SCREEN_HEIGHT:
            return True
        # 如果蛇头的位置在身体列表中出现了两次以上,表示撞到了自己的身体,返回True
        if self.body.count((self.x, self.y)) > 1:
            return True
        # 否则返回False,表示没有发生碰撞
        return False

# 定义一个食物类
class Food:
    # 初始化方法,接受一个窗口对象和一个蛇对象作为参数
    def __init__(self, screen, snake):
        # 设置窗口对象为属性
        self.screen = screen
        # 设置蛇对象为属性
        self.snake = snake
        # 随机生成食物的初始位置,保证不和蛇重叠
        self.x = random.randrange(0, SCREEN_WIDTH, GRID_SIZE)
        self.y = random.randrange(0, SCREEN_HEIGHT, GRID_SIZE)
        while (self.x, self.y) in snake.body:
            self.x = random.randrange(0, SCREEN_WIDTH, GRID_SIZE)
            self.y = random.randrange(0, SCREEN_HEIGHT, GRID_SIZE)

    # 定义一个绘制方法,用于在窗口上画出食物
    def draw(self):
        # 在食物的位置画一个矩形,颜色为食物的颜色,宽度为0表示填充
        pygame.draw.rect(self.screen, FOOD_COLOR, (self.x, self.y, GRID_SIZE, GRID_SIZE), 0)

    # 定义一个重置方法,用于在被吃掉后重新生成食物的位置,保证不和蛇重叠
    def reset(self):
        # 随机生成食物的新位置,保证不和蛇重叠
        self.x = random.randrange(0, SCREEN_WIDTH, GRID_SIZE)
        self.y = random.randrange(0, SCREEN_HEIGHT, GRID_SIZE)
        while (self.x, self.y) in snake.body:
            self.x = random.randrange(0, SCREEN_WIDTH, GRID_SIZE)
            self.y = random.randrange(0, SCREEN_HEIGHT, GRID_SIZE)
# 创建一个蛇对象
snake = Snake(screen)
# 创建一个食物对象
food = Food(screen, snake)

# 定义一个游戏是否结束的标志
game_over = False

# 进入游戏主循环
while not game_over:
    # 处理事件队列中的所有事件
    for event in pygame.event.get():
        # 如果事件是退出,那么结束游戏主循环
        if event.type == pygame.QUIT:
            game_over = True
        # 如果事件是按键,那么根据按键改变蛇的方向
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                snake.change_direction('right')
            elif event.key == pygame.K_LEFT:
                snake.change_direction('left')
            elif event.key == pygame.K_UP:
                snake.change_direction('up')
            elif event.key == pygame.K_DOWN:
                snake.change_direction('down')

    # 让蛇移动一步
    snake.move()
    # 检查蛇是否碰撞,如果是,那么结束游戏主循环
    if snake.check_collision():
        game_over = True
    # 检查蛇是否吃到了食物,如果是,那么让蛇增长,并重置食物的位置
    if snake.x == food.x and snake.y == food.y:
        snake.grow()
        food.reset()

    # 用背景颜色填充整个窗口
    screen.fill(BACKGROUND_COLOR)
    # 绘制蛇和食物
    snake.draw()
    food.draw()
    # 更新窗口的显示
    pygame.display.flip()
    # 设置帧率为10,也就是每秒10次循环
    clock.tick(10)

# 退出pygame
pygame.quit()


贪吃蛇游戏的魅力在于其简单规则下的无限挑战,而通过编程实现这个经典游戏,更是一次将逻辑思维转化为实际应用的绝佳实践。无论是初学者还是有经验的开发者,都能从这个项目中收获对游戏开发的基础认知与编码乐趣。