一个非常简单的小项目。
看到了杨旭大佬的教学视频,自己跟着实现了一下,完善了一下游戏逻辑。
通过空格键进行控制。
游戏中可按 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.rs
- pub mod game;
- // bird/mod.rs
- pub mod bird;
- // obstacles/mod.rs
- pub 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,
- }
- /// 游戏 State
- pub struct State {
- /// 当前模式
- mode: GameMode,
- /// 得分
- score: u32,
- /// 小鸟
- bird: bird::Bird,
- /// 等待时间
- waite_time: f32,
- /// 障碍物列表,因为需要从头尾两处进行操作,所以选择使用双向链表
- obstacle_list: LinkedList<obstacle::OBstacle>,
- }
- /// 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() <= self.bird.bird_x() as u32 {
- let half_sie = first_obt.obstacle_size() / 2;
- let first_top = first_obt.obstacle_gap_y() - half_sie;
- let first_bottom = first_obt.obstacle_gap_y() + half_sie;
- let dragon_y = self.bird.bird_y() as u32;
- if dragon_y > first_top && dragon_y < first_bottom {
- self.score += 1;
- } else {
- self.mode = GameMode::End;
- }
- self.obstacle_list.pop_front();
- self.obstacle_list
- .push_back(obstacle::OBstacle::new(self.score, 80, GAME_HEIGHT));
- }
- }
- /// 游戏结束时的控制方法
- fn end_control(&mut self, ctx: &mut BTerm) {
- ctx.cls();
- ctx.print_centered(6, format!("You are DEAD ! with {} score. ", self.score));
- ctx.print_centered(8, " (R) Restart game");
- ctx.print_centered(9, " (Q/Esc) Exit ");
- if let Some(key) = ctx.key {
- match key {
- VirtualKeyCode::R => {
- 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 < 0.0
- }
- /// 获取当前下落的速度,限制到了2.0
- fn get_velocity(&mut self) -> 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 中文社区
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |