8个版本 (4个重大更新)
0.4.2 | 2023年7月10日 |
---|---|
0.4.0 | 2023年7月1日 |
0.3.2 | 2023年2月7日 |
0.2.0 | 2023年2月3日 |
0.0.0 | 2021年8月2日 |
#66 in 配置
每月112次下载
在 4 crates 中使用
27KB
Cvars
配置变量 .rs一种简单且直观的方式来存储和编辑运行时配置
Cvars(控制台变量或配置变量)是一种简单的方式来存储您希望在运行时更改的设置,而无需重新启动程序。
控制台是设置cvars的最直观的方式,但您可以编写自己的UI或从stdin读取,如果您想的话。它们适用于Fyrox和Macroquad。
这些crates受到了idTech(Doom、Quake)和Source游戏引擎系列的影响,但它们也可以在游戏之外有用。Cvars允许您通过让您测试某些游戏玩法更改而不重新编译来更快地迭代。如果您向玩家公开(一部分)它们,它们还可以使您的游戏更容易修改。
TL;DR:根据字段名称作为字符串设置和获取结构字段。用户将cvar的名称和新值写入控制台,它设置您的配置结构中适当的字段,游戏现在表现不同。您的游戏代码将cvars作为常规静态类型值使用。
无样板代码 - 没有需要手动实现的特质,也没有每个cvar都要调用的设置代码。
最小的性能成本 - 只是结构字段访问而不是硬编码的常量。Cvars足够便宜,即使在您找到最佳值之后,也可以保持一切可配置 - 您可以在发布的游戏中保持可调整性,让玩家自行实验。
用法
- 将cvars添加到您的
Cargo.toml
cargo add cvars
- 将您的配置放入一个结构中并派生
SetGet
use cvars::cvars;
// This generates a Cvars struct containing all your config options
// and a corresponding Default impl.
cvars! {
g_rocket_launcher_ammo_max: i32 = 20,
g_rocket_launcher_damage: f32 = 100.0,
}
// Store this in your game state.
let mut cvars = Cvars::default();
- 允许用户更改配置
// These normally come from the user
// (from stdin / your game's console / etc.)
let cvar_name = "g_rocket_launcher_damage";
let new_value = "150";
// This looks up the right field and sets it to the new value.
cvars.set_str(cvar_name, new_value).unwrap();
动机
玩家/修改者/游戏开发者希望火箭造成更多伤害。他在游戏的控制台或stdin中输入了g_rocket_launcher_damage 150
。代码将cvar名称和新值作为字符串获取,因此不能写入cvars.g_rocket_launcher_damage = 150
。您需要根据字符串查找正确的字段,这就是cvars
所做的事情——它生成set_str
(和一些其他有用的方法)。您调用cvars.set_str("g_rocket_launcher_damage", "150");
,它会查找正确的字段,将值解析为其类型,并使用它更新字段。从那时起,火箭造成150点伤害。
重要的是,在您的应用程序的其余部分中,您仍然可以像常规结构体字段一样访问您的cvar - 例如,player.health -= cvars.g_rocket_launcher_damage;
。这意味着您只需要在用户(玩家或开发者在调试或测试不同平衡时)更改值时使用字符串。您的游戏逻辑的其余部分仍然是静态类型,并且在游戏代码中使用cvar就像访问字段一样,没有任何开销。
一个典型的游戏将具有数百或数千个可调整的参数。通过cvar和控制台,您可以保持所有这些参数可配置,以便高级玩家、修改者和您的游戏开发者自我,而无需构建复杂的设置菜单。您可以在保持所有内容可配置的同时,使用TUI同时向游戏GUI中的普通玩家公开常用设置。
有关小型可运行示例,请参阅cvars/examples/stdin.rs。
有关真实世界的示例,请查看使用cvar的游戏
- RecWars - 使用Macroquad控制台,游戏玩法中的每个方面都是可配置的,您可以在浏览器中测试它
- RustCycles - 使用Fyrox控制台
Fyrox控制台
Fyrox控制台是本存储库中的一个单独的crate。要在您的游戏中使用它,请将其添加到您的Cargo.toml
,并在相关引擎事件上调用其方法。
有关更多信息,请参阅crates.io页面或其文档。
Macroquad控制台
Macroquad控制台是本存储库中的一个单独的crate。要在您的游戏中使用它,请将其添加到您的Cargo.toml
,并在每一帧上调用其update
方法。
有关更多信息,请参阅crates.io页面或其文档。
功能
- 派生宏
SetGet
,根据cvar的名称创建setter和getter- 静态类型(
set
、get
) - 作为字符串(
set_str
、get_string
)
- 静态类型(
- 使用类似于
cvars!
的宏来在单行中声明类型和初始值 - 支持用户定义的cvar类型(结构和枚举)
- 将cvar保存到/从文件中 - 如果您的游戏有多个平衡预设,则很有用
- Fyrox引擎的内置控制台
- Macroquad 引擎的内置控制台
- 自动完成
目前我还没有计划自己实现的功能,但如果有很好的实现,我可能会接受PR。最好是在自己的crate中实现它们。
- Bevy 引擎的内置控制台
- Egui UI 工具包的内置控制台
- 基于stdio的无阻塞控制台
- 这将使任何可以访问stdin/out的程序都能获得cvars的全部功能,而无需为每个引擎或UI工具包实现控制台。
替代方案
- inline_tweak
- 使用hashmaps,每次访问都有开销
- 不能在某些上下文中使用(例如,在
const
中) - Veloren 从const-tweaker切换到它
- const-tweaker
- Web GUI
- 只支持少数stdlib类型,不支持自定义类型
- 根据tuna的作者的说法,存在一些安全性问题
- 使用hashmaps,每次访问都有开销
- tuna
- Web GUI
- 不清楚是否支持枚举
- 使用hashmaps,每次访问都有开销
- cvar
- 使用trait而不是宏。这个trait似乎需要手动实现,所以需要更多的样板代码。
- 具有cvars目前没有的额外功能(列表、操作)
与这些相比,cvars在运行时没有开销或需要更少的设置代码。目前可能的不利之处在于略微增加的增量编译时间(数百毫秒)。
Cvars的用途也与inline_tweak和const-tweaker略有不同。它的目的是永远留在代码中,即使在发布游戏之后,也允许游戏社区进行修改。
最后,你可能根本不需要像cvars或inline_tweak这样的专用crate。游戏开发中的许多常识是错误的或过时的。你可能只需要一个包含RON或JSON的文件,该文件在每一帧加载并反序列化为配置结构体。大多数时间由操作系统缓存,并且在开发过程中编辑文件后丢失帧不会引起任何人的注意。
开发
仓库按照cargo workspace组织,主功能是独立的crate,控制台和基准测试也是独立的crate - 请参考Cargo.toml
中的技术原因。
-
测试:在根目录中,使用
cargo test
测试工作空间中的所有内容。要测试控制台,请cd
到它们的目录并运行cargo test
。 -
调试:
- 使用
cargo expand --package cvars-macros --example testing-fnlike
查看proc宏生成的代码。对于derive宏也有类似的文件。您也可以在宏中使用println!
和dbg!
。 - 扩展后的代码无法编译,但输出末尾通常包含错误,这些错误可以帮助您追踪生成的代码中的问题:
cargo expand --package cvars-macros --example testing-fnlike > cvars-macros/examples/testing-expanded.rs && cargo build --package cvars-macros --example testing-expanded ; rm cvars-macros/examples/testing-expanded.rs
. 例外情况是当宏生成语法无效的代码时,这时其输出将完全缺失。
- 使用
-
基准测试:在
cvars-bench-compile-time
目录下运行./bench.sh
以基准测试使用 proc 宏时的增量编译时间。 -
有用的命令:
- cargo-llvm-lines 和 cargo-bloat。在
cvars-bench-compile-time
中使用其中一个(例如cargo llvm-lines --features string,typed,fnlike,cvars-1000
)以找出哪些函数生成大量的 LLVM IR 和编译成大量的代码。这通常是导致编译时间长的原因。LLVM IR 的行数更为重要,因为它能更好地表明后端即使编译成少量的机器代码,也需要做多少工作。 - 设置环境变量
CVARS_STATS
以使宏打印它们花费的时间 - 例如CVARS_STATS= cargo bloat --features string,typed,fnlike,cvars-1000
。如果它与总编译时间相比很小,那么大部分时间都花在代码生成上,处理大量生成的代码。请注意,如果编译后的代码没有变化,编译器的输出(包括 proc 宏的输出)将被缓存,因此您可能需要设置变量并编辑代码以查看统计信息。
- cargo-llvm-lines 和 cargo-bloat。在
贡献
如果您有任何问题或建议,您可以在 Rusty Games Discord 服务器上找到我。
欢迎 问题 和 拉取请求。请查看 good first issue 标签以获取简单的任务。
许可证
AGPL-v3 或更新的版本
依赖
~265–710KB
~17K SLoC