I have a query system that finds an object in which the mouse is over. This is not a button, but, I wish to change the color. I'm not sure where to start. What property would I query and how would I change it? Currently, I have the following:
fn mouse_move(mut commands: Commands, cursor: Res<Cursor>, mut query: Query<(&Translation,&mut Sprite,&Box,&Name)>)
{
for (translation,mut sprite,_box,name) in &mut query.iter() {
let cursor_tup = translate_cursor ((cursor.0,cursor.1));
let cursor_vec = Vec3::new(cursor_tup.0,cursor_tup.1,0.0);
if collides(cursor_vec,Vec2::new(1.0,1.0),translation.0,sprite.size) {
println!("{}",name.0);
}
}
}
fn mouse_move(mut commands: Commands, cursor: Res<Cursor>, mut materials: ResMut<Assets<ColorMaterial>>, mut query: Query<(&Translation,&mut Sprite,&Box,&Name, &mut Handle<ColorMaterial>)>)
{
for (translation,mut sprite,_box,name, color) in &mut query.iter() {
let cursor_tup = translate_cursor ((cursor.0,cursor.1));
let cursor_vec = Vec3::new(cursor_tup.0,cursor_tup.1,0.0);
if collides(cursor_vec,Vec2::new(1.0,1.0),translation.0,sprite.size) {
println!("{}",name.0);
let mut color_mat = materials.get_mut(&color).unwrap();
color_mat.color = Color::rgb(1.0,1.0,1.0);
}
}
}
So you have to take the material handle associated with entity, and then have to get the
ColorMaterial from Assets.
Currently the color should just change to white.
Related
I am just a beginner in Rust and so far I have managed to obey borrow-checker's warnings in a async-heavy tokio app until now.
Basically I have a struct which has a HashMap of games. I want to insert a created game into the hashmap and at the same time pass it to a game_loop that I spawn with tokio::spawn. Game loop will update the game but I also want to be able to retrieve the game from the hashmap to execute some functions to check its state etc.
I've tried wrapping it in Arc and Mutexes and whatnot. At the moment I just clone it to the game_loop but - as smarter people probably know - that will only pass a clone of the original and the entity in the hashmap wont update.
GameManager
pub struct GameManager {
games: HashMap<Uuid, Game>,
}
impl GameManager {
fn find_or_create_game(&mut self, user_options: &GameOptions) -> Uuid {
for g in self.games.values() {
println!("game id {:?}", g.id);
println!("game players {:?}", g.state.get_players());
if g.allows_joining() && g.matches_player_options(user_options) {
println!("Joining existing game");
return g.id;
}
}
let rng = ::rand::rngs::StdRng::from_seed(OsRng.gen());
let mut game = Game::new(Some(user_options.clone()), rng);
let game_id = game.id;
let (game_sender, game_receiver) = mpsc::unbounded_channel::<GameEvent>();
let broadcast = self.broadcast.clone();
self.game_channels.insert(game_id, game_sender.clone());
self.games.insert(game_id, game);
tokio::spawn(game_loop(game, broadcast, game_receiver));
return game_id;
}
}
game_loop
pub async fn game_loop(
mut game: Game,
broadcast: UnboundedSender<ServerEvent>,
mut receiver: UnboundedReceiver<GameEvent>,
) -> Result<(), io::Error> {
let dur = std::time::Duration::from_secs_f64(1.0 / game.state.options.fps as f64);
let mut interval = tokio::time::interval(dur);
loop {
interval.tick().await;
while let Some(is_event) = unconstrained(receiver.recv()).now_or_never() {
if let Some(event) = is_event {
handle_game_event(event, &mut game, &broadcast);
}
}
if game.has_ended() {
break;
} else {
game.tick();
let _ = broadcast.send(ServerEvent::Tick(game.get_tick()));
}
}
Ok(())
}
Okay yeah, I am a dummy. Thanks #Peterrabbit though for giving me the motivation to try using Arc again. Had to wrap it in a mutex but all together, I am just happy that it now works.
So now it is:
pub struct GameManager {
games: HashMap<Uuid, Arc<Mutex<Game>>>,
}
impl GameManager {
fn find_or_create_game(&mut self, user_options: &GameOptions) -> Uuid {
for g in self.games.values() {
if g.allows_joining() && g.matches_player_options(user_options) {
println!("Joining existing game");
return g.id;
}
}
let rng = ::rand::rngs::StdRng::from_seed(OsRng.gen());
let mut game = Arc::new(Mutex::new(Game::new(Some(user_options.clone()), rng)));
let game_id = game.lock().await.id;
let (game_sender, game_receiver) = mpsc::unbounded_channel::<GameEvent>();
let broadcast = self.broadcast.clone();
self.game_channels.insert(game_id, game_sender.clone());
self.games.insert(game_id, game.clone());
tokio::spawn(game_loop(game.clone(), broadcast, game_receiver));
return game_id;
}
}
pub async fn game_loop(
mut game: Arc<Mutex<Game>>,
broadcast: UnboundedSender<ServerEvent>,
mut receiver: UnboundedReceiver<GameEvent>,
) -> Result<(), io::Error> {
let dur = std::time::Duration::from_secs_f64(1.0 / game.lock().await.state.options.fps as f64);
let mut interval = tokio::time::interval(dur);
loop {
interval.tick().await;
while let Some(is_event) = unconstrained(receiver.recv()).now_or_never() {
if let Some(event) = is_event {
handle_game_event(event, &mut game, &broadcast).await;
}
}
if game.lock().await.has_ended() {
break;
} else if game.lock().await.is_running() {
handle_game_event(GameEvent::Tick(), &mut game, &broadcast).await;
}
}
Ok(())
}
Bevy v0.8 has several "displayable-object" types, Sprite/SpriteBundle, SpriteSheetBundle, and MaterialMeshBundle. I would like to change the transparency (Alpha-channel, stored on Color Components) of, well, all of them - if possible.
The Answer Changing Sprite Color, addresses changing the Color of an object (where Color has the Alpha-channel) , but it only works for things that have a material associated. In Bevy v0.8, SpriteBundles don't have a material (they did in 0.5).
So is it possible to change the transparency of other objects, that don't have materials? (And if so, how?)
Fortunately, it is doable (and thanks to people in the Bevy-Discord channel for suggestions). There are two elements to the solution - first, all of the items I named, do have a Color component (although finding it can be tricky). Even a SpriteSheet, which is an Atlas of bitmaps, has one - presumably just for this usage. Second, the AnyOf filter, which gets any of the requested components, as a tuple of Options, each of which is either an object, or None.
// Create some disparate objects
pub fn do_add_sprites(mut commands: Commands, asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<ColorMaterial>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
) {
commands.spawn_bundle(SpriteSheetBundle {
transform: Transform::from_xyz(-300.0, -200.0, 1.0),
texture_atlas: texture_atlas_handle2, ..default()
})
.insert(IsMousing { is_dragging: false, is_hovering: false });
commands.spawn_bundle(MaterialMesh2dBundle {
transform: Transform::from_translation(Vec3::new(500.0, -200.0, 1.0)),
mesh: meshes.add(shape::RegularPolygon::new(50., 6).into()).into(),
material: materials.add(ColorMaterial::from(Color::BLUE)),
..default()})
.insert(IsMousing { is_dragging: false, is_hovering: false });
}
// Code elsewhere that says: if cursor over object, set is_hovering flag
pub fn apply_any_hovers(mut materials: ResMut<Assets<ColorMaterial>>,
mut any_hovercraft: Query<(Entity, &IsMousing,
AnyOf<(&mut TextureAtlasSprite, &Handle<ColorMaterial>)>,)>,
) {
for (_entity_id, mouser_data, some_object) in any_hovercraft.iter_mut() {
if mouser_data.is_hovering {
if some_object.0.is_some() {
let some_color = &mut some_object.0.unwrap().color;
some_color.set_a(0.5);
}
if some_object.1.is_some() {
let some_handle = some_object.1.unwrap();
let some_color = &mut materials.get_mut(some_handle).unwrap().color;
some_color.set_a(0.5);
}
} else {
if some_object.0.is_some() {
let some_color = &mut some_object.0.unwrap().color;
some_color.set_a(1.0);
}
if some_object.1.is_some() {
let some_handle = some_object.1.unwrap();
let some_color = &mut materials.get_mut(some_handle).unwrap().color;
some_color.set_a(1.0);
}
}
}
}
I am working on a procedural generation system and want to be able to modify a mesh frame by frame in bevy rust.
I have tried using assets.get_mut() however this results in an error: help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `bevy::prelude::Res<'_, bevy::prelude::Assets<bevy::prelude::Mesh>>
Any help would be greatly appreciated.
Here is what my current code looks like roughly:
// Function which is executed at the very start
fn setup (
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
asset_server: Res<AssetServer>
) {
let mut mesh = Mesh::from(bevy::prelude::shape::Icosphere { radius: 0.5, subdivisions: 10 });
commands.spawn()
.insert_bundle(PbrBundle {
mesh: meshes.add(mesh),
material: materials.add(colour.into()),
..Default::default()
})
.insert(Transform::from_xyz(0.0, 0.0, 0.0));
}
// Function which is executed every frame
fn update_planets (
mut query: Query<(&Transform, &Handle<Mesh>)>,
assets: Res<Assets<Mesh>>
) {
let (transform, handle) = query.get_single_mut().expect("");
let mut mesh = assets.get_mut(handle.id); // Error caused here
if mesh.is_some() {
let positions = temp.attribute(Mesh::ATTRIBUTE_POSITION).unwrap();
if let VertexAttributeValues::Float32x3(thing) = positions {
let mut temporary = Vec::new();
for i in thingy {
let temp = Vec3::new(i[0], i[1], i[2]);
... // Modify temp here
temporary.push(temp);
}
mesh.unwrap().insert_attribute(Mesh::ATTRIBUTE_POSITION, temporary);
}
}
Fairly new to Bevy, but if you want to mutate an asset, I believe you should be using ResMut rather than Res.
i.e. assets: Res<Assets<Mesh>> should actually be mut assets: ResMut<Assets<Mesh>>.
I used FLTK to create a window and two buttons inside, the btn_A has a callback and should change the btn_B label, but I dont see any non-monstrous approach do to this, ples halp? =''[
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application=app::App::default();
let mut win = window::Window::default().with_size(500,300);
//BTN_A
let mut btn_A:Listener<_> = button::Button::new(100,100,100,50,"btn_A").into();
//BTN_B
let mut btn_B:Listener<_> = button::Button::new(300,100,100,50,"btn_B").into();
//BTN_A_CALLBACK
btn_A.handle(|elem,evt| match evt {
enums::Event::Push => { btn_A(elem); true }
_ => { false }
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A(elem:&mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
}
In principle all that is needed is to pass a mutable reference to btn_B to your handler function:
pub fn btn_A(elem:&mut button::Button, btn_B: &mut button::Button) {
...
}
However there is one slight problem with your code: You named the function the same as the variable that holds your button.
Apart from that in the most recent version of the fltk crate (v.1.2.23, that I used because you did not specify which version you used in your question) there does not seem to be a Listener<_> type.
Here is an example based on the snippet you posted for changing the label of btn_B:
use fltk::{prelude::{WidgetExt, GroupExt, WidgetBase}, window, app, button, enums};
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application = app::App::default();
let mut win = window::Window::default().with_size(500, 300);
//BTN_A
let mut btn_A = button::Button::new(100, 100, 100, 50, "btn_A");
//BTN_B
let mut btn_B = button::Button::new(300, 100, 100, 50, "btn_B");
//BTN_A_CALLBACK
btn_A.handle(move |elem, evt| match evt {
enums::Event::Push => {
btn_A_click(elem, &mut btn_B);
true
}
_ => false,
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A_click(elem: &mut button::Button, btn_B: &mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
btn_B.set_label("New title.")
}
Also note, that the handle closure now takes ownership of btn_B because of the move keyword.
While trying out Bevy, I had the need for dragging and dropping sprites.
Unfortunately, this does not seem to come ready made or I did not find it in the documentation.
What would be the most idiomatic way to achieve this goal?
What I have tried so far is in my answer, but I'll gladly accept another solution that is better/faster/more idiomatic.
I'm not experienced enough to know what's idiomatic unfortunately, however, here's an overview of how I've implemented sprite dragging in my application, and it feels like a good way to me:
I have a "cursor location" entity with a transform component (and a Cursor component for identification) that I update in a system each frame to the location of the cursor.
Every draggable object has a Hoverable and Draggable component. I iterate over those objects in one system each where I add/remove a Hovered and Dragged component to the entities to indicate if they are hovered or dragged.
I have a system that checks if an object is getting dropped, and if so gives it a Dropped component.
I have a system that runs when an entity gets the 'Dragged' component (using the Added<C> filter), which sets the objects parent to the "cursor location" entity.
And another system for when an entity gets the 'Dropped' component, which clears the parent.
To me, having many systems with small areas of responsibility feels good. I would be interested to hear opposing views as I lack experience.
There are of course many things I've left out in this overview, so here's my code for reference. There are some oddities and unnecessary code for a minimal example since this is adapted from my actual code:
#![allow(clippy::type_complexity)]
use bevy::{prelude::*, render::camera::Camera};
fn main() {
App::build()
.init_resource::<State>()
.add_resource(WindowDescriptor {
title: "Bevy".to_string(),
width: 1024.0,
height: 768.0,
vsync: true,
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(MyPlugin)
.run();
}
pub struct MyPlugin;
impl Plugin for MyPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_startup_system(setup.system())
.add_system_to_stage(stage::PRE_UPDATE, cursor_state.system())
.add_system_to_stage(stage::UPDATE, cursor_transform.system())
.add_system_to_stage(stage::UPDATE, draggable.system())
.add_system_to_stage(stage::UPDATE, hoverable.system())
.add_system_to_stage(stage::POST_UPDATE, drag.system())
.add_system_to_stage(stage::POST_UPDATE, drop.system())
.add_system_to_stage(stage::POST_UPDATE, material.system());
}
}
const SPRITE_SIZE: f32 = 55.0;
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let bevy_texture = asset_server.load("sprites/bevy-icon.png");
commands
.spawn(Camera2dBundle::default())
.spawn(())
.with(CursorState::default())
.spawn((Transform::default(), GlobalTransform::default(), Cursor));
for _ in 0..4 {
commands
.spawn(SpriteBundle {
material: materials.add(bevy_texture.clone().into()),
sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
..Default::default()
})
.with(Hoverable)
.with(Draggable);
}
}
#[derive(Default)]
struct CursorState {
cursor_world: Vec2,
cursor_moved: bool,
}
struct Cursor;
struct Draggable;
struct Dragged;
struct Dropped;
struct Hoverable;
struct Hovered;
fn cursor_state(
mut state: ResMut<State>,
e_cursor_moved: Res<Events<CursorMoved>>,
windows: Res<Windows>,
mut q_cursor_state: Query<&mut CursorState>,
q_camera: Query<&Transform, With<Camera>>,
) {
let event_cursor_screen = state.er_cursor_moved.latest(&e_cursor_moved);
for mut cursor_state in q_cursor_state.iter_mut() {
if let Some(event_cursor_screen) = event_cursor_screen {
let window = windows.get_primary().unwrap();
let cam_transform = q_camera.iter().last().unwrap();
cursor_state.cursor_world =
cursor_to_world(window, cam_transform, event_cursor_screen.position);
cursor_state.cursor_moved = true;
} else {
cursor_state.cursor_moved = false;
}
}
}
fn cursor_transform(
commands: &mut Commands,
q_cursor_state: Query<&CursorState>,
mut q_cursor: Query<(Entity, &mut Transform), With<Cursor>>,
) {
let cursor_state = q_cursor_state.iter().next().unwrap();
for (cursor_e, mut transform) in q_cursor.iter_mut() {
transform.translation.x = cursor_state.cursor_world.x;
transform.translation.y = cursor_state.cursor_world.y;
commands.remove_one::<Parent>(cursor_e);
}
}
fn hoverable(
commands: &mut Commands,
q_cursor_state: Query<&CursorState>,
q_hoverable: Query<(Entity, &Transform, &Sprite), (With<Hoverable>, Without<Dragged>)>,
) {
let cursor_state = q_cursor_state.iter().next().unwrap();
if cursor_state.cursor_moved {
for (entity, transform, sprite) in q_hoverable.iter() {
let half_width = sprite.size.x / 2.0;
let half_height = sprite.size.y / 2.0;
if transform.translation.x - half_width < cursor_state.cursor_world.x
&& transform.translation.x + half_width > cursor_state.cursor_world.x
&& transform.translation.y - half_height < cursor_state.cursor_world.y
&& transform.translation.y + half_height > cursor_state.cursor_world.y
{
commands.insert_one(entity, Hovered);
} else {
commands.remove_one::<Hovered>(entity);
}
}
}
}
fn material(
mut materials: ResMut<Assets<ColorMaterial>>,
q_hoverable: Query<
(&Handle<ColorMaterial>, Option<&Hovered>, Option<&Dragged>),
With<Hoverable>,
>,
) {
let mut first = true;
for (material, hovered, dragged) in q_hoverable.iter() {
let (red, green, alpha) = if dragged.is_some() {
(0.0, 1.0, 1.0)
} else if first && hovered.is_some() {
first = false;
(1.0, 0.0, 1.0)
} else if hovered.is_some() {
(1.0, 1.0, 0.5)
} else {
(1.0, 1.0, 1.0)
};
materials.get_mut(material).unwrap().color.set_r(red);
materials.get_mut(material).unwrap().color.set_g(green);
materials.get_mut(material).unwrap().color.set_a(alpha);
}
}
fn cursor_to_world(window: &Window, cam_transform: &Transform, cursor_pos: Vec2) -> Vec2 {
// get the size of the window
let size = Vec2::new(window.width() as f32, window.height() as f32);
// the default orthographic projection is in pixels from the center;
// just undo the translation
let screen_pos = cursor_pos - size / 2.0;
// apply the camera transform
let out = cam_transform.compute_matrix() * screen_pos.extend(0.0).extend(1.0);
Vec2::new(out.x, out.y)
}
fn draggable(
commands: &mut Commands,
i_mouse_button: Res<Input<MouseButton>>,
q_pressed: Query<Entity, (With<Hovered>, With<Draggable>)>,
q_released: Query<Entity, With<Dragged>>,
) {
if i_mouse_button.just_pressed(MouseButton::Left) {
if let Some(entity) = q_pressed.iter().next() {
commands.insert_one(entity, Dragged);
}
} else if i_mouse_button.just_released(MouseButton::Left) {
for entity in q_released.iter() {
commands.remove_one::<Dragged>(entity);
commands.insert_one(entity, Dropped);
}
}
}
fn drag(
commands: &mut Commands,
mut q_dragged: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dragged>>,
q_cursor: Query<(Entity, &GlobalTransform), With<Cursor>>,
) {
if let Some((cursor_e, cursor_transform)) = q_cursor.iter().next() {
for (entity, mut transform, global_transform) in q_dragged.iter_mut() {
let global_pos = global_transform.translation - cursor_transform.translation;
commands.insert_one(entity, Parent(cursor_e));
transform.translation.x = global_pos.x;
transform.translation.y = global_pos.y;
}
}
}
fn drop(
commands: &mut Commands,
mut q_dropped: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dropped>>,
) {
for (entity, mut transform, global_transform) in q_dropped.iter_mut() {
let global_pos = global_transform.translation;
transform.translation.x = global_pos.x;
transform.translation.y = global_pos.y;
commands.remove_one::<Parent>(entity);
commands.remove_one::<Dropped>(entity);
}
}
#[derive(Default)]
struct State {
er_cursor_moved: EventReader<CursorMoved>,
}
This code is for bevy 0.4.
This is the solution I came up with. Complete example
main.rs
use bevy::prelude::*;
use bevy::render::pass::ClearColor;
use bevy::window::CursorMoved;
const SPRITE_SIZE: f32 = 55.0;
fn main() {
App::build()
.add_resource(WindowDescriptor {
width: 1000.0,
height: 1000.0,
resizable: false,
title: "Bevy: drag sprite".to_string(),
..Default::default()
})
.add_resource(Msaa { samples: 4 })
.add_resource(ClearColor(Color::rgb(0.9, 0.9, 0.9)))
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(sprite_system.system())
.add_system(bevy::input::system::exit_on_esc_system.system())
.run();
}
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2dBundle::default());
// show sprite in the middle of the screen
let bevy_texture = asset_server.load("sprites/bevy-icon.png");
commands.spawn(SpriteBundle {
sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
material: materials.add(bevy_texture.clone().into()),
..Default::default()
});
}
#[derive(Default)]
struct State {
cursor_moved_event_reader: EventReader<CursorMoved>,
// store current cursor/mouse position
cursor_pos: Vec2,
// store entity ID and the difference between sprite center and mouse click location
sprite: Option<(Entity, Vec3)>,
}
fn sprite_system(
mut state: Local<State>,
windows: Res<Windows>,
mouse_button_input: Res<Input<MouseButton>>,
cursor_moved_events: Res<Events<CursorMoved>>,
mut sprites: Query<(Entity, &Sprite)>,
mut transforms: Query<&mut Transform>,
) {
let window = windows.get_primary().unwrap();
let half_window = Vec2::new(window.width() / 2.0, window.height() / 2.0);
// if cursor has moved, transform to graphics coordinates and store in state.curser_pos
if let Some(cursor_event) = state.cursor_moved_event_reader.latest(&cursor_moved_events) {
state.cursor_pos = cursor_event.position - half_window;
state.cursor_pos.x = state.cursor_pos.x;
};
// stop dragging if mouse button was released
if mouse_button_input.just_released(MouseButton::Left) {
state.sprite = None;
return;
}
// set new sprite position, if mouse button is pressed and a sprite was clicked on
// take previous click difference into account, to avoid sprite jumps on first move
if mouse_button_input.pressed(MouseButton::Left) && state.sprite.is_some() {
let sprite = state.sprite.unwrap();
let mut sprite_pos = transforms.get_mut(sprite.0).unwrap();
trace!("Sprite position old: {:?}", sprite_pos.translation);
sprite_pos.translation.x = state.cursor_pos.x + sprite.1.x;
sprite_pos.translation.y = state.cursor_pos.y + sprite.1.y;
trace!("Sprite position new: {:?}", sprite_pos.translation);
// position clamping was left out intentionally
}
// store sprite ID and mouse distance from sprite center, if sprite was clicked
if mouse_button_input.just_pressed(MouseButton::Left) {
for (entity, sprite) in sprites.iter_mut() {
let sprite_pos = transforms.get_mut(entity).unwrap().translation;
let diff = cursor_to_sprite_diff(&state.cursor_pos, &sprite_pos);
// sprite is a circle, so check distance from center < sprite radius
if diff.length() < (sprite.size.x / 2.0) {
state.sprite = Some((entity, diff));
}
}
}
}
fn cursor_to_sprite_diff(cursor_pos: &Vec2, sprite_pos: &Vec3) -> Vec3 {
Vec3::new(
sprite_pos.x - cursor_pos.x,
sprite_pos.y - cursor_pos.y,
0.0,
)
}
Cargo.toml
[package]
name = "bevy-drag-sprite"
version = "0.1.0"
authors = ["Me"]
edition = "2018"
[dependencies]
bevy = "0.4"