1. 环境准备与Pygame基础
要开发贪吃蛇游戏,首先需要搭建Python开发环境。我推荐使用Python 3.8+版本,这个版本对Pygame库的兼容性最好。安装Pygame只需要一行命令:
pip install pygame
安装完成后,可以创建一个简单的测试脚本来验证是否安装成功:
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Pygame测试")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255, 255, 255))
pygame.display.flip()
pygame.quit()
这个基础框架展示了Pygame的核心工作流程:初始化、创建窗口、事件循环和渲染。在贪吃蛇游戏中,我们会在这个基础上添加更多功能模块。
Pygame的坐标系以左上角为原点(0,0),x轴向右增加,y轴向下增加。这个坐标系系统在定位游戏元素时非常重要。游戏中的每个"帧"都会重新绘制整个画面,通过快速连续的画面更新来产生动画效果。
2. 游戏核心架构设计
贪吃蛇游戏的核心逻辑可以分为几个关键模块:蛇的移动控制、食物生成、碰撞检测和游戏状态管理。我们先来看蛇的数据结构设计。
蛇的身体可以用一个双端队列(deque)来存储,这样在头部添加新节点和在尾部删除节点的操作都非常高效:
from collections import deque
snake = deque([(10, 5), (9, 5), (8, 5)]) # 初始蛇身
direction = (1, 0) # 初始向右移动
def move_snake():
head_x, head_y = snake[0]
new_head = (head_x + direction[0], head_y + direction[1])
snake.appendleft(new_head) # 在头部添加新位置
snake.pop() # 删除尾部位置
食物生成需要考虑不与蛇身重叠:
def generate_food():
while True:
food_pos = (random.randint(0, 19), random.randint(0, 19))
if food_pos not in snake:
return food_pos
游戏主循环的基本结构如下:
clock = pygame.time.Clock()
FPS = 10 # 控制游戏速度
while True:
# 处理输入事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
# 处理方向键改变方向
# 游戏逻辑更新
move_snake()
check_collision()
# 渲染
draw_background()
draw_snake()
draw_food()
pygame.display.flip()
clock.tick(FPS) # 控制帧率
3. 蛇的移动与控制实现
蛇的移动控制是游戏的核心机制之一。我们需要处理玩家的输入,并确保蛇的移动方向变化是合理的(不能直接180度转向)。
首先定义方向常量:
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
方向控制逻辑需要注意防止直接反向移动:
direction = RIGHT # 初始方向
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and direction != DOWN:
direction = UP
elif event.key == pygame.K_DOWN and direction != UP:
direction = DOWN
elif event.key == pygame.K_LEFT and direction != RIGHT:
direction = LEFT
elif event.key == pygame.K_RIGHT and direction != LEFT:
direction = RIGHT
蛇的移动需要处理吃到食物时增长的情况:
def move_snake():
head_x, head_y = snake[0]
new_head = (head_x + direction[0], head_y + direction[1])
snake.appendleft(new_head)
# 如果没吃到食物,删除尾部
if new_head != food_pos:
snake.pop()
else:
# 生成新食物
food_pos = generate_food()
# 增加分数
global score
score += 10
为了让游戏体验更好,可以随着分数增加逐渐提高游戏速度:
if score % 100 == 0 and score > 0:
FPS += 1 # 每100分加速一次
4. 碰撞检测与游戏逻辑
碰撞检测包括与边界、自身以及食物的碰撞。边界检测相对简单:
def check_boundary_collision():
head = snake[0]
if head[0] < 0 or head[0] >= GRID_WIDTH or head[1] < 0 or head[1] >= GRID_HEIGHT:
return True
return False
自身碰撞检测需要检查头部是否与身体其他部分重叠:
def check_self_collision():
head = snake[0]
for segment in list(snake)[1:]:
if head == segment:
return True
return False
食物碰撞检测已经在move_snake函数中实现。当检测到碰撞时,需要处理游戏结束逻辑:
def game_over():
font = pygame.font.SysFont('Arial', 50)
text = font.render('Game Over!', True, (255, 0, 0))
screen.blit(text, (WIDTH//2 - text.get_width()//2, HEIGHT//2 - text.get_height()//2))
pygame.display.flip()
pygame.time.wait(2000) # 显示2秒
reset_game()
游戏重置函数需要将所有状态恢复到初始值:
def reset_game():
global snake, direction, food_pos, score, FPS
snake = deque([(10, 5), (9, 5), (8, 5)])
direction = RIGHT
food_pos = generate_food()
score = 0
FPS = 10
5. 游戏界面渲染与优化
游戏视觉效果对用户体验至关重要。我们先定义一些颜色常量:
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
绘制蛇身时,可以让头部和身体有视觉区分:
def draw_snake():
for i, (x, y) in enumerate(snake):
rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
if i == 0: # 头部
pygame.draw.rect(screen, BLUE, rect)
else: # 身体
pygame.draw.rect(screen, GREEN, rect)
pygame.draw.rect(screen, BLACK, rect, 1) # 边框
食物可以设计得更醒目一些:
def draw_food():
x, y = food_pos
center = (x * CELL_SIZE + CELL_SIZE//2, y * CELL_SIZE + CELL_SIZE//2)
pygame.draw.circle(screen, RED, center, CELL_SIZE//2)
添加分数显示和游戏状态提示:
def draw_hud():
font = pygame.font.SysFont('Arial', 20)
score_text = font.render(f'Score: {score}', True, WHITE)
fps_text = font.render(f'Speed: {FPS}', True, WHITE)
screen.blit(score_text, (10, 10))
screen.blit(fps_text, (10, 30))
# 暂停提示
if paused:
pause_text = font.render('PAUSED - Press SPACE to continue', True, WHITE)
screen.blit(pause_text, (WIDTH//2 - pause_text.get_width()//2, 10))
6. 完整代码实现与优化建议
将以上各部分组合起来,就得到了完整的贪吃蛇游戏。以下是完整的代码结构:
import pygame
import sys
import random
from collections import deque
# 初始化
pygame.init()
# 常量定义
CELL_SIZE = 20
GRID_WIDTH, GRID_HEIGHT = 30, 20
WIDTH, HEIGHT = CELL_SIZE * GRID_WIDTH, CELL_SIZE * GRID_HEIGHT
# 颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# 方向
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
# 游戏状态
snake = deque([(10, 5), (9, 5), (8, 5)])
direction = RIGHT
food_pos = (15, 10)
score = 0
FPS = 10
paused = False
# 初始化屏幕
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('贪吃蛇')
clock = pygame.time.Clock()
def generate_food():
while True:
food_pos = (random.randint(0, GRID_WIDTH-1), random.randint(0, GRID_HEIGHT-1))
if food_pos not in snake:
return food_pos
def move_snake():
global food_pos, score, FPS
head_x, head_y = snake[0]
new_head = (head_x + direction[0], head_y + direction[1])
snake.appendleft(new_head)
if new_head == food_pos:
food_pos = generate_food()
score += 10
if score % 100 == 0:
FPS += 1
else:
snake.pop()
def check_collision():
head = snake[0]
# 边界检测
if (head[0] < 0 or head[0] >= GRID_WIDTH or
head[1] < 0 or head[1] >= GRID_HEIGHT):
return True
# 自身碰撞
for segment in list(snake)[1:]:
if head == segment:
return True
return False
def draw_game():
screen.fill(BLACK)
# 画网格
for x in range(0, WIDTH, CELL_SIZE):
pygame.draw.line(screen, (50, 50, 50), (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, CELL_SIZE):
pygame.draw.line(screen, (50, 50, 50), (0, y), (WIDTH, y))
# 画蛇
for i, (x, y) in enumerate(snake):
rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
if i == 0:
pygame.draw.rect(screen, BLUE, rect)
else:
pygame.draw.rect(screen, GREEN, rect)
pygame.draw.rect(screen, BLACK, rect, 1)
# 画食物
x, y = food_pos
center = (x * CELL_SIZE + CELL_SIZE//2, y * CELL_SIZE + CELL_SIZE//2)
pygame.draw.circle(screen, RED, center, CELL_SIZE//2)
# 画HUD
font = pygame.font.SysFont('Arial', 20)
score_text = font.render(f'Score: {score}', True, WHITE)
fps_text = font.render(f'Speed: {FPS}', True, WHITE)
screen.blit(score_text, (10, 10))
screen.blit(fps_text, (10, 30))
if paused:
pause_text = font.render('PAUSED - Press SPACE to continue', True, WHITE)
screen.blit(pause_text, (WIDTH//2 - pause_text.get_width()//2, 10))
pygame.display.flip()
def reset_game():
global snake, direction, food_pos, score, FPS, paused
snake = deque([(10, 5), (9, 5), (8, 5)])
direction = RIGHT
food_pos = generate_food()
score = 0
FPS = 10
paused = False
def game_over():
font = pygame.font.SysFont('Arial', 50)
text = font.render('Game Over!', True, RED)
screen.blit(text, (WIDTH//2 - text.get_width()//2, HEIGHT//2 - text.get_height()//2))
pygame.display.flip()
pygame.time.wait(2000)
reset_game()
# 主循环
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and direction != DOWN:
direction = UP
elif event.key == pygame.K_DOWN and direction != UP:
direction = DOWN
elif event.key == pygame.K_LEFT and direction != RIGHT:
direction = LEFT
elif event.key == pygame.K_RIGHT and direction != LEFT:
direction = RIGHT
elif event.key == pygame.K_SPACE:
paused = not paused
elif event.key == pygame.K_r:
reset_game()
if not paused:
move_snake()
if check_collision():
game_over()
draw_game()
clock.tick(FPS)
pygame.quit()
sys.exit()
这个实现大约200行代码,包含了贪吃蛇游戏的所有核心功能。如果想进一步优化,可以考虑:
- 添加游戏音效和背景音乐
- 实现游戏开始界面和难度选择
- 添加特殊食物类型(加速、减速、加分等)
- 实现高分记录功能
- 优化视觉效果,如添加动画效果
在实际开发过程中,我发现使用双端队列(deque)来存储蛇身确实比普通列表(list)更高效,特别是在蛇变得很长时。另外,将游戏逻辑和渲染分离可以使代码更清晰,便于维护和扩展。
2万+

被折叠的 条评论
为什么被折叠?



