一个非常简单的小项目。
看到了杨旭大佬的教学视频,自己跟着实现了一下,完善了一下游戏逻辑。
通过空格键进行控制。
游戏中可按 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