贪吃蛇游戏:从零开发

原理

游戏循环结构:

init()
while running:
    process_input()
    update_game_state()
    render()
    sleep(frame_duration)

贪吃蛇核心数据结构:

  • 蛇:VecDeque<Point>,吃食物 push_front,正常移动 push_front + pop_back
  • 食物:Point 随机生成,避免与蛇身重叠
  • 方向:enum Direction { Up, Down, Left, Right },输入控制

碰撞检测:蛇头 自身任意段落 → 游戏结束。蛇头 食物 → 增长一节。

使用 pistonmacroquad 渲染。macroquad 更简单:clear() + draw_rectangle() 直接绘制彩色方块。

帧率通常 8-15fps(根据难度),通过 async_std::task::sleepstd::thread::sleep 控制。


语法

[dependencies]
macroquad = "0.4"
use macroquad::prelude::*;
use std::collections::VecDeque;
 
#[derive(PartialEq)]
enum Dir { Up, Down, Left, Right }
 
struct Game {
    snake: VecDeque<(i32, i32)>,
    food: (i32, i32),
    dir: Dir,
    score: u32,
}
 
impl Game {
    fn new() -> Self {
        Self {
            snake: VecDeque::from([(5, 5)]),
            food: (10, 10),
            dir: Dir::Right,
            score: 0,
        }
    }
 
    fn update(&mut self) -> bool {
        let head = *self.snake.front().unwrap();
        let new_head = match self.dir {
            Dir::Up => (head.0, head.1 - 1),
            Dir::Down => (head.0, head.1 + 1),
            Dir::Left => (head.0 - 1, head.1),
            Dir::Right => (head.0 + 1, head.1),
        };
        if self.snake.contains(&new_head) { return false; }
        self.snake.push_front(new_head);
        if new_head == self.food {
            self.score += 1;
            self.food = (rand::gen_range(0, 20), rand::gen_range(0, 20));
        } else {
            self.snake.pop_back();
        }
        true
    }
}
 
#[macroquad::main("Snake")]
async fn main() {
    let mut game = Game::new();
    loop {
        if is_key_pressed(KeyCode::Up) { game.dir = Dir::Up; }
        if is_key_pressed(KeyCode::Down) { game.dir = Dir::Down; }
        // ...
 
        if !game.update() { break; }
 
        clear_background(BLACK);
        for (x, y) in &game.snake {
            draw_rectangle(*x as f32 * 20.0, *y as f32 * 20.0, 18.0, 18.0, GREEN);
        }
        next_frame().await;
    }
}

实践

力扣问题

力扣: 力扣搜索题 — 搜索与碰撞检测思想

AI 自检

  1. VecDeque 为什么适合蛇身数据结构?push_front + pop_back 的复杂度?
  2. 游戏循环中使用 asyncthread::sleep 的区别?对 CPU 负载的影响?