写点什么

rust 语言写的贪吃蛇游戏

  • 2022 年 9 月 27 日
    北京
  • 本文字数:4836 字

    阅读完需:约 16 分钟

rust语言写的贪吃蛇游戏

首先新建工程,然后用 vscode 打开,命令如下:


cargo new snake --bin
复制代码


文件结构如下:



Cargo.Toml 文件内容如下:


[package]name = "snake"version = "0.1.0"edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]rand = "0.4.6"piston_window="0.74.0"[profile.release]opt-level = 0lto = truecodegen-units = 1panic = "abort"
复制代码


src/draw.rs 文件内容如下:


use piston_window::types::Color;use piston_window::{rectangle, Context, G2d};const BLOCK_SIZE: f64 = 25.0;pub fn to_coord(game_coord: i32) -> f64 {    (game_coord as f64) * BLOCK_SIZE}pub fn to_coord_u32(game_coord: i32) -> u32 {    to_coord(game_coord) as u32}pub fn draw_block(color: Color, x: i32, y: i32, con: &Context, g: &mut G2d) {    let gui_x = to_coord(x);    let gui_y = to_coord(y);    rectangle(        color,        [gui_x, gui_y, BLOCK_SIZE, BLOCK_SIZE],        con.transform,        g,    );}pub fn draw_rectangle(    color: Color,    x: i32,    y: i32,    width: i32,    height: i32,    con: &Context,    g: &mut G2d,) {    let x = to_coord(x);    let y = to_coord(y);    rectangle(        color,        [            x,            y,            BLOCK_SIZE * (width as f64),            BLOCK_SIZE * (height as f64),        ],        con.transform,        g,    );}
复制代码


game.rs 文件内容如下:


use crate::draw::{draw_block, draw_rectangle};use piston_window::types::Color;use piston_window::*;use rand::{thread_rng, Rng};use crate::snake::{Direction, Snake};const FOOD_COLOR: Color = [0.80, 0.00, 0.00, 1.0];const BORDER_COLOR: Color = [0.80, 0.00, 0.00, 1.0];const GAMEOVER_COLOR: Color = [0.90, 0.00, 0.00, 0.5];const MOVING_PERIOD: f64 = 0.1;const RESTART_TIME: f64 = 1.0;
pub struct Game { snake: Snake, food_exists: bool, food_x: i32, food_y: i32, width: i32, height: i32, game_over: bool, waiting_time: f64,}impl Game { pub fn new(width: i32, height: i32) -> Game { Game { snake: Snake::new(2, 2), food_exists: true, food_x: 6, food_y: 4, width, height, game_over: false, waiting_time: 0.0, } } pub fn key_pressed(&mut self, key: Key) { if self.game_over { return; } let dir = match key { Key::Up => Some(Direction::Up), Key::Down => Some(Direction::Down), Key::Left => Some(Direction::Left), Key::Right => Some(Direction::Right), _ => None, }; if dir.unwrap() == self.snake.head_direction().opposite() { return; } self.update_snake(dir); } pub fn draw(&self, con: &Context, g: &mut G2d) { self.snake.draw(con, g); if self.food_exists { draw_block(FOOD_COLOR, self.food_x, self.food_y, con, g); } draw_rectangle(BORDER_COLOR, 0, 0, self.width, 1, con, g); draw_rectangle(BORDER_COLOR, 0, self.height - 1, self.width, 1, con, g); draw_rectangle(BORDER_COLOR, 0, 0, 1, self.height, con, g); draw_rectangle(BORDER_COLOR, self.width - 1, 0, 1, self.height, con, g); if self.game_over { draw_rectangle(GAMEOVER_COLOR, 0, 0, self.width, self.height, con, g); } } pub fn update(&mut self, delta_time: f64) { self.waiting_time += delta_time; if self.game_over { if self.waiting_time > RESTART_TIME { self.restart(); } return; } if !self.food_exists { self.add_food(); } if self.waiting_time > MOVING_PERIOD { self.update_snake(None); } } fn check_eating(&mut self) { let (head_x, head_y): (i32, i32) = self.snake.head_position(); if self.food_exists && self.food_x == head_x && self.food_y == head_y { self.food_exists = false; self.snake.restore_tail(); } } fn check_if_snake_alive(&self, dir: Option<Direction>) -> bool { let (next_x, next_y) = self.snake.next_head(dir); if self.snake.overlap_tail(next_x, next_y) { return false; } next_x > 0 && next_y > 0 && next_x < self.width - 1 && next_y < self.height - 1 } fn add_food(&mut self) { let mut rng = thread_rng(); let mut new_x = rng.gen_range(1, self.width - 1); let mut new_y = rng.gen_range(1, self.width - 1); while self.snake.overlap_tail(new_x, new_y) { new_x = rng.gen_range(1, self.width - 1); new_y = rng.gen_range(1, self.width - 1); } self.food_x = new_x; self.food_y = new_y; self.food_exists = true; } fn update_snake(&mut self, dir: Option<Direction>) { if self.check_if_snake_alive(dir) { self.snake.move_forward(dir); self.check_eating(); } else { self.game_over = true; } self.waiting_time = 0.0; } fn restart(&mut self) { self.snake = Snake::new(2, 2); self.waiting_time = 0.0; self.food_exists = true; self.food_x = 6; self.food_y = 4; self.game_over = false; }}
复制代码


main.rs 文件内容如下:


extern crate piston_window;extern crate rand;mod draw;mod game;mod snake;use draw::to_coord_u32;use game::Game;use piston_window::types::Color;use piston_window::*;const BACK_COLOR: Color = [0.5, 0.5, 0.5, 1.0];fn main() {    //https://magiclen.org/rust-compile-optimize/    let (width, height) = (30, 30);    let mut window: PistonWindow =        WindowSettings::new("Snake", [to_coord_u32(width), to_coord_u32(height)])            .exit_on_esc(true)            .build()            .unwrap();    let mut game = Game::new(width, height);    while let Some(event) = window.next() {        if let Some(Button::Keyboard(key)) = event.press_args() {            game.key_pressed(key);        }        window.draw_2d(&event, |c, g| {            clear(BACK_COLOR, g);            game.draw(&c, g);        });        event.update(|arg| {            game.update(arg.dt);        });    }}
复制代码


snake.rs 文件内容如下:


use piston_window::types::Color;use piston_window::{Context, G2d};use std::collections::LinkedList;
use crate::draw::draw_block;const SNAKE_COLOR: Color = [0.00, 0.80, 0.00, 1.0];#[derive(Copy, Clone, PartialEq)]pub enum Direction { Up, Down, Left, Right,}impl Direction { pub fn opposite(&self) -> Direction { match *self { Direction::Up => Direction::Down, Direction::Down => Direction::Up, Direction::Left => Direction::Right, Direction::Right => Direction::Left, } }}#[derive(Debug, Clone)]struct Block { x: i32, y: i32,}pub struct Snake { direction: Direction, body: LinkedList<Block>, tail: Option<Block>,}impl Snake { pub fn new(x: i32, y: i32) -> Snake { let mut body: LinkedList<Block> = LinkedList::new(); body.push_back(Block { x: x + 2, y }); body.push_back(Block { x: x + 1, y }); body.push_back(Block { x, y }); Snake { direction: Direction::Right, body, tail: None, } } pub fn draw(&self, con: &Context, g: &mut G2d) { for block in &self.body { draw_block(SNAKE_COLOR, block.x, block.y, con, g); } } pub fn head_position(&self) -> (i32, i32) { let head_block = self.body.front().unwrap(); (head_block.x, head_block.y) } pub fn move_forward(&mut self, dir: Option<Direction>) { match dir { Some(d) => self.direction = d, None => (), } let (last_x, last_y): (i32, i32) = self.head_position(); let new_block = match self.direction { Direction::Up => Block { x: last_x, y: last_y - 1, }, Direction::Down => Block { x: last_x, y: last_y + 1, }, Direction::Left => Block { x: last_x - 1, y: last_y, }, Direction::Right => Block { x: last_x + 1, y: last_y, }, }; self.body.push_front(new_block); let removed_block = self.body.pop_back().unwrap(); self.tail = Some(removed_block); } pub fn head_direction(&self) -> Direction { self.direction } pub fn next_head(&self, dir: Option<Direction>) -> (i32, i32) { let (head_x, head_y): (i32, i32) = self.head_position(); let mut moving_dir = self.direction; match dir { Some(d) => moving_dir = d, None => {} } match moving_dir { Direction::Up => (head_x, head_y - 1), Direction::Down => (head_x, head_y + 1), Direction::Left => (head_x - 1, head_y), Direction::Right => (head_x + 1, head_y), } } pub fn restore_tail(&mut self) { let blk = self.tail.clone().unwrap(); self.body.push_back(blk); } pub fn overlap_tail(&self, x: i32, y: i32) -> bool { let mut ch = 0; for block in &self.body { if x == block.x && y == block.y { return true; } ch += 1; if ch == self.body.len() - 1 { break; } } return false; }}
复制代码


cargo build --release
复制代码


./target/release/snake.exe
复制代码


结果如下:





代码见github

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2021.02.15 加入

还未添加个人简介

评论

发布
暂无评论
rust语言写的贪吃蛇游戏_rust_福大大架构师每日一题_InfoQ写作社区