使用Rust开发小游戏
本文是对 使用 Rust 开发一个微型游戏【已完结】[1]的学习与记录.
cargo new flappy
在Cargo.toml的[dependencies]下方增加:
bracket-lib = "~0.8.7"
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: BTerm = BTermBuilder::simple80x50()
.with_title("爽哥做游戏--Flappy Dragon")
.build()?;
main_loop(context, State {})
}
cargo run后,可以看到
use bracket_lib::prelude::*;
// 游戏3种模式(菜单,游戏中,结束)
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, "欢迎来到游戏~");
ctx.print_centered(8, "Press P key to start Game");
ctx.print_centered(9, "Press Q to 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, "你挂了..");
ctx.print_centered(8, "按P键 再来一局");
ctx.print_centered(9, "按Q键 退出游戏");
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),
}
// ctx.cls();
// ctx.print(1, 1, "Hello,Bracket Terminal!");
}
}
fn main() -> BError {
let context: BTerm = BTermBuilder::simple80x50()
.with_title("爽哥做游戏--Flappy Dragon")
.build()?;
main_loop(context, State::new())
}
增加玩家
use bracket_lib::prelude::*;
// 游戏3种模式(菜单,游戏中,结束)
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.player.gravity_and_move();
self.frame_time = 0.0;
}
// 按空格
if let Some(VirtualKeyCode::Space) = ctx.key {
self.player.flap();
}
self.player.render(ctx);
ctx.print(0, 0, "按空格起飞~");
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, "欢迎来到游戏~");
ctx.print_centered(8, "Press P key to start Game");
ctx.print_centered(9, "Press Q to 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, "你挂了..");
ctx.print_centered(8, "按P键 再来一局");
ctx.print_centered(9, "按Q键 退出游戏");
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),
}
// ctx.cls();
// ctx.print(1, 1, "Hello,Bracket Terminal!");
}
}
fn main() -> BError {
let context: BTerm = BTermBuilder::simple80x50()
.with_title("爽哥做游戏--Flappy Dragon")
.build()?;
main_loop(context, State::new())
}
增加障碍
use std::fmt::format;
use bracket_lib::prelude::*;
// 游戏3种模式(菜单,游戏中,结束)
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.player.gravity_and_move();
self.frame_time = 0.0;
}
// 按空格
if let Some(VirtualKeyCode::Space) = ctx.key {
self.player.flap();
}
self.player.render(ctx);
ctx.print(0, 0, "按空格起飞~");
// 障碍物&积分
// 实时打印分数
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; // 分数+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, "欢迎来到游戏~");
ctx.print_centered(8, "Press P key to start Game");
ctx.print_centered(9, "Press Q to 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, "你挂了..");
// 挂了后显示一下分数
ctx.print_centered(6, &format!("本局获得了 {} 分", self.score));
ctx.print_centered(8, "按P键 再来一局");
ctx.print_centered(9, "按Q键 退出游戏");
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 = 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: BTerm = BTermBuilder::simple80x50()
.with_title("爽哥做游戏--Flappy Dragon")
.build()?;
main_loop(context, State::new())
}
参考资料
使用 Rust 开发一个微型游戏【已完结】: https://www.bilibili.com/video/BV1vM411J74S
本文由 mdnice 多平台发布
相关文章:
使用Rust开发小游戏
本文是对 使用 Rust 开发一个微型游戏【已完结】[1]的学习与记录. cargo new flappy 在Cargo.toml的[dependencies]下方增加: bracket-lib "~0.8.7" main.rs中: use bracket_lib::prelude::*;struct State {}impl GameState for State { fn tick(&mut self,…...
笔记二十一、使用路由search进行传递参数
21.1 父组件设置路由参数 <NavLink to{classify?param_A${this.state.name}¶m_B${this.state.age}} className{this.activeStyle}>classify</NavLink> import React from "react"; import {NavLink, Outlet} from "react-router-dom"…...
python多线程和多进程
1.多线程 线程是程序执行的最小单位,一个进程至少有一个线程。 提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。 进程之间不能共享内存,但线程之间共享内存非常容易。 Python 常用的多线程库有threading 和…...
VMware虚拟机网络配置详解
vmware为我们提供了三种网络工作模式,它们分别是:Bridged(桥接模式)、NAT(网络地址转换模式)、Host-Only(仅主机模式) 打开vmware虚拟机,我们可以在选项栏的“编辑”下的…...
VUE语法--img图片不显示/img的src动态赋值图片显示
1、问题概述 常见情景1:在VUE中使用img显示图片的时候,通过传参的方式传入图片的路径和名称,VUE不加载本地资源而是通过http://localhost:8080/...的地址去加载网络资源,从而出现了图片无法显示的情况。 常见情景2:针…...
springboot+vue智能企业设备管理系统05k50
智能设备管理系统主要是为了提高工作人员的工作效率和更方便快捷的满足用户,更好存储所有数据信息及快速方便的检索功能,对系统的各个模块是通过许多今天的发达系统做出合理的分析来确定考虑用户的可操作性,遵循开发的系统优化的原则…...
C++中的new、operator new与placement new
new operator new operator是我们常用的new。 new 和 delete 是用来在 堆上申请和释放空间的 ,是 C 定义的 关键字,和 sizeof 一样。 实际 new / delete 和 malloc / free 最大的区别是,前者对于 自定义类型 除了可以开辟空间,…...
ElasticSearch之cat anomaly detectors API
curl -X GET "https://localhost:9200/_cat/ml/anomaly_detectors?vtrue&pretty" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"执行结果输出如下: curl -X GET "https://localhost:9200/_cat/ml/ano…...
Luminar Neo1.16.0(ai智能图像处理)
Luminar Neo是一款ai智能图像编辑软件,它专注于使用人工智能技术来实现对照片的快速、高效和创造性的编辑。 具体来说,Luminar Neo可以自动移除景观或旅行照片中令人分心的元素,例如电话线、电线杆等,从而增强照片的整体质量。同…...
ElasticSearch之cat aliases API
执行aliases命令,如下: curl -X GET "https://localhost:9200/_cat/aliases?pretty&vtrue" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"执行结果输出如下: alias index …...
bash编程 数组和for循环的应用
bash编程 数组和for循环的应用 1、问题背景2、bash 定义数组3、for循环遍历输出数组所有元素4、编写bash脚本输出每个端口是否在监听状态 1、问题背景 linux服务器开机后,需要检查一组端口是否在监听,以便判断这些端口对应的服务是否在运行。可以考虑使…...
Python基础:标准库概览
1. 标准库介绍 Python 标准库非常庞大,所提供的组件涉及范围十分广泛,正如以下内容目录所显示的。这个库包含了多个内置模块 (以 C 编写),Python 程序员必须依靠它们来实现系统级功能,例如文件 I/O,此外还有大量以 Pyt…...
C#,《小白学程序》第三课:类class,类的数组及类数组的排序
类class把数值与功能巧妙的进行了结合,是编程技术的主要进步。 下面的程序你可以确立 分数 与 姓名 之间关系,并排序。 1 文本格式 /// <summary> /// 同学信息类 /// </summary> public class Classmate { /// <summary> /…...
建筑结构健康监测系统和传统人工监测的区别
在繁华的城市里,建筑结构作为城市生命线的重要一环,其安全与稳定对城市的运转和居民的生活至关重要。为了更好地守护建筑结构的健康,WITBEE万宾自主研发建筑结构健康监测系统让建筑安全,在上一个台阶。 WITBEE万宾建筑结构健康监测…...
二 使用GPIO的复用功能 利用USART 实现printf()
参考这篇: STM32串口通信详解 1. 关于USART USART ( universal synchronous / asynchronous receiver /transmitter) 是一种串行通讯协议 , 允许设备通过串行端口进行数据传输, USART 能够以同步或者异步的方式进行工作,在实际的运用中&…...
C#中的警告CS0120、CS0176、CS0183、CS0618、CS0649、CS8600、CS8601、CS8602、CS8604、CS8625及处理
目录 一、CS0120 二、CS0176 1.解决前 2.解决后 3.解决办法 三、CS0183 四、CS0618 五、CS8600 六、CS8602 七、CS8622 1. 解决前: 2. 解决后: 3.解决方法: 八、CS8604和CS8625 九、CS0649 十、CS8601 一、CS0120 严重性 代…...
js中声明变量的关键字(const,let,var)
const 特点: const不允许在同一作用域重复声明,块级作用域暂时性死区,在声明之前,该变量是不可用的const声明的是一个只读变量,声明之后不能改变其值,一旦声明必须初始化但是const定义的对象属性是可以修…...
Android13 launcher循环切页
launcher 常规切页:https://blog.csdn.net/a396604593/article/details/125305234 循环切页 我们知道,launcher切页是在packages\apps\Launcher3\src\com\android\launcher3\PagedView.java的onTouchEvent中实现的。 1、滑动限制 public boolean onT…...
Java学习路线第一篇:Java基础(2)
这篇则分享Java学习路线第一part:Java基础(2) 从看到这篇内容开始,你就是被选定的天命骚年,将承担起学完Java基础的使命,本使命为单向契约,你可选择YES或者选择YES。 具体路线安排:…...
网络工程师精华篇,50种网络故障及解决方法大集合
上午好,我的网工朋友。 做网络工程师,自然离不开网络,而日常工作中能搞多少大项目?最常见的其实还是网络故障的处理了。 怎么最高效地排查网络故障?怎么简单几招通网? 今天就从基础的入手,分…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
