16个版本
新 0.14.8 | 2024年8月19日 |
---|---|
0.14.7 | 2024年8月13日 |
0.14.0 | 2024年7月17日 |
0.0.8 | 2024年6月22日 |
0.0.7 | 2024年1月21日 |
#66 在 游戏开发
1,123 每月下载量
150KB
2.5K SLoC
bevy_bsml
BSML代表Bevy的Simple Markup Language,或简称BS Markup Language。
bevy_bsml允许您使用简单标记语言组合UI元素,灵感来自svelte和tailwindcss。
它建立在官方bevy_ui库之上,因此您仍然可以使用bevy_ui
手动与UI节点或样式交互。
要查看bevy_bsml的基本用法,请查看示例
有关重大更改和新功能,请参阅CHANGELOG.md
目录
为什么不使用HTML或XML?
因为尖括号标记语言与Rust宏不兼容,而(...)
和{...}
可以自然地工作。
支持的Bevy版本
从版本0.14开始,bevy_bsml将与Bevy保持相同的次要版本,而补丁版本将有所不同。
Bevy | bevy_bsml |
---|---|
0.14 | 0.14 |
0.13 | 0.0.8 |
0.12 | 0.0.7 |
设置
导入预览模块,并添加BsmlPlugin
。
use bevy_bsml::prelude::*;
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_plugin(BsmlPlugin)
.run();
}
有关更详细的用法,请参阅示例。
BSML基础知识
在BSML中,(...)
包含元素的名称和属性,而 {...}
包含元素的内容,例如嵌套元素。
以下是一个基本的BSML示例
(node) {
(text) { "hello world" }
}
它等同于以下HTML
<div>
hello world
</div>
如果一个元素没有任何内容,你可以省略大括号
(node)
这是一个有效的bsml;它将生成一个没有内容的默认样式的NodeBundle。
你还可以在括号内嵌套元素
(node) {
(node)
(text) { "hello world" }
(node) {
(text) { "nested text" }
}
}
等于
<div>
<div />
hello world
<div>
nested text
</div>
</div>
BSML中的元素类型
node
(node)
类似于HTML中的 div
;你可以在其中嵌套元素,或者不嵌套元素仅用于样式。
示例:
没有嵌套元素的100x100px蓝色方块:
(node class=[w(100.0), h(100.0), BG_BLUE_400])
居中节点:
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER]) {
(node class=[h(100.0), w(100.0), BG_BLUE_400])
}
蓝色框中的文本:
(node class=[w(100.0), h(100.0), BG_BLUE_400]) {
(text class=[TEXT_WHITE]}) { "hello world" }
}
带有标签:
#[derive(Component)]
pub struct MyNode { i: u8 }
(node labels=[MyNode { i: 0 }] class=[w(100.0), h(100.0), BG_BLUE_400])
for
(for)
是一个遍历给定迭代器的节点,并为每个项目重复嵌套元素。
语法是 (for {<item> in <iterator>}) { <nested elements> }
。
<item>
是一个变量名,而 <iterator>
可以是任何实现 IntoIterator
的表达式,就像在 for ... in ...
循环中。
可选地,你还可以像这样获取项目的索引
(for {<index>, <item> in <iterator>}) { <nested elements> }
示例:
简单的菜单屏幕:
(for
{name in ["Continue", "Setting", "Exit"]}
class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_WHITE]
) {
(node class=[w(100.0), h(100.0), BG_BLUE_400]) {
(text class=[TEXT_BASE]) { "{}", name }
}
}
带有索引的简单菜单屏幕:
(for
{i, name in ["Continue", "Setting", "Exit"]}
class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_WHITE]
) {
(node class=[w(100.0), h(100.0), BG_BLUE_400]) {
(text class=[TEXT_BASE]) { "{}: {}", i, name }
}
}
slot
(slot)
是一个特殊元素;它用于 可复用组件 定义,用于公开其子元素的空间。
当它包含括号内的嵌套元素时,它将用作插槽的默认内容,当使用插槽时将被替换。如果你不需要默认内容,可以省略括号。
示例:
定义一个按钮组件:
#[derive(Component)]
pub struct MyButton;
// define a button component
bsml!{MyButton;
(slot class=[w(100.0), h(100.0), BG_BLUE_400, hovered(BG_BLUE_600)]) {
(text class=[TEXT_WHITE]) { "I am button" } // default content
}
}
fn spawn_ui_system(mut commands: Commands) {
// spawn a screen using bsml!
commands.spawn_bsml(bsml!(
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_TRANSPARENT]) {
(MyButton) { (text class=[TEXT_WHITE]) { "button 1" } } // displays "button 1"
(MyButton) { (text class=[TEXT_WHITE]) { "button 2" } } // displays "button 2"
(MyButton) // displays "i am button"
}
));
}
text
(text)
元素与其他元素不同之处在于你无法在其中嵌套元素。
相反,{...}
内的内容与 format!(...)
的参数完全一样。
示例:
基本:
(text) { "hello world" }
带有参数:
(text) { "{} + {} = {}", 1, 2, 1 + 2 }
带有样式:
(text class=[TEXT_XS]) { "I'm a tiny wittle text" }
带有标签:
#[derive(Component)]
pub struct MyText;
(text labels=[MyText] class=[TEXT_XS]) { "I'm a tiny little text" }
img
(img)
元素用于渲染图像。
此元素与其他元素不同之处在于你无法在其中嵌套元素。
相反,{...}
内的内容可以是任何返回 UiImage 的表达式。
示例:
基本:
(img) { some_fn_returning_UiImage() }
(img) { UiImage { color: ..., texture: ..., flip_x: false, flip_y: false } }
带有样式:
(img class=[W_FULL]) { UiImage { color: ..., texture: ..., flip_x: false, flip_y: false } }
带有标签:
#[derive(Component)]
pub struct MyImg;
(img labels=[MyImg] class=[W_FULL]) { UiImage { color: ..., texture: ..., flip_x: false, flip_y: false }
material
(material)
元素用于在 ui Node 中渲染材质;内部,它使用 MaterialNodeBundle。
此元素与其他元素不同之处在于你无法在其中嵌套元素。
相反,{...}
内的内容可以是任何返回 Handle<M: UiMaterial> 的表达式。
示例:
基本:
(material) { some_fn_returning_material_handle() }
带有样式:
(material class=[W_FULL]) { some_fn_returning_material_handle() }
带有标签:
#[derive(Component)]
pub struct MyMaterial;
(material labels=[MyMaterial] class=[W_FULL]) { some_fn_returning_material_handle() }
BSML中的属性
class
使用 class
属性来指定元素的样式,例如 tailwindcss。
在类属性中,您可以提供提供的样式类列表,如 W_FULL 或 BG_SLATE_200,或者返回一个: StyleClass, BackgroundColorClass, BorderColorClass,以及 ZIndex。
示例 将节点居中于屏幕中央
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_TRANSPARENT]) {
(node class=[h(100.0), w(100.0), BG_BLUE_400])
}
外部节点将填充整个屏幕,并居中其内容。内部节点将是 100x100px 大小,背景颜色为蓝色。
在交互中更改样式
您可以指定当节点悬停或按下时应用的样式,如 tailwindcss。
(node class=[h(100.0), w(100.0), BG_BLUE_400, hovered(BG_BLUE_600), pressed(BG_BLUE_800)])
注意:如果您计划使用 hovered
或 pressed
样式类,您还必须指定基础类,否则它将不会返回到以前的样式。
labels
labels
属性是节点生成的 bevy 组件的列表。
您可以使用这些组件查询节点,或当节点悬停或按下时更改数据。
然后您可以使用标签组件查询节点。
(node labels=[MyComponent])
#[derive(Component)]
pub struct MyComponent;
// get the entity of the node with MyComponent
// when the node is clicked or hovered
fn my_system(query: Query<Entity, (With<MyComponent>, Changed<Interaction>)>) {
// ...
}
如果您的标签组件有字段,您还可以提供返回初始化组件的表达式。
(node labels=[MyComponent { i: 0, name: "hello".to_owned() }, label2()])
#[derive(Component)]
pub struct MyComponent {
pub i: u8,
pub name: String,
}
#[derive(Component)]
pub struct Label2;
fn label2() -> Label2 {
Label2
}
然后您可以在系统中与这些组件交互
fn my_system(query: Query<&MyComponent, (With<Label2>, Changed<Interaction>)>) {
for my_component in query.iter() {
println!("Interacted with node {}: {}", my_component.i, my_component.name);
}
}
高级:您还可以在 bsml 中使用标签组件的字段
#[derive(Component)]
pub struct Label {
pub text: &'static str,
pub width: f32
}
(node labels=[Label { text: "hello world", width: 100.0 }]) {
(text class=[w(labels.0.width)]) { "{}", labels.0.text }
}
自定义可重用组件
定义可重用组件
您可以使用 bsml 定义自己的可重用组件。
唯一的要求是您必须在结构体上派生 bevy::prelude::Component
特性。
以下是一个可重用组件定义的示例
#[derive(Component)]
pub struct MyComponent {
pub i: u8,
pub name: &'static str,
}
bsml! {MyComponent;
(node class=[h(100.0), w(100.0), BG_BLUE_400]) {
(text class=[TEXT_WHITE, TEXT_BASE]) { "index: {}, name: {}", self.i, self.name }
}
}
在宏中,请注意组件结构体后面跟着分号,这不是 bsml 语法的一部分。
这看起来可能有些突兀,但这是故意的,以清楚地表明您正在定义一个可重用组件。
在BSML中使用组件字段
如您所注意到的,您还可以在 bsml 中使用组件的字段。
您可以在类、标签或嵌套元素内容(如文本元素)中使用它,通过像 self.<field_name>
这样的方式引用它们。
#[derive(Component)]
pub struct MyComponent {
pub i: u8,
pub name: &'static str,
pub width: f32,
}
bsml! {MyComponent;
(node class=[h(100.0), w(self.width), BG_BLUE_400]) {
(text class=[TEXT_WHITE, TEXT_BASE]) { "index: {}, name: {}", self.i, self.name }
}
}
注意 这些不是响应式的。它们仅在组件初始化时评估一次。
即使字段值更改,bsml UI 也不会更新。
使用可重用组件
定义了可重用组件后,您可以直接生成它或将其包含在其他 bsml 元素中使用。
直接生成组件
使用可重用组件的最简单方法是直接创建它。
use bevy_bsml::prelude::*;
fn spawn_bsml_ui(mut commands: Commands) {
commands.spawn_bsml(MyComponent { i: 0, name: "hello" });
}
在其他BSML元素中包含
在其他bsml元素中使用可重用组件时,您可以在括号中使用任何返回初始化组件的表达式。
use bevy_bsml::prelude::*;
#[derive(Component)]
pub struct MyContainer;
bsml! {MyContainer;
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_TRANSPARENT]) {
(MyComponent { i: 0, name: "hello" })
}
}
fn spawn_bsml_ui(mut commands: Commands) {
commands.spawn_bsml(MyContainer);
}
表达式可以是任何内容。您甚至可以执行类似的操作
use bevy_bsml::prelude::*;
#[derive(Component)]
pub struct MyContainer;
fn component(i: u8, name: &'static str) -> MyComponent {
MyComponent { i, name }
}
bsml! {MyContainer;
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_TRANSPARENT]) {
(component(0, "hello"))
}
}
fn spawn_bsml_ui(mut commands: Commands) {
commands.spawn_bsml(MyContainer);
}
使用BSML生成UI元素
使用bsml创建UI元素有两种方法。
生成匿名UI元素
您可以直接在bsml!
宏中包含在Commands::spawn_bsml
方法中。
use bevy_bsml::prelude::*;
fn spawn_bsml_ui(mut commands: Commands) {
commands.spawn_bsml(bsml!(
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_TRANSPARENT]) {
(node class=[h(100.0), w(100.0), BG_BLUE_400])
}
));
}
生成可重用组件
参见直接创建组件。
销毁BSML元素
要销毁bsml元素,可以使用Commands::despawn_bsml
方法,并提供组件的实体。
use bevy_bsml::prelude::*;
fn spawn_bsml_ui(mut commands: Commands) {
// spawn a screen using bsml!
let entity = commands.spawn_bsml(bsml!(
(node class=[W_FULL, H_FULL, JUSTIFY_CENTER, ITEMS_CENTER, BG_TRANSPARENT]) {
(node class=[h(100.0), w(100.0), BG_BLUE_400])
}
))
.id();
// despawn the screen entity
commands.despawn_bsml(entity);
}
与生成的组件的响应性
由于bsml内部创建了bev_ui组件,因此您只需使用bevy::ui::Interaction
来检测和响应用户界面交互。
查看示例以了解如何响应用户界面交互
依赖关系
~33–70MB
~1M SLoC