使用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种网络故障及解决方法大集合
上午好,我的网工朋友。 做网络工程师,自然离不开网络,而日常工作中能搞多少大项目?最常见的其实还是网络故障的处理了。 怎么最高效地排查网络故障?怎么简单几招通网? 今天就从基础的入手,分…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
