使用 Rust 开发一个微型游戏

这篇具有很好参考价值的文章主要介绍了使用 Rust 开发一个微型游戏。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用 Rust 构建微型游戏 -- 用于理解游戏开发

一、 创建游戏

Agenda

  • 建立项目
  • 实现 Game loop
  • 不同的游戏模式
  • 添加玩家
  • 添加障碍和计分
  • 汇总

理解 Game loop

为了让游戏流畅、顺滑的运行,需要使用 Game loop

Game loop:

  • 初始化窗口、图形和其它资源
  • 每当屏幕刷新(通常是每秒 30、60或更多次),它都会运行
  • 每次通过循环,它都会调用游戏的 tick() 函数

Game loop

开始 -> 配置 App、Window 和图形 -> Poll (轮询 OS 监听输入状态 -> 调用 tick() 函数 -> 更新屏幕 -> 停止? -> 退出

游戏引擎

  • 游戏引擎用来处理平台特定的部分
  • 以便开发者专心开发游戏

Bracket-Lib (Amethyst Foundation)

Bracket-Lib 是一个 Rust 游戏编程库:

  • 作为简单的教学工具
  • 抽象了游戏开发很多复杂的东西
  • 但保留了相关的概念

Bracket-Lib 包括很多库:

  • 随机数生成、几何、路径寻找、颜色处理、常用算法等

Bracket-terminal

bracket-terminal 是 Bracket-Lib 中负责显示部分

  • 它提供了模拟控制台
  • 可与多种渲染平台配合:
    • 从文本控制台到 Web Assembly
    • 例如:OpenGL、Vulkan、Metal
  • 支持 sprites 和原生 OpenGL 开发

Codepage 437:IBM 扩展 ASCII 字符集

Codepage 437:

  • 来自 Dos PC 上的字符,用于终端输出,除了字母和数字,还提供了一些符号
  • Bracket-lib 会把字符翻译成图形 sprites 并提供一个有限的字符集,字符所展示的是相应的图片
~ via 🅒 base
➜ cd rust

~/rust via 🅒 base
➜ cargo new flappy
     Created binary (application) `flappy` package

~/rust via 🅒 base
➜ cd flappy

flappy on  master [?] via 🦀 1.67.1 via 🅒 base
➜ c

flappy on  master [?] via 🦀 1.67.1 via 🅒 base
➜

main.rs

use bracket_lib::prelude::*;

struct State {}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print(1, 1, "Hello, Bracket Terminal!");
    }
}

fn main() -> BError {
    let context = BTermBuilder::simple80x50()
        .with_title("Flappy Dragon")
        .build()?;

    main_loop(context, State {})
}

游戏的模式

  • 游戏通常在不同的模式中运行
  • 每种模式会明确游戏在当前的 tick() 中应该做什么

我们这个游戏需要 3 种模式:

  • 菜单
  • 游戏中
  • 结束
use bracket_lib::prelude::*;

enum GameMode {
    Menu,
    Playing,
    End,
}

struct State {
    mode: GameMode,
}

impl State {
    fn new() -> Self {
        State {
            mode: GameMode::Menu,
        }
    }

    fn play(&mut self, ctx: &mut BTerm) {
        // TODO
        self.mode = GameMode::End;
    }

    fn restart(&mut self) {
        self.mode = GameMode::Playing;
    }

    fn main_menu(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "Welcome to Flappy Dragon");
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "You are dead!");
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        match self.mode {
            GameMode::Menu => self.main_menu(ctx),
            GameMode::End => self.dead(ctx),
            GameMode::Playing => self.play(ctx),
        }
    }
}

fn main() -> BError {
    let context = BTermBuilder::simple80x50()
        .with_title("Flappy Dragon")
        .build()?;

    main_loop(context, State::new())
}

二、添加 Player

main.rs文章来源地址https://www.toymoban.com/news/detail-460167.html

use bracket_lib::prelude::*;

enum GameMode {
    Menu,
    Playing,
    End,
}

const SCREEN_WIDTH: i32 = 80;
const SCREEN_HEIGHT: i32 = 50;
const FRAME_DURATION: f32 = 75.0;

struct Player {
    x: i32,
    y: i32,
    velocity: f32,
}

impl Player {
    fn new(x: i32, y: i32) -> Self {
        Player {
            x: 0,
            y: 0,
            velocity: 0.0,
        }
    }

    fn render(&mut self, ctx: &mut BTerm) {
        ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'))
    }

    fn gravity_and_move(&mut self) {
        if self.velocity < 2.0 {
            self.velocity += 0.2;
        }
        self.y += self.velocity as i32;
        self.x += 1;

        if self.y < 0 {
            self.y = 0;
        }
    }

    fn flap(&mut self) {
        self.velocity = -2.0; // 往上飞是负的
    }
}

struct State {
    player: Player,
    frame_time: f32,
    mode: GameMode,
}

impl State {
    fn new() -> Self {
        State {
            player: Player::new(5, 25),
            frame_time: 0.0,
            mode: GameMode::Menu,
        }
    }

    fn play(&mut self, ctx: &mut BTerm) {
        ctx.cls_bg(NAVY);
        self.frame_time += ctx.frame_time_ms;

        if self.frame_time > FRAME_DURATION {
            self.frame_time = 0.0;
            self.player.gravity_and_move();
        }

        if let Some(VirtualKeyCode::Space) = ctx.key {
            self.player.flap();
        }

        self.player.render(ctx);
        ctx.print(0, 0, "Press Space to Flap");

        if self.player.y > SCREEN_HEIGHT {
            self.mode = GameMode::End;
        }
    }

    fn restart(&mut self) {
        self.player = Player::new(5, 25);
        self.frame_time = 0.0;
        self.mode = GameMode::Playing;
    }

    fn main_menu(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "Welcome to Flappy Dragon");
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "You are dead!");
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        match self.mode {
            GameMode::Menu => self.main_menu(ctx),
            GameMode::End => self.dead(ctx),
            GameMode::Playing => self.play(ctx),
        }
    }
}

fn main() -> BError {
    let context = BTermBuilder::simple80x50()
        .with_title("Flappy Dragon")
        .build()?;

    main_loop(context, State::new())
}

三、添加障碍

use bracket_lib::prelude::*;

enum GameMode {
    Menu,
    Playing,
    End,
}

const SCREEN_WIDTH: i32 = 80;
const SCREEN_HEIGHT: i32 = 50;
const FRAME_DURATION: f32 = 75.0;

struct Player {
    x: i32, // 世界空间
    y: i32,
    velocity: f32,
}

impl Player {
    fn new(x: i32, y: i32) -> Self {
        Player {
            x: 0,
            y: 0,
            velocity: 0.0,
        }
    }

    fn render(&mut self, ctx: &mut BTerm) {
        ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'))
    }

    fn gravity_and_move(&mut self) {
        if self.velocity < 2.0 {
            self.velocity += 0.2;
        }
        self.y += self.velocity as i32;
        self.x += 1;

        if self.y < 0 {
            self.y = 0;
        }
    }

    fn flap(&mut self) {
        self.velocity = -2.0; // 往上飞是负的
    }
}

struct State {
    player: Player,
    frame_time: f32,
    mode: GameMode,
    obstacle: Obstacle,
    score: i32,
}

impl State {
    fn new() -> Self {
        State {
            player: Player::new(5, 25),
            frame_time: 0.0,
            mode: GameMode::Menu,
            obstacle: Obstacle::new(SCREEN_WIDTH, 0),
            score: 0,
        }
    }

    fn play(&mut self, ctx: &mut BTerm) {
        ctx.cls_bg(NAVY);
        self.frame_time += ctx.frame_time_ms;

        if self.frame_time > FRAME_DURATION {
            self.frame_time = 0.0;
            self.player.gravity_and_move();
        }

        if let Some(VirtualKeyCode::Space) = ctx.key {
            self.player.flap();
        }

        self.player.render(ctx);
        ctx.print(0, 0, "Press Space to Flap");
        ctx.print(0, 1, &format!("Score: {}", self.score));

        self.obstacle.render(ctx, self.player.x);
        if self.player.x > self.obstacle.x {
            self.score += 1;
            self.obstacle = Obstacle::new(self.player.x + SCREEN_WIDTH, self.score);
        }

        if self.player.y > SCREEN_HEIGHT || self.obstacle.hit_obstacle(&self.player) {
            self.mode = GameMode::End;
        }
    }

    fn restart(&mut self) {
        self.player = Player::new(5, 25);
        self.frame_time = 0.0;
        self.mode = GameMode::Playing;
        self.obstacle = Obstacle::new(SCREEN_WIDTH, 0);
        self.score = 0;
    }

    fn main_menu(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "Welcome to Flappy Dragon");
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "You are dead!");
        ctx.print_centered(6, &format!("You earned {} points", self.score));
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        match self.mode {
            GameMode::Menu => self.main_menu(ctx),
            GameMode::End => self.dead(ctx),
            GameMode::Playing => self.play(ctx),
        }
    }
}

struct Obstacle {
    x: i32, // 世界空间
    gap_y: i32,
    size: i32,
}

impl Obstacle {
    fn new(x: i32, score: i32) -> Self {
        let mut random = RandomNumberGenerator::new();
        Obstacle {
            x,
            gap_y: random.range(10, 40),
            size: i32::max(2, 20 - score),
        }
    }

    fn render(&mut self, ctx: &mut BTerm, player_x: i32) {
        let screen_x = self.x - player_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('|'))
        }
    }

    fn hit_obstacle(&self, player: &Player) -> bool {
        let half_size = self.size / 2;
        let does_x_match = player.x == self.x;
        let player_above_gap = player.y < self.gap_y - half_size;
        let player_below_gap = player.y > self.gap_y + half_size;
        does_x_match && (player_above_gap || player_below_gap)
    }
}

fn main() -> BError {
    let context = BTermBuilder::simple80x50()
        .with_title("Flappy Dragon")
        .build()?;

    main_loop(context, State::new())
}

到了这里,关于使用 Rust 开发一个微型游戏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 快速配置 Rust 开发环境并编写一个小应用

    安装: curl --proto \\\'=https\\\' --tlsv1.2 -sSf https://sh.rustup.rs | sh 更新: Rust 的升级非常频繁. 如果安装 Rustup 后已有一段时间,那么很可能 Rust 版本已经过时, 运行 rustup update 获取最新版本的 Rust rustc:编译Rust程序 rustc只适合简单的Rust程序,较大型的项目还是推荐使用Cargo Cargo:Rust 的构建

    2024年02月16日
    浏览(50)
  • 【一起学Rust | 框架篇 | Frui框架】rust一个对开发者友好的GUI框架——Frui

    本次内容接上回《rust原生跨平台GUI框架——iced》,最近突然涌现出多个Rust的UI框架,真实令人兴奋,同时也突出了Rust的勃然生机,我将尽量为大家介绍Rust领域的UI框架,带大家体验Rust领域的好玩意儿。 Frui是一个对开发者相当友好的UI框架,它使得开发者构建用户界面变得

    2024年02月01日
    浏览(39)
  • 在 macOS 上安装 Rust 开发环境并运行第一个程序的详细步骤

    首先,打开终端并执行以下命令来安装 Rust 环境: 该命令将下载并运行 Rust 安装脚本。按照提示完成 Rust 环境的安装。 为了确保每次启动终端时都能加载 Rust 环境变量,我们需要将以下内容添加到 .zshrc 文件中。在终端中执行以下命令: 这将在 .zshrc 文件的末尾添加一行代码

    2024年02月02日
    浏览(50)
  • 微型导轨的使用寿命能达到多久?

    微型导轨,顾名思义就是体积很小的导轨,一般是应用在小型化设备中的,像半导体设备,医疗设备,IC制造设备,X-Y table,精密测量及检测仪器,高速皮带驱动设备,高速移载设备等都需要应用到微型导轨。 对于点胶机、焊锡机、医疗检测发光机、以及其他非标自动化设备

    2024年02月11日
    浏览(35)
  • Unity开发一个FPS游戏之二

    在之前的文章中,我介绍了如何开发一个FPS游戏,添加一个第一人称的主角,并设置武器。现在我将继续完善这个游戏,打算添加敌人,实现其智能寻找玩家并进行对抗。完成的效果如下: fps_enemy_demo   首先是设计敌人,我们可以在网上找到一些好的免费素材,例如在Unity商

    2024年03月17日
    浏览(56)
  • 用Python开发一个迷你打飞机的游戏

    Python是一门非常简单的语言,快速入门之后可以做很多事情!比如爬虫啊,数据分析啊,自动化运维啊,机器学习,量化分析等等!但是入门到进阶的过程有时会非常痛苦,如果有一些好玩有趣的例子就好了。 比如通过游戏来学编程是一个非常好的途径,今天在Github上发现一

    2024年02月05日
    浏览(47)
  • 用python开发一个炸金花小游戏

    众所周知扑克牌可谓是居家旅行、桌面交友的必备道具, 今天我们用  Python  来实现一个类似 炸金花 的扑克牌小游戏,先来看一下基本的游戏规则。 炸(诈)金花又叫三张牌,是在全国广泛流传的一种民间多人纸牌游戏。游戏使用一副除去大小王的扑克牌,共 4 个花色

    2024年02月09日
    浏览(44)
  • html css js 开发一个猜数字游戏

    以下是一个使用HTML、CSS和JS开发的简单猜数字游戏的示例: HTML代码: CSS代码(style.css): JavaScript代码(script.js): 将上述代码保存为HTML、CSS和JS文件,并在浏览器中打开HTML文件,即可开始玩猜数字游戏。用户输入一个1到100之间的整数,点击\\\"猜\\\"按钮,程序会给出相应的提

    2024年01月20日
    浏览(53)
  • 用Python开发一个飞机大战游戏(附源码教程)

    点击蓝字 关注我们 Python是一门非常简单的语言,快速入门之后可以做很多事情!比如爬虫啊,数据分析啊,自动化运维啊,机器学习,量化分析等等!但是入门到进阶的过程有时会非常痛苦,如果有一些好玩有趣的例子就好了。 比如通过游戏来学编程是一个非常好的途径,

    2024年04月14日
    浏览(44)
  • Unity 3D游戏开发+脚本编程完整指南:制作第一个游戏:3D滚球跑酷

    教程相关资源 Unity 3D游戏开发+脚本编程完整指南(工程文件+PPT).zip 本节利用前面的知识来实现第一个较为完整的小游戏,如 图 1-21 所示。 图1-21 3D滚球跑酷游戏完成效果 1. 功能点分析 游戏中的小球会以恒定速度向前移动,而玩家控制着小球 左右移动来躲避跑道中的黄色障

    2024年02月21日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包