#控制台 #配置 #设置 #调试 #游戏开发 #结构字段

cvars

配置变量 - 一种简单且直观的方式来存储和编辑运行时配置

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 配置

Download history 33/week @ 2024-03-11 21/week @ 2024-03-18 25/week @ 2024-03-25 40/week @ 2024-04-01 33/week @ 2024-04-08 27/week @ 2024-04-15 14/week @ 2024-04-22 43/week @ 2024-04-29 32/week @ 2024-05-06 31/week @ 2024-05-13 21/week @ 2024-05-20 25/week @ 2024-05-27 27/week @ 2024-06-03 36/week @ 2024-06-10 16/week @ 2024-06-17 31/week @ 2024-06-24

每月112次下载
4 crates 中使用

AGPL-3.0-or-later

27KB

Cvars

配置变量 .rs
一种简单且直观的方式来存储和编辑运行时配置

GitHub Docs.rs Crates.io License (AGPL3) CI Audit Dependency status Discord

Cvars(控制台变量或配置变量)是一种简单的方式来存储您希望在运行时更改的设置,而无需重新启动程序。

控制台是设置cvars的最直观的方式,但您可以编写自己的UI或从stdin读取,如果您想的话。它们适用于Fyrox和Macroquad。

这些crates受到了idTech(Doom、Quake)和Source游戏引擎系列的影响,但它们也可以在游戏之外有用。Cvars允许您通过让您测试某些游戏玩法更改而不重新编译来更快地迭代。如果您向玩家公开(一部分)它们,它们还可以使您的游戏更容易修改。

TL;DR:根据字段名称作为字符串设置和获取结构字段。用户将cvar的名称和新值写入控制台,它设置您的配置结构中适当的字段,游戏现在表现不同。您的游戏代码将cvars作为常规静态类型值使用。

15*1000字/秒值的使用示例视频

无样板代码 - 没有需要手动实现的特质,也没有每个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的游戏

Fyrox控制台

GitHub Docs.rs Crates.io License (AGPL3) CI Audit Dependency status Discord

Fyrox控制台是本存储库中的一个单独的crate。要在您的游戏中使用它,请将其添加到您的Cargo.toml,并在相关引擎事件上调用其方法。

Fyrox console

有关更多信息,请参阅crates.io页面或其文档

Macroquad控制台

GitHub Docs.rs Crates.io License (AGPL3) CI Audit Dependency status Discord

Macroquad控制台是本存储库中的一个单独的crate。要在您的游戏中使用它,请将其添加到您的Cargo.toml,并在每一帧上调用其update方法。

Macroquad console

有关更多信息,请参阅crates.io页面或其文档

功能

  • 派生宏SetGet,根据cvar的名称创建setter和getter
    • 静态类型(setget
    • 作为字符串(set_strget_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-linescargo-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 宏的输出)将被缓存,因此您可能需要设置变量并编辑代码以查看统计信息。

贡献

如果您有任何问题或建议,您可以在 Rusty Games Discord 服务器上找到我。

欢迎 问题拉取请求。请查看 good first issue 标签以获取简单的任务。

许可证

AGPL-v3 或更新的版本

依赖

~265–710KB
~17K SLoC