一个非常简单的小项目。
看到了杨旭大佬的教学视频,自己跟着实现了一下,完善了一下游戏逻辑。
通过空格键进行控制。
游戏中可按 P 键 暂停/恢复 游戏

项目结构

·├── Cargo.lock├── Cargo.toml├── src/│ ├── main.rs│ ├──bird/│ │ ├── bird.rs│ │ └── mod.rs│ ├──game/│ │ ├── game.rs│ │  └── mod.rs│ └──obstacles/│   ├──obstacle.rs│   └── mod.rs
  • game.rs 负责游戏的逻辑、控制、等内容。
  • bird.rs 负责小鸟本身的实现。
  • obstacle.rs 负责障碍物的实现。

三个mod.rs 文件

// game/mod.rspub mod game;// bird/mod.rspub mod bird;// obstacles/mod.rspub mod obstacle;

main.rs

use bracket_lib::prelude::{main_loop, BError, BTermBuilder};pub mod bird;pub mod game;pub mod obstacles;fn main() -> BError {    // 创建窗口    let ctx = BTermBuilder::simple80x50()        .with_title("Flappy Bird")        .build()        .unwrap();    // 创建游戏内容    let game = game::game::State::new();    // 初始化游戏 loop    main_loop(ctx, game)}

game.rs

use crate::bird::bird;use crate::obstacles::obstacle;use bracket_lib::{    prelude::{BTerm, GameState},    terminal::{VirtualKeyCode, LIGHT_BLUE},};use std::collections::LinkedList;/// 游戏窗口的宽const GAME_WIDTH: u32 = 80;/// 游戏窗口的高const GAME_HEIGHT: u32 = 50;/// 游戏周期const PERIOD: f32 = 120.0;/// 游戏模式enum GameMode {    /// 游戏中    Playing,    /// 游戏结束    End,    /// 游戏暂停    Paused,    /// 菜单页面    Menu,}/// 游戏 Statepub struct State {    /// 当前模式    mode: GameMode,    /// 得分    score: u32,    /// 小鸟    bird: bird::Bird,    /// 等待时间    waite_time: f32,    /// 障碍物列表,因为需要从头尾两处进行操作,所以选择使用双向链表    obstacle_list: LinkedList,}/// bracket_lib::prelude::State 的实现impl GameState for State {    fn tick(&mut self, ctx: &mut BTerm) {        match self.mode {            GameMode::Playing => self.paly_control(ctx),            GameMode::End => self.end_control(ctx),            GameMode::Menu => self.menu_control(ctx),            GameMode::Paused => self.paused_control(ctx),        }    }}impl State {    /// 游戏状态初始化    pub fn new() -> Self {        let mut obstacle_list = LinkedList::new();        obstacle_list.push_back(obstacle::OBstacle::new(0, 35, GAME_HEIGHT));        obstacle_list.push_back(obstacle::OBstacle::new(0, 50, GAME_HEIGHT));        obstacle_list.push_back(obstacle::OBstacle::new(0, 65, GAME_HEIGHT));        obstacle_list.push_back(obstacle::OBstacle::new(0, 80, GAME_HEIGHT));        Self {            mode: GameMode::Menu,            score: 0,            bird: bird::Bird::new(GAME_WIDTH, GAME_HEIGHT),            waite_time: 0.0,            obstacle_list,        }    }    /// 重置游戏内容的方法    fn reset(&mut self) {        self.mode = GameMode::Playing;        self.waite_time = 0.0;        self.bird = bird::Bird::new(GAME_WIDTH, GAME_HEIGHT);        self.score = 0;        let mut obstacle_list = LinkedList::new();        obstacle_list.push_back(obstacle::OBstacle::new(0, 35, GAME_HEIGHT));        obstacle_list.push_back(obstacle::OBstacle::new(0, 50, GAME_HEIGHT));        obstacle_list.push_back(obstacle::OBstacle::new(0, 65, GAME_HEIGHT));        obstacle_list.push_back(obstacle::OBstacle::new(0, 80, GAME_HEIGHT));        self.obstacle_list = obstacle_list;    }    /// 游戏中的控制方法    fn paly_control(&mut self, ctx: &mut BTerm) {        ctx.cls();        ctx.set_bg(GAME_WIDTH, GAME_HEIGHT, LIGHT_BLUE);        // tick 函数的调用周期 ctx.frame_time_ms =33        self.waite_time += ctx.frame_time_ms;        // 达到等待时间了,需要执行游戏内容        if self.waite_time >= PERIOD {            self.bird.gravity_and_move();            self.move_obstacles();            self.pass_obstacles();            self.waite_time = 0.0;        }        if let Some(key) = ctx.key {            match key {                VirtualKeyCode::P => self.mode = GameMode::Paused,                VirtualKeyCode::Space => {                    self.bird.flap();                    self.waite_time = 0.0;                }                VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quit(),                _ => (),            }        }        self.bird.draw(ctx);        for obt in &self.obstacle_list {            obt.draw(GAME_HEIGHT, ctx)        }        ctx.print(0, 0, format!("Score :{}", self.score));        ctx.print(0, 1, "Space to flap");        ctx.print(0, 2, "(Q/Esc) Quit game)");        if self.bird.bird_out(GAME_HEIGHT) {            self.mode = GameMode::End        }    }    /// 移动障碍物    fn move_obstacles(&mut self) {        for obt in &mut self.obstacle_list {            obt.move_forward(1);        }    }    /// 通过障碍物检测,并记分    fn pass_obstacles(&mut self) {        let first_obt = self.obstacle_list.front().unwrap();        if first_obt.obstacle_x()  first_top && dragon_y  {                    self.reset();                    self.mode = GameMode::Playing;                }                VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quitting = true,                _ => (),            }        }    }    /// 菜单状态的控制方法    fn menu_control(&mut self, ctx: &mut BTerm) {        ctx.cls();        ctx.print_centered(6, "Welcome to flappy dragon ");        ctx.print_centered(8, " (S) Start game");        ctx.print_centered(9, " (P) Paused game");        ctx.print_centered(10, " (Q/Esc) Exit ");        if let Some(key) = ctx.key {            match key {                VirtualKeyCode::S => self.mode = GameMode::Playing,                VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quit(),                _ => (),            }        }    }    /// 游戏暂停时的控制方法    fn paused_control(&mut self, ctx: &mut BTerm) {        ctx.print_centered(0, format!("Score :{}", self.score));        ctx.print_centered(7, "Game Paused, (P) to return game!");        self.bird.draw(ctx);        if let Some(key) = ctx.key {            match key {                VirtualKeyCode::P => self.mode = GameMode::Playing,                VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quit(),                _ => (),            }        }    }}

bird.rs

use bracket_lib::prelude::BTerm;use bracket_lib::terminal::{to_cp437, BLACK, YELLOW_GREEN};/// 小鸟pub struct Bird {    /// x坐标    x: f64,    /// y坐标    y: f64,    /// 下落速度    velocity: f64,}impl Bird {    /// 初始化小鸟    pub fn new(game_width: u32, game_height: u32) -> Self {        Self {            x: (game_width / 5) as f64,            y: (game_height / 2) as f64,            velocity: 0.1,        }    }    /// 绘制    pub fn draw(&self, ctx: &mut BTerm) {        ctx.set(            self.x as u32,            self.y as u32,            YELLOW_GREEN,            BLACK,            to_cp437('&'),        );    }    /// 获取当前的 x 坐标    pub fn bird_x(&self) -> f64 {        self.x    }    /// 获取当前的 y 坐标    pub fn bird_y(&self) -> f64 {        self.y    }    /// 自由下落    pub fn gravity_and_move(&mut self) {        self.velocity += 0.1;        self.y += self.get_velocity();    }    /// 点击空格向上    pub fn flap(&mut self) {        self.y -= 2.0;        self.velocity = 0.1;    }    /// 是否超出边界    pub fn bird_out(&self, max_y: u32) -> bool {        self.y as u32 > max_y || self.y  f64 {        if self.velocity > 2.0 {            self.velocity = 2.0        }        self.velocity    }}

obstacle.rs

use bracket_lib::{    prelude::BTerm,    random::RandomNumberGenerator,    terminal::{to_cp437, BLACK, RED},};/// 障碍物pub struct OBstacle {    /// 障碍物的横坐标    x: u32,    /// 缝隙高度    size: u32,    /// 障碍物的中心点    gap_y: u32,}impl OBstacle {    /// 障碍物初始化    ///    /// *score 当前得分    ///    /// *start_x 初始x坐标    ///    /// *screen_height 游戏窗口高度    pub fn new(score: u32, start_x: u32, screen_height: u32) -> Self {        let mut random = RandomNumberGenerator::new();        let size;        if score > 60 {            size = 2        } else {            size = u32::max(2, 20 - score / 3);        }        let half_size = size / 2 + 1;        Self {            x: start_x,            size,            gap_y: random.range(half_size, screen_height - half_size),        }    }    /// 向前移动    ///    /// *distance 向前移动的距离    pub fn move_forward(&mut self, distance: u32) {        self.x -= distance    }    pub fn obstacle_x(&self) -> u32 {        self.x    }    pub fn obstacle_size(&self) -> u32 {        self.size    }    pub fn obstacle_gap_y(&self) -> u32 {        self.gap_y    }    /// 绘制    pub fn draw(&self, screen_height: u32, ctx: &mut BTerm) {        let screen_x = self.x;        let half_size = self.size / 2;        for y in 0..self.gap_y - half_size {            ctx.set(screen_x, y, RED, BLACK, to_cp437('#'));        }        for y in self.gap_y + half_size..screen_height {            ctx.set(screen_x, y, RED, BLACK, to_cp437('#'));        }    }}

Cargo.toml

[package]name = "flappy_bird"version = "0.1.0"edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]bracket-lib = "0.8.7"

Rust官网
Rust 中文社区

本文来自博客园,作者:贤云曳贺,转载请注明原文链接:https://www.cnblogs.com/SantiagoZhang/p/17297670.html