Pygame贪吃蛇:如何实现每帧仅处理一次转向按键?
贪吃蛇单帧仅处理一次转向的实现优化
核心思路
通过维护一个按键事件队列,每帧只处理队列中的第一个有效转向按键,剩余按键留到后续帧依次处理,从根本上避免同一帧内多次转向导致的撞身问题。同时优化原代码中的事件循环、方向判断等细节问题。
改进后的完整代码
import pygame import sys import random # 定义蛇身节点类 class Body: def __init__(self, properties=(0, 0, 20, 20), colour=-1): self.properties = properties self.colour = colour self.next = None # 定义果实类 class Fruit: def __init__(self, centre=(0, 0), size=10): self.centre = centre self.size = size def draw_snake(window, snake): # 绘制蛇头 pygame.draw.rect(window, (0, 0, 0), snake.properties) snake.colour *= -1 temp = snake.next # 绘制蛇身,交替颜色 while temp: colour = (0, 150, 0) if temp.colour == -1 else (0, 100, 0) temp.colour *= -1 pygame.draw.rect(window, colour, temp.properties) temp = temp.next return snake def draw_apple(window, snake, size): grid_size = size * 2 max_pos = (500 // grid_size) - 1 # 生成在网格上的果实位置 while True: x = random.randint(0, max_pos) * grid_size + size y = random.randint(0, max_pos) * grid_size + size # 检查是否生成在蛇身上 temp = snake overlap = False while temp: if temp.properties[0] == x - size and temp.properties[1] == y - size: overlap = True break temp = temp.next if not overlap: break apple = Fruit((x, y), size) pygame.draw.circle(window, "red", apple.centre, apple.size) return apple def new_game(window): window.fill((255, 255, 255)) snake = Body((240, 240, 20, 20), -1) draw_snake(window, snake) apple = draw_apple(window, snake, 10) return snake, apple def die(window, snake): # 蛇头变红提示死亡 pygame.draw.rect(window, (180, 0, 0), snake.properties) pygame.display.update() pygame.time.wait(1000) def get_new_direction(current_dir, key): # 用元组存储方向,避免列表引用比较的问题 if key == pygame.K_w and current_dir != (0, 20): return (0, -20) elif key == pygame.K_a and current_dir != (20, 0): return (-20, 0) elif key == pygame.K_s and current_dir != (0, -20): return (0, 20) elif key == pygame.K_d and current_dir != (-20, 0): return (20, 0) # 无效按键,返回原方向 return current_dir def move_snake(window, snake, apple, direction, length): head_x, head_y, _, _ = snake.properties new_head_x = head_x + direction[0] new_head_y = head_y + direction[1] # 边界碰撞检测 if new_head_x < 0 or new_head_y < 0 or new_head_x > 480 or new_head_y > 480: die(window, snake) return new_game(window) + (0, True) # 创建新蛇头 new_head = Body((new_head_x, new_head_y, 20, 20), snake.colour * -1) new_head.next = snake snake = new_head # 检查是否吃到果实 if (new_head_x, new_head_y) == (apple.centre[0] - 10, apple.centre[1] - 10): length += 1 apple = draw_apple(window, snake, 10) else: # 检查蛇身碰撞 temp = snake.next while temp.next: if (temp.next.properties[0], temp.next.properties[1]) == (new_head_x, new_head_y): die(window, snake) return new_game(window) + (0, True) temp = temp.next # 删除蛇尾 pygame.draw.rect(window, "white", temp.properties) temp.next = None return snake, apple, length, False def main(): pygame.init() window = pygame.display.set_mode((500, 500)) pygame.display.set_caption("贪吃蛇") snake, apple = new_game(window) length = 0 clock = pygame.time.Clock() game_started = False direction = (0, 0) key_queue = [] # 按键队列,存储未处理的转向按键 while True: # 处理所有事件,将有效转向按键加入队列 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if not game_started: # 第一次按键开始游戏,初始化方向 direction = get_new_direction((0,0), event.key) game_started = True else: # 仅将方向键加入队列 if event.key in [pygame.K_w, pygame.K_a, pygame.K_s, pygame.K_d]: key_queue.append(event.key) if game_started: # 每帧只处理队列中的第一个有效按键 if key_queue: direction = get_new_direction(direction, key_queue.pop(0)) # 移动蛇并更新画面 snake, apple, length, game_over = move_snake(window, snake, apple, direction, length) if game_over: game_started = False direction = (0,0) key_queue.clear() draw_snake(window, snake) pygame.display.update() # 控制游戏帧率(建议10-15帧,原1帧太慢) clock.tick(10) if __name__ == "__main__": main()
关键改进点说明
- 按键队列机制:用
key_queue存储玩家按下的方向键,每帧只取出队列头部的一个按键处理,剩余按键留到后续帧,确保单帧仅一次转向。 - 方向存储优化:用元组代替列表存储方向,避免引用类型比较的潜在问题,方向判断逻辑更可靠。
- 事件循环重构:改成单循环结构,避免嵌套循环导致的事件堆积问题,游戏状态控制更清晰。
- 帧率调整:将帧率从1调整为10,游戏体验更流畅,同时不影响转向逻辑。
- 果实生成优化:新增果实与蛇身的碰撞检测,避免果实生成在蛇身上。
内容的提问来源于stack exchange,提问作者DumDog6
相关产品推荐
相关产品推荐

