一、引言
贪吃蛇作为一款风靡全球的经典小游戏,是许多人接触电子游戏的启蒙作品。其核心玩法简单却富有挑战性 —— 控制蛇不断吃掉食物增长身体,同时避免撞到边界或自身。本文将基于 Python 的 Pygame 库,详细解析如何实现一个完整的贪吃蛇游戏,适合初学者学习游戏开发的基础逻辑与面向对象编程思想。
二、技术选型与环境准备
(一)技术栈选择
Pygame:Python 专用的游戏开发库,提供图形渲染、事件处理、音频支持等功能,适合 2D 游戏开发
面向对象编程:通过类封装蛇和食物的行为,提高代码可维护性
网格系统:基于固定格子大小(20x20 像素)设计,简化位置计算与碰撞检测
(二)环境配置
安装依赖:
bash
pip install pygame
开发工具:
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)
三阶段循环:
事件处理:捕获用户输入(方向键控制、退出操作)
逻辑更新:处理蛇的移动、碰撞检测、食物交互
画面渲染:清空屏幕→绘制元素→更新显示
性能与流畅度:通过固定帧率(10FPS)平衡游戏难度与运行效率
四、技术亮点与优化方向
(一)当前实现亮点
面向对象设计:将蛇和食物的行为封装为独立类,代码结构清晰易扩展
网格对齐机制:避免浮点运算,简化位置计算与碰撞检测逻辑
边界与自碰撞检测:完整实现游戏结束条件,增强玩法挑战性
(二)可扩展功能建议
分数系统:每次吃掉食物增加分数,显示在屏幕顶部
python
score = 0 # 食物碰撞时加分 if snake.body[0] == (food.x, food.y): score += 10 # 每次加10分 # ...
难度调节:通过增加帧率或缩小格子大小提升游戏速度
音效与界面美化:
添加吃食物音效和碰撞音效
使用图片代替纯色矩形,设计更美观的 UI
游戏结束界面:显示最终得分,提供重新开始选项
五、总结与学习价值
(一)项目价值
入门游戏开发:通过简单案例掌握 Pygame 的基本用法(事件处理、图形渲染、帧率控制)
理解游戏循环:掌握 “事件处理→逻辑更新→画面渲染” 的核心游戏架构
实践 OOP 思想:学会用类封装对象行为,理解数据与方法的绑定关系
(二)代码优化建议
代码重构:将颜色、尺寸等常量提取到独立配置模块
异常处理:添加初始化失败或资源加载错误的异常捕获
性能优化:使用更高效的数据结构(如双端队列)存储蛇的身体,提升移动操作效率
(三)运行效果
操作说明:方向键控制蛇的移动方向,吃掉红色食物增长身体,避免触碰边界或自身
游戏结束:碰撞发生时窗口关闭,可通过修改主循环添加重试机制
通过这个项目,开发者可以深入理解 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()
贪吃蛇游戏的魅力在于其简单规则下的无限挑战,而通过编程实现这个经典游戏,更是一次将逻辑思维转化为实际应用的绝佳实践。无论是初学者还是有经验的开发者,都能从这个项目中收获对游戏开发的基础认知与编码乐趣。