ToB企服应用市场:ToB评测及商务社交产业平台

标题: 一个简单的 rust项目 flappy bird [打印本页]

作者: 杀鸡焉用牛刀    时间: 2023-4-8 00:26
标题: 一个简单的 rust项目 flappy bird
一个非常简单的小项目。
看到了杨旭大佬的教学视频,自己跟着实现了一下,完善了一下游戏逻辑。
通过空格键进行控制。
游戏中可按 P 键 暂停/恢复 游戏
项目结构
  1. ·
  2. ├── Cargo.lock
  3. ├── Cargo.toml
  4. ├── src/
  5. │   ├── main.rs
  6. │   ├──bird/
  7. │   │  ├── bird.rs
  8. │   │  └── mod.rs
  9. │   ├──game/
  10. │   │   ├── game.rs
  11. │   │   └── mod.rs
  12. │   └──obstacles/
  13. │       ├──obstacle.rs
  14. │       └── mod.rs
复制代码
三个mod.rs 文件
  1. // game/mod.rs
  2. pub mod game;
  3. // bird/mod.rs
  4. pub mod bird;
  5. // obstacles/mod.rs
  6. pub mod obstacle;
复制代码
main.rs
  1. use bracket_lib::prelude::{main_loop, BError, BTermBuilder};
  2. pub mod bird;
  3. pub mod game;
  4. pub mod obstacles;
  5. fn main() -> BError {
  6.     // 创建窗口
  7.     let ctx = BTermBuilder::simple80x50()
  8.         .with_title("Flappy Bird")
  9.         .build()
  10.         .unwrap();
  11.     // 创建游戏内容
  12.     let game = game::game::State::new();
  13.     // 初始化游戏 loop
  14.     main_loop(ctx, game)
  15. }
复制代码
game.rs
  1. use crate::bird::bird;
  2. use crate::obstacles::obstacle;
  3. use bracket_lib::{
  4.     prelude::{BTerm, GameState},
  5.     terminal::{VirtualKeyCode, LIGHT_BLUE},
  6. };
  7. use std::collections::LinkedList;
  8. /// 游戏窗口的宽
  9. const GAME_WIDTH: u32 = 80;
  10. /// 游戏窗口的高
  11. const GAME_HEIGHT: u32 = 50;
  12. /// 游戏周期
  13. const PERIOD: f32 = 120.0;
  14. /// 游戏模式
  15. enum GameMode {
  16.     /// 游戏中
  17.     Playing,
  18.     /// 游戏结束
  19.     End,
  20.     /// 游戏暂停
  21.     Paused,
  22.     /// 菜单页面
  23.     Menu,
  24. }
  25. /// 游戏 State
  26. pub struct State {
  27.     /// 当前模式
  28.     mode: GameMode,
  29.     /// 得分
  30.     score: u32,
  31.     /// 小鸟
  32.     bird: bird::Bird,
  33.     /// 等待时间
  34.     waite_time: f32,
  35.     /// 障碍物列表,因为需要从头尾两处进行操作,所以选择使用双向链表
  36.     obstacle_list: LinkedList<obstacle::OBstacle>,
  37. }
  38. /// bracket_lib::prelude::State 的实现
  39. impl GameState for State {
  40.     fn tick(&mut self, ctx: &mut BTerm) {
  41.         match self.mode {
  42.             GameMode::Playing => self.paly_control(ctx),
  43.             GameMode::End => self.end_control(ctx),
  44.             GameMode::Menu => self.menu_control(ctx),
  45.             GameMode::Paused => self.paused_control(ctx),
  46.         }
  47.     }
  48. }
  49. impl State {
  50.     /// 游戏状态初始化
  51.     pub fn new() -> Self {
  52.         let mut obstacle_list = LinkedList::new();
  53.         obstacle_list.push_back(obstacle::OBstacle::new(0, 35, GAME_HEIGHT));
  54.         obstacle_list.push_back(obstacle::OBstacle::new(0, 50, GAME_HEIGHT));
  55.         obstacle_list.push_back(obstacle::OBstacle::new(0, 65, GAME_HEIGHT));
  56.         obstacle_list.push_back(obstacle::OBstacle::new(0, 80, GAME_HEIGHT));
  57.         Self {
  58.             mode: GameMode::Menu,
  59.             score: 0,
  60.             bird: bird::Bird::new(GAME_WIDTH, GAME_HEIGHT),
  61.             waite_time: 0.0,
  62.             obstacle_list,
  63.         }
  64.     }
  65.     /// 重置游戏内容的方法
  66.     fn reset(&mut self) {
  67.         self.mode = GameMode::Playing;
  68.         self.waite_time = 0.0;
  69.         self.bird = bird::Bird::new(GAME_WIDTH, GAME_HEIGHT);
  70.         self.score = 0;
  71.         let mut obstacle_list = LinkedList::new();
  72.         obstacle_list.push_back(obstacle::OBstacle::new(0, 35, GAME_HEIGHT));
  73.         obstacle_list.push_back(obstacle::OBstacle::new(0, 50, GAME_HEIGHT));
  74.         obstacle_list.push_back(obstacle::OBstacle::new(0, 65, GAME_HEIGHT));
  75.         obstacle_list.push_back(obstacle::OBstacle::new(0, 80, GAME_HEIGHT));
  76.         self.obstacle_list = obstacle_list;
  77.     }
  78.     /// 游戏中的控制方法
  79.     fn paly_control(&mut self, ctx: &mut BTerm) {
  80.         ctx.cls();
  81.         ctx.set_bg(GAME_WIDTH, GAME_HEIGHT, LIGHT_BLUE);
  82.         // tick 函数的调用周期 ctx.frame_time_ms =33
  83.         self.waite_time += ctx.frame_time_ms;
  84.         // 达到等待时间了,需要执行游戏内容
  85.         if self.waite_time >= PERIOD {
  86.             self.bird.gravity_and_move();
  87.             self.move_obstacles();
  88.             self.pass_obstacles();
  89.             self.waite_time = 0.0;
  90.         }
  91.         if let Some(key) = ctx.key {
  92.             match key {
  93.                 VirtualKeyCode::P => self.mode = GameMode::Paused,
  94.                 VirtualKeyCode::Space => {
  95.                     self.bird.flap();
  96.                     self.waite_time = 0.0;
  97.                 }
  98.                 VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quit(),
  99.                 _ => (),
  100.             }
  101.         }
  102.         self.bird.draw(ctx);
  103.         for obt in &self.obstacle_list {
  104.             obt.draw(GAME_HEIGHT, ctx)
  105.         }
  106.         ctx.print(0, 0, format!("Score :{}", self.score));
  107.         ctx.print(0, 1, "Space to flap");
  108.         ctx.print(0, 2, "(Q/Esc) Quit game)");
  109.         if self.bird.bird_out(GAME_HEIGHT) {
  110.             self.mode = GameMode::End
  111.         }
  112.     }
  113.     /// 移动障碍物
  114.     fn move_obstacles(&mut self) {
  115.         for obt in &mut self.obstacle_list {
  116.             obt.move_forward(1);
  117.         }
  118.     }
  119.     /// 通过障碍物检测,并记分
  120.     fn pass_obstacles(&mut self) {
  121.         let first_obt = self.obstacle_list.front().unwrap();
  122.         if first_obt.obstacle_x() <= self.bird.bird_x() as u32 {
  123.             let half_sie = first_obt.obstacle_size() / 2;
  124.             let first_top = first_obt.obstacle_gap_y() - half_sie;
  125.             let first_bottom = first_obt.obstacle_gap_y() + half_sie;
  126.             let dragon_y = self.bird.bird_y() as u32;
  127.             if dragon_y > first_top && dragon_y < first_bottom {
  128.                 self.score += 1;
  129.             } else {
  130.                 self.mode = GameMode::End;
  131.             }
  132.             self.obstacle_list.pop_front();
  133.             self.obstacle_list
  134.                 .push_back(obstacle::OBstacle::new(self.score, 80, GAME_HEIGHT));
  135.         }
  136.     }
  137.     /// 游戏结束时的控制方法
  138.     fn end_control(&mut self, ctx: &mut BTerm) {
  139.         ctx.cls();
  140.         ctx.print_centered(6, format!("You are DEAD ! with {} score. ", self.score));
  141.         ctx.print_centered(8, " (R)  Restart game");
  142.         ctx.print_centered(9, " (Q/Esc) Exit ");
  143.         if let Some(key) = ctx.key {
  144.             match key {
  145.                 VirtualKeyCode::R => {
  146.                     self.reset();
  147.                     self.mode = GameMode::Playing;
  148.                 }
  149.                 VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quitting = true,
  150.                 _ => (),
  151.             }
  152.         }
  153.     }
  154.     /// 菜单状态的控制方法
  155.     fn menu_control(&mut self, ctx: &mut BTerm) {
  156.         ctx.cls();
  157.         ctx.print_centered(6, "Welcome to flappy dragon ");
  158.         ctx.print_centered(8, " (S) Start game");
  159.         ctx.print_centered(9, " (P) Paused game");
  160.         ctx.print_centered(10, " (Q/Esc) Exit ");
  161.         if let Some(key) = ctx.key {
  162.             match key {
  163.                 VirtualKeyCode::S => self.mode = GameMode::Playing,
  164.                 VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quit(),
  165.                 _ => (),
  166.             }
  167.         }
  168.     }
  169.     /// 游戏暂停时的控制方法
  170.     fn paused_control(&mut self, ctx: &mut BTerm) {
  171.         ctx.print_centered(0, format!("Score :{}", self.score));
  172.         ctx.print_centered(7, "Game Paused, (P) to return game!");
  173.         self.bird.draw(ctx);
  174.         if let Some(key) = ctx.key {
  175.             match key {
  176.                 VirtualKeyCode::P => self.mode = GameMode::Playing,
  177.                 VirtualKeyCode::Q | VirtualKeyCode::Escape => ctx.quit(),
  178.                 _ => (),
  179.             }
  180.         }
  181.     }
  182. }
复制代码
bird.rs
  1. use bracket_lib::prelude::BTerm;
  2. use bracket_lib::terminal::{to_cp437, BLACK, YELLOW_GREEN};
  3. /// 小鸟
  4. pub struct Bird {
  5.     /// x坐标
  6.     x: f64,
  7.     /// y坐标
  8.     y: f64,
  9.     /// 下落速度
  10.     velocity: f64,
  11. }
  12. impl Bird {
  13.     /// 初始化小鸟
  14.     pub fn new(game_width: u32, game_height: u32) -> Self {
  15.         Self {
  16.             x: (game_width / 5) as f64,
  17.             y: (game_height / 2) as f64,
  18.             velocity: 0.1,
  19.         }
  20.     }
  21.     /// 绘制
  22.     pub fn draw(&self, ctx: &mut BTerm) {
  23.         ctx.set(
  24.             self.x as u32,
  25.             self.y as u32,
  26.             YELLOW_GREEN,
  27.             BLACK,
  28.             to_cp437('&'),
  29.         );
  30.     }
  31.     /// 获取当前的 x 坐标
  32.     pub fn bird_x(&self) -> f64 {
  33.         self.x
  34.     }
  35.     /// 获取当前的 y 坐标
  36.     pub fn bird_y(&self) -> f64 {
  37.         self.y
  38.     }
  39.     /// 自由下落
  40.     pub fn gravity_and_move(&mut self) {
  41.         self.velocity += 0.1;
  42.         self.y += self.get_velocity();
  43.     }
  44.     /// 点击空格向上
  45.     pub fn flap(&mut self) {
  46.         self.y -= 2.0;
  47.         self.velocity = 0.1;
  48.     }
  49.     /// 是否超出边界
  50.     pub fn bird_out(&self, max_y: u32) -> bool {
  51.         self.y as u32 > max_y || self.y < 0.0
  52.     }
  53.     /// 获取当前下落的速度,限制到了2.0
  54.     fn get_velocity(&mut self) -> f64 {
  55.         if self.velocity > 2.0 {
  56.             self.velocity = 2.0
  57.         }
  58.         self.velocity
  59.     }
  60. }
复制代码
obstacle.rs
  1. use bracket_lib::{
  2.     prelude::BTerm,
  3.     random::RandomNumberGenerator,
  4.     terminal::{to_cp437, BLACK, RED},
  5. };
  6. /// 障碍物
  7. pub struct OBstacle {
  8.     /// 障碍物的横坐标
  9.     x: u32,
  10.     /// 缝隙高度
  11.     size: u32,
  12.     /// 障碍物的中心点
  13.     gap_y: u32,
  14. }
  15. impl OBstacle {
  16.     /// 障碍物初始化
  17.     ///
  18.     /// *score 当前得分
  19.     ///
  20.     /// *start_x 初始x坐标
  21.     ///
  22.     /// *screen_height 游戏窗口高度
  23.     pub fn new(score: u32, start_x: u32, screen_height: u32) -> Self {
  24.         let mut random = RandomNumberGenerator::new();
  25.         let size;
  26.         if score > 60 {
  27.             size = 2
  28.         } else {
  29.             size = u32::max(2, 20 - score / 3);
  30.         }
  31.         let half_size = size / 2 + 1;
  32.         Self {
  33.             x: start_x,
  34.             size,
  35.             gap_y: random.range(half_size, screen_height - half_size),
  36.         }
  37.     }
  38.     /// 向前移动
  39.     ///
  40.     /// *distance 向前移动的距离
  41.     pub fn move_forward(&mut self, distance: u32) {
  42.         self.x -= distance
  43.     }
  44.     pub fn obstacle_x(&self) -> u32 {
  45.         self.x
  46.     }
  47.     pub fn obstacle_size(&self) -> u32 {
  48.         self.size
  49.     }
  50.     pub fn obstacle_gap_y(&self) -> u32 {
  51.         self.gap_y
  52.     }
  53.     /// 绘制
  54.     pub fn draw(&self, screen_height: u32, ctx: &mut BTerm) {
  55.         let screen_x = self.x;
  56.         let half_size = self.size / 2;
  57.         for y in 0..self.gap_y - half_size {
  58.             ctx.set(screen_x, y, RED, BLACK, to_cp437('#'));
  59.         }
  60.         for y in self.gap_y + half_size..screen_height {
  61.             ctx.set(screen_x, y, RED, BLACK, to_cp437('#'));
  62.         }
  63.     }
  64. }
复制代码
Cargo.toml
  1. [package]
  2. name = "flappy_bird"
  3. version = "0.1.0"
  4. edition = "2021"
  5. # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
  6. [dependencies]
  7. bracket-lib = "0.8.7"
复制代码
Rust官网
Rust 中文社区

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4