首页 > 其他分享 >一个简单的 rust项目 flappy bird

一个简单的 rust项目 flappy bird

时间:2023-04-07 23:22:25浏览次数:49  
标签:flappy self ctx pub obstacle fn bird rust

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

标签:flappy,self,ctx,pub,obstacle,fn,bird,rust
From: https://www.cnblogs.com/SantiagoZhang/p/17297670.html

相关文章

  • 深度了解hybird
    hybird特点:即可以连接pc,又可以连接交换机注意事项:1)pc加入网络后,会在以太网帧中添加vid2)pc只能识标没有vid的以太帧,因此数据帧离开交换机时需要去点vid3)hybird即可加tagged,又可以去除untaggedlsw1:<Huawei>displaycurrent-configuration #sysnameHuawei#undoinfo-centerenab......
  • Rust 所有权规则
    Rust是一种系统级编程语言,其最为突出的特点之一是所有权规则。这些规则确保了Rust代码的内存安全和并发安全,并在编译时检查了内存管理的正确性。下面是Rust中的所有权规则的概述:1.每个值都有一个所有者:在Rust中,每个值都有一个唯一的所有者。这意味着变量在任何时候只能有......
  • Rust编程语言入门之项目实例:- 命令行程序
    项目实例:-命令行程序一、实例:接收命令行参数本章内容12.1接收命令行参数12.2读取文件12.3重构:改进模块和错误处理12.4使用TDD(测试驱动开发)开发库功能12.5使用环境变量12.6将错误消息写入标准错误而不是标准输出创建项目~/rust➜cargonewminigrepCre......
  • rust 建立窗口并关闭。
    [dependencies]web-view="0.7.3"usestd::thread;useweb_view::*;fnmain(){lethandle=thread::spawn(||{letwebview=web_view::builder().title("").content(Content::Html("<html>......
  • [rust学习] 二、 rust中的智能指针
     rust中智能指针大致分类以下内容提炼自rust官方文档: https://doc.rust-lang.org/book/ch15-01-box.html 一、Box<T>1.使用例子:  1fnmain(){2letb=Box::new(5);3println!("b={}",b);4}2.特性:a.由B......
  • Rust关键字及作用
    Rust中的关键字被特定用于定义语法规则和限制名称空间中的标识符。以下是Rust中所有的关键字及其作用:as:类型转换;async:声明异步函数;await:等待异步操作结果;break:结束循环或跳出循环语句块;const:声明常量;continue:继续下一轮循环;crate:当前crate的名称;dyn:动态分发trait......
  • 一个简单的rust项目贪吃蛇
    一个贪吃蛇游戏的rust实现,使用了piston_window和randcrate。游戏使用上下左右方向键进行操控,使用R重置游戏,使用P进行暂停/启动。项目结构·├──Cargo.lock├──Cargo.toml├──src/│  ├──main.rs│  ├──snake_game/│  │ ├─......
  • 为什么 Python、Go 和 Rust 都不支持三元运算符?
    在编程时,我们经常要作条件判断,并根据条件的结果选择执行不同的语句块。在许多编程语言中,最常见的写法是三元运算符,但是,Python并不支持三元运算符,无独有偶,两个最热门的新兴语言Go和Rust也不支持!为什么Python不支持三元运算符呢?本文将主要分析Python在设计条件选择语法时......
  • Rust如何引入源码作为依赖
    问题描述通常我们在rust项目中引入第三方依赖包时,会直接指定包的版本,这种方式指定后,Cargo在编译时会从crates.io这个源中下载这些依赖包。[package]name="foo"version="0.1.0"edition="2021"[dependencies]j4rs=0.15.3比如这里我们就在项目中引用了j4rs这个包,这......
  • Rust编程语言入门
    Rust编程语言入门Rust简介为什么要用Rust?Rust是一种令人兴奋的新编程语言,它可以让每个人编写可靠且高效的软件。它可以用来替换C/C++,Rust和他们具有同样的性能,但是很多常见的bug在编译时就可以被消灭。Rust是一种通用的编程语言,但是它更善于以下场景:需要运行时的速度需......