贪吃蛇游戏:从零开发
原理
游戏循环结构:
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 },输入控制
碰撞检测:蛇头 自身任意段落 → 游戏结束。蛇头 食物 → 增长一节。
使用 piston 或 macroquad 渲染。macroquad 更简单:clear() + draw_rectangle() 直接绘制彩色方块。
帧率通常 8-15fps(根据难度),通过 async_std::task::sleep 或 std::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 自检
VecDeque为什么适合蛇身数据结构?push_front+pop_back的复杂度?- 游戏循环中使用
async和thread::sleep的区别?对 CPU 负载的影响?