Assets
Let's complete our board, for this we will need assets:
- A bomb png texture
- A font
(You can use the assets from the tutorial repository)
Place your assets in an assets
folder at the root of your project
├── Cargo.lock ├── Cargo.toml ├── assets │ ├── fonts │ │ └── my_font.ttf │ └── sprites │ ├── bomb.png ├── board_plugin │ ├── Cargo.toml │ └── src │ ├── components │ ├── lib.rs │ └── resources ├── src │ └── main.rs
Let's load these assets in our create_board
startup system.
For this we need to add an argument to the system:
pub fn create_board( mut commands: Commands, board_options: Option<Res<BoardOptions>>, window: Res<WindowDescriptor>, asset_server: Res<AssetServer>, // The AssetServer resource ) {
The AssetServer
resource allows loading files from the assets
folder.
We can now load our assets right at the beginning of the function and retrieve handles:
// lib.rs // .. let font = asset_server.load("fonts/pixeled.ttf"); let bomb_image = asset_server.load("sprites/bomb.png"); // ..
Component declaration
Our tile map knows which tile is a bomb, a bomb neighbor or empty, but the ECS doesn't.
Let's declare components we will attach to our tile entities in our plugin under board_plugin/components
with our Coordinates
component:
board_plugin/src/components/bomb.rs
board_plugin/src/components/bomb_neighbor.rs
-
board_plugin/src/components/uncover.rs
// board_plugin/src/components/mod.rs pub use bomb::Bomb; pub use bomb_neighbor::BombNeighbor; pub use uncover::Uncover; mod bomb; mod bomb_neighbor; mod uncover;
Bomb
This component will identify a tile as a bomb
// bomb.rs use bevy::prelude::Component; /// Bomb component #[cfg_attr(feature = "debug", derive(bevy_inspector_egui::Inspectable))] #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component)] pub struct Bomb;
Bomb neighbor
This component will identify a tile as bomb neighbor
use bevy::prelude::Component; /// Bomb neighbor component #[cfg_attr(feature = "debug", derive(bevy_inspector_egui::Inspectable))] #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component)] pub struct BombNeighbor { /// Number of neighbor bombs pub count: u8, }
Uncover
This component will identify tiles to uncover, we will use it in part 6
use bevy::prelude::Component; /// Uncover component, indicates a covered tile that should be uncovered #[cfg_attr(feature = "debug", derive(bevy_inspector_egui::Inspectable))] #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component)] pub struct Uncover;
Let's register the components for the debug inspector in our plugin:
// lib.rs #[cfg(feature = "debug")] use bevy_inspector_egui::RegisterInspectable; use components::*; impl Plugin for BoardPlugin { fn build(&self, app: &mut App) { // .. #[cfg(feature = "debug")] { // registering custom component to be able to edit it in inspector app.register_inspectable::<Coordinates>(); app.register_inspectable::<BombNeighbor>(); app.register_inspectable::<Bomb>(); app.register_inspectable::<Uncover>(); } } }
Component use
Let's create a function for BoardPlugin
creating a Text2DBundle
for our bomb counters:
/// Generates the bomb counter text 2D Bundle for a given value fn bomb_count_text_bundle(count: u8, font: Handle<Font>, size: f32) -> Text2dBundle { // We retrieve the text and the correct color let (text, color) = ( count.to_string(), match count { 1 => Color::WHITE, 2 => Color::GREEN, 3 => Color::YELLOW, 4 => Color::ORANGE, _ => Color::PURPLE, }, ); // We generate a text bundle Text2dBundle { text: Text { sections: vec![TextSection { value: text, style: TextStyle { color, font, font_size: size, }, }], alignment: TextAlignment { vertical: VerticalAlign::Center, horizontal: HorizontalAlign::Center, }, }, transform: Transform::from_xyz(0., 0., 1.), ..Default::default() } }
It takes in parameter:
-
count
: the neighboring bomb count to print -
font
: a assetHandle
of our font -
size
: a text size
The colors are completely arbitrary.
Again, we put the z
value of the Transform
translation to 1.
so the text is printed on top of the tile.
We can now move our tile spawning loop into a distinct function:
// lib.rs fn spawn_tiles( parent: &mut ChildBuilder, tile_map: &TileMap, size: f32, padding: f32, color: Color, bomb_image: Handle<Image>, font: Handle<Font>, ) { // Tiles for (y, line) in tile_map.iter().enumerate() { for (x, tile) in line.iter().enumerate() { let coordinates = Coordinates { x: x as u16, y: y as u16, }; let mut cmd = parent.spawn(); cmd.insert_bundle(SpriteBundle { sprite: Sprite { color, custom_size: Some(Vec2::splat(size - padding)), ..Default::default() }, transform: Transform::from_xyz( (x as f32 * size) + (size / 2.), (y as f32 * size) + (size / 2.), 1., ), ..Default::default() }) .insert(Name::new(format!("Tile ({}, {})", x, y))) .insert(coordinates); } } }
- Notice we now use a temporary
cmd
value for our entity builder*
We can now call it from our create_board
startup system, in our with_children
block after the background spawn:
// lib.rs // .. Self::spawn_tiles( parent, &tile_map, tile_size, options.tile_padding, Color::GRAY, bomb_image, font, );
Then we complete our spawn_tiles
function to add our bombs sprites and counter texts inside the double for
loop:
// lib.rs use resources::tile::Tile; // .. match tile { // If the tile is a bomb we add the matching component and a sprite child Tile::Bomb => { cmd.insert(Bomb); cmd.with_children(|parent| { parent.spawn_bundle(SpriteBundle { sprite: Sprite { custom_size: Some(Vec2::splat(size - padding)), ..Default::default() }, transform: Transform::from_xyz(0., 0., 1.), texture: bomb_image.clone(), ..Default::default() }); }); } // If the tile is a bomb neighbour we add the matching component and a text child Tile::BombNeighbor(v) => { cmd.insert(BombNeighbor { count: *v }); cmd.with_children(|parent| { parent.spawn_bundle(Self::bomb_count_text_bundle( *v, font.clone(), size - padding, )); }); } Tile::Empty => (), } // ..
Until now all tiles had identical components like Coordinates
, Transform
, Sprite
, etc.
But now some tiles have:
- A
Bomb
component and a child entity with the bomb sprite - A
BombNeighbor
component and a child entity with the counter text
We added a texture to the bomb sprite, what about the others?
By default, a white square texture is used if no texture
is specified on SpriteBundle
. In Part 9 we will see it in more detail.
Let's run our app and get our beautiful board:
Previous Chapter -- Next Chapter
Author: Félix de Maneville
Follow me on Twitter
Published by Qongzi
Top comments (5)
When I run using "cargo run" assets are loaded correctly. But when I try to launch debugger with visual studio code it fails loading assets.
my configuration looks like this:
should "cwd" (program working directory) be changed to something else?
Oh and also had to change BoardPlugin build debug section to use registry instead to make it work.:
Had to add environment variable in launch json :
a clue found in : docs.rs/bevy_asset/0.6.0/bevy_asse...
I don't use VS Code so I don't know about the issues with the built in debugger.
About the registry, you are using the old way of registering the components
Are you using the latest version of
bevy_inspector_egui
? It should be0.8.x
.Accessing the inspectable registry is no longer required
hmm. set the version in cargo to version = "~0.8". checked to see that version 0.8.2 is being downloaded. still had the same issue. Got panic:
bevy inspector egui panicked when unwrapping:
also. it seems that order of adding world inspector plugin matters (somehow expecting it to be strictly declarative). If I register the plugin at the start like this:
I get no panic, but sadly I also do not get any world inspector plugin.
If I add the world inspector plugin just before the app run it ends up panicking.
but anyways...
registry.register::<T>()
work for me so I do not really care at this point :P. Thanks for your time!github.com/leonidv/bevy-minesweepe... - full tutorial updated to 12.1. One chapter per commit.