贪吃蛇 是一款经典的休闲小游戏:玩家通过操控一条会不断变长的“蛇”在屏幕中移动,去吃随机出现的食物,同时要避免撞到墙壁或自己身体的其他部分。由于其逻辑相对简单,但可玩性和扩展性都不错,非常适合作为新手练习游戏编程的项目。在本篇博客中,我们将使用 Python 语言 + Pygame 库来从零实现一款贪吃蛇。
1. 开发环境准备
- Python 3.x
- Pygame 库:如果尚未安装,请在命令行执行:
pip install pygame
- 图形界面环境:在常见的桌面系统(Windows、macOS、Linux)上都可以正常运行 Pygame 程序。
2. 游戏实现思路
要实现一个简易的贪吃蛇,主要需要以下几个要点:
-
游戏界面
- 一般的做法是将屏幕拆分成网格(如 20×20 的方格),蛇和食物都在这些方格坐标上移动。
- 每一个方格的大小可定义为 20×20 或更适合你需求的像素。
- 蛇的运动每次走一格,方向由玩家通过箭头键或 WASD 控制。
-
蛇的表示
- 通常用一个列表来储存蛇身每个“方块”的坐标(从头到尾依次存放)。
- 当蛇移动时,需要在头部插入一个新的坐标(根据方向计算),同时移除最后一个坐标(蛇尾)来保持“长度”不变;只有当蛇吃到食物时,才不移除尾部,从而实现“变长”。
-
食物的产生
- 在网格上随机产生一个坐标点,作为食物。
- 当蛇头和食物坐标重合时,表示蛇吃到了食物,此时增加蛇的长度并在其他空格处随机生成新的食物。
-
边界与身体碰撞检测
- 边界检测:如果蛇头超出屏幕范围,则表示撞墙,游戏结束。
- 身体碰撞检测:如果蛇头的坐标跟身体某个方块相同,表示咬到自己,游戏结束。
-
游戏循环
- 使用 Pygame 的事件与时钟,循环更新游戏状态:接收玩家按键,移动蛇头,检测碰撞,更新得分等。
3. 完整示例代码
将以下代码保存为 snake_game.py
并运行,即可体验一个最基本版本的贪吃蛇。你可以根据自己的需求对其中的参数或逻辑进行修改和完善。
python">import pygame
import random
import sys
# 初始化 Pygame
pygame.init()
# ---------------------
# 全局配置
# ---------------------
BLOCK_SIZE = 20 # 蛇和食物的大小(方块尺寸)
GRID_WIDTH = 30 # 水平方向方块数
GRID_HEIGHT = 20 # 垂直方向方块数
SCREEN_WIDTH = GRID_WIDTH * BLOCK_SIZE
SCREEN_HEIGHT = GRID_HEIGHT * BLOCK_SIZE
FPS = 10 # 游戏刷新率(蛇移动速度)
# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 200, 0)
GRAY = (50, 50, 50)
# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("贪吃蛇 - Pygame")
# 字体
font = pygame.font.SysFont("arial", 24)
# ---------------------
# 功能函数
# ---------------------
def draw_text(text, color, x, y):
"""在屏幕指定位置绘制文字"""
surface = font.render(text, True, color)
screen.blit(surface, (x, y))
def draw_block(color, x, y):
"""在(x, y)绘制一个BLOCK_SIZE大小的方块"""
rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
pygame.draw.rect(screen, color, rect)
# ---------------------
# 蛇类
# ---------------------
class Snake:
def __init__(self):
# 蛇初始位置(列表: 记录蛇身每个方块的坐标)
# 例如[(x1, y1), (x2, y2), ...] 从头到尾
self.body = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
self.direction = 'UP' # 初始方向:上
self.grow = False # 标识是否需要增长身体
def move(self):
# 计算新头部位置
head_x, head_y = self.body[0]
if self.direction == 'UP':
head_y -= 1
elif self.direction == 'DOWN':
head_y += 1
elif self.direction == 'LEFT':
head_x -= 1
elif self.direction == 'RIGHT':
head_x += 1
new_head = (head_x, head_y)
self.body.insert(0, new_head) # 头部插入到列表最前
# 如果不需要生长,则移除尾部
if not self.grow:
self.body.pop()
else:
self.grow = False # 本次已经完成增长
def change_direction(self, new_dir):
"""
根据玩家按键改变蛇的方向,
但需要防止蛇直接掉头(如当前在往左跑,不可立即改成往右)
"""
opposite = {'UP': 'DOWN', 'DOWN': 'UP', 'LEFT': 'RIGHT', 'RIGHT': 'LEFT'}
if new_dir != opposite[self.direction]:
self.direction = new_dir
def check_collision(self):
"""
检测是否撞墙或撞自己:
- 撞墙: 头部超出网格范围
- 撞自己: 头部坐标出现在身体其他部分
"""
head_x, head_y = self.body[0]
# 撞墙
if head_x < 0 or head_x >= GRID_WIDTH or head_y < 0 or head_y >= GRID_HEIGHT:
return True
# 撞自己
if self.body[0] in self.body[1:]:
return True
return False
def draw(self):
"""绘制蛇"""
for block in self.body:
x_coord = block[0] * BLOCK_SIZE
y_coord = block[1] * BLOCK_SIZE
draw_block(GREEN, x_coord, y_coord)
# ---------------------
# 食物类
# ---------------------
class Food:
def __init__(self):
self.position = self.random_pos()
def random_pos(self):
"""随机生成食物坐标(网格中)"""
return (random.randint(0, GRID_WIDTH - 1),
random.randint(0, GRID_HEIGHT - 1))
def draw(self):
"""绘制食物"""
x_coord = self.position[0] * BLOCK_SIZE
y_coord = self.position[1] * BLOCK_SIZE
draw_block(RED, x_coord, y_coord)
# ---------------------
# 游戏主函数
# ---------------------
def main():
clock = pygame.time.Clock()
snake = Snake()
food = Food()
score = 0
running = True
while running:
clock.tick(FPS) # 控制游戏帧率(蛇移动速度)
# 1) 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
snake.change_direction('UP')
elif event.key == pygame.K_DOWN:
snake.change_direction('DOWN')
elif event.key == pygame.K_LEFT:
snake.change_direction('LEFT')
elif event.key == pygame.K_RIGHT:
snake.change_direction('RIGHT')
# 2) 更新游戏状态 (蛇移动, 检测碰撞, 食物处理)
snake.move()
# 如果蛇头与食物位置相同 => 吃到食物
if snake.body[0] == food.position:
score += 1
snake.grow = True
food.position = food.random_pos()
# 检测蛇是否撞墙或撞自己
if snake.check_collision():
running = False
# 3) 绘制游戏界面
screen.fill(BLACK) # 清屏(背景黑色)
snake.draw()
food.draw()
draw_text(f"Score: {score}", WHITE, 10, 10)
pygame.display.flip()
# 如果跳出主循环,说明游戏结束
game_over(screen, score)
def game_over(surface, score):
"""
游戏结束界面,可在此添加重来或退出选项
"""
surface.fill(GRAY)
draw_text("Game Over!", WHITE, SCREEN_WIDTH // 2 - 60, SCREEN_HEIGHT // 2 - 30)
draw_text(f"Your Score: {score}", WHITE, SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 + 10)
pygame.display.flip()
# 等待若干秒后退出
pygame.time.wait(3000)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
核心逻辑解析
-
Snake 类
- 用一个列表
body
存储从头到尾的网格坐标。 move()
函数在前面插入新的头部坐标,如不需要增长则移除尾部。check_collision()
判断是否超出网格边界或头部与身体其他部分重合。
- 用一个列表
-
Food 类
- 只需保存一个
position
表示食物在网格的坐标。 - 当被吃到时,用
random_pos()
重新生成新的位置。
- 只需保存一个
-
主循环
- 通过
clock.tick(FPS)
控制蛇每秒移动多少次。 - 监听方向键来改变蛇的方向。
- 判断蛇头和食物是否重合,若重合则得分、食物重生、蛇变长。
- 若蛇撞墙或撞自己,则游戏结束并跳转到
game_over
界面。
- 通过
4. 实现效果
5. 总结
通过这篇文章,我们用 Python + Pygame 从零实现了一款简单的贪吃蛇游戏。该示例涵盖了网格移动、随机食物生成、碰撞检测、方向控制等基本概念,为你打下初步的游戏开发基础。