Bevy Rectray is a native component based 2D and UI solution for the bevy engine.
First add the RectrayPlugin
:
app.add_plugins(RectrayPlugin)
Import the DSL prelude, preferably inside the function scope.
fn spawn(mut commands: RCommands) {
use bevy_rectray::dsl::prelude::*;
...
}
Create a sprite:
sprite!(commands {
sprite: "Ferris.png",
anchor: Left,
offset: [40, 0],
dimension: [200, 200],
})
This spawns a "Ferris.png" to the center left of the screen,
moved to the right by 40 px
, with dimension 200 px * 200 px
,
and returns an Entity
.
Create a hierarchy:
vstack!(commands {
font_size: em(2),
child: text! {
text: "Hello"
},
child: text! {
text: "rust",
child: text! {
text: "and bevy!"
},
},
});
bevy_rectray
's DSL is a very simple macro_rules
macro that reorganizes arguments in a way to make
commands
usable anywhere during the macro invocation, without the need to bend over backwards
to create children/bundles before parents. As a result we have a simple rust-like syntax.
that is intuitive and enjoyable to write.
If you don't like the DSL you can use our
bundles
or
widgets
directly,
bevy_rectray
is all about rectangles!
Each sprite is a rectangle, and placed relative to the parent rectangle.
You might want to
Place a sprite to the center right of the parent sprite,
move left by 10 px,
with 20% of parent's width as width
2x font size as height
and rotate by 45 degrees.
In bevy_rectray
this is incredibly simple:
sprite!(commands {
anchor: Right,
offset: [-10, 0],
dimension: size2!(20 %, 2 em),
rotation: degrees(45),
...
})
Use Transform2D
and Dimension
to manipulate our widgets directly.
- Fine grained low level anchor-offset layout system.
- First class support for rotation and scaling.
- Simple and intuitive containers.
- Decentralized ECS components with no central state.
- Complete support of bevy's 2D primitives.
- Input handling system for mouse and cursor.
- Building blocks for most common widgets.
- Event handling through one-shot systems.
- Reactivity and animation through signals.
macro_rules
based DSL that annihilates boilerplate.- Easy integration with third-party 2D crates.
- Easy migration to future bevy versions.
-
Not a renderer.
bevy_rectray
has minimal rendering features and no third party bevy dependencies, this ensures maintainability and easy migration to future bevy versions, at the cost of not having out of the box widget styles. -
Not
bevy_ui
compatible.bevy_rectray
is not dependent onbevy_ui
in any way. This meansbevy_ui
exclusive features won't be available inbevy_rectray
as is. -
No ui script or serialization.
bevy_rectray
uses rust closures for a lot of things, including events and reactivity, those are unfortunately not serializable. -
Limited reflection support.
Limiting the scope of this project to the supported feature set of
Reflection
is not ideal of this project. Use reflect to some extent to debug is supported, but don't expect every reflect based feature to work withbevy_rectray
. -
No styling
Styling is outside the scope of this crate.
Anchor-Offset offers fine-grained control over the layout, but you can surrender that control to containers for ergonomics.
The Container
is a very simple layout system that
only depends on insertion order of its children. You can find your
hstack
,
grid
or
paragraph
here.
You can implement Layout
yourself to create a custom layout.
Widget builders are used to empower our DSL.
Widget builders implements Widget
and Default
.
They can be used in general like so:
FrameBuilder {
offset: [121, 423].dinto(),
anchor: Center.dinto(),
color: color!(red).dinto()
..Default::default()
}.build(commands)
This returns an Entity
.
dinto
is implemented in DslFrom
or DslInto
.
which gives us nice conversion like [i32; 2] -> Vec2
, which can save us a lot of typing!
When using the dsl macro, this becomes
frame! (commands {
offset: [121, 423],
anchor: Center,
color: color!(red),
});
much cleaner, right?
The DSL have a few special fields that makes it much more powerful than a simple struct constructor.
At the root level, the DSL takes a
RCommands
,
which is a combination of Commands
, AssetServer
and
SignalPool
,
child:
is a special field that can be repeated, it accepts an Entity
, Option<Entity>
or an iterator of Entity
/&Entity
,
and inserts it/them as a child/children.
frame! (commands {
...
child: rectangle! {
dimension: [40, 40]
},
child: text! {
text: "Hello, World!!"
},
child: "Hello".chars().map(|c|
text! (commands {
text: c,
})
)
});
This syntax, notice the use of braces {}
,
field: macro! { .. },
Will be automatically rewritten as
field: macro!(commands { .. }),
Which serves as context propagation.
Extra adds a component or a bundle to a widget, which is the idiomatic pattern to compose behaviors.
// Example: Add dragging support to a `Sprite`.
sprite! (commands {
...
extra: DragX,
extra: DragConstraint,
extra: DragSnapBack,
});
entity
lets us fetch the Entity
directly from a nested macro invocation.
let sprite_entity: Entity;
sprite! (commands {
child: sprite! {
entity: sprite_entity,
}
});
We have support for a syntax inspired by the quote!
crate,
that can be used to repeat a child by an iterator.
vstack! (commands {
child: #rectangle! {
dimension: #dimensions,
color: #colors,
}
});
This zips colors
and dimensions
and
iterate through them to create multiple rectangles.
The dsl normally functions on the field level, which is
performant and editor friendly, but using quote!
syntax
requires running a tt
muncher, which may cause editors to give up
or break your recursion limit. You can use
#![recursion_limit="256"]
to increase your recursion limit.
Checkout our modules for more documentations and examples.
License under either of
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) at your option.
Contributions are welcome!
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.