Trying to render a checkerboard with Bevy

I'm trying to render a checkerboard (or make a grid-based system in general) in Bevy.

I have this component:

#[derive(Debug, Component)]
/// Defines a single grid.
/// As `Cell` is a term used in Rust terminology, Grid is a better way to refer to this.
pub(crate) struct Grid(u8, u8);

impl Grid {
    pub(crate) fn new(x: u8, y: u8) -> Self {
        Self(x, y)
    }

    pub(crate) fn x(&self) -> u8 {
        self.0
    }

    pub(crate) fn y(&self) -> u8 {
        self.1
    }

    pub(crate) fn indices(&self) -> (u8, u8) {
        (self.x(), self.y())
    }
}

And these startup systems:

pub(crate) fn init_grids(mut commands: Commands) {
    debug!("Initializing grids...");

    //| initializing grids up to u8::MAX
    for x in 0..u8::MAX {
        for y in 0..u8::MAX {
            commands.spawn(Grid::new(x, y));
        }
    }
}

pub(crate) fn paint_grids(
    grid_query: Query<&Grid>,
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    debug!("Painting grids...");

    //| iterating over each grid
    for grid in grid_query.iter() {
        //| unpack x and y val of the grid
        let (grid_x, grid_y) = grid.indices();

        //| choose color black or white based on grid x and y value
        let color = if (grid_x + grid_y) % 2 == 0 {
            Color::BLACK
        } else {
            Color::WHITE
        };

        //| define grid's pixel position
        let pos_x = grid_x * GRID_SIZE;
        let pos_y = grid_y * GRID_SIZE;

        //| create a rectangle
        let mesh_handler =
            Mesh2dHandle(meshes.add(Rectangle::new(GRID_SIZE as f32, GRID_SIZE as f32)));

        //| draw the rectangle
        commands.spawn(MaterialMesh2dBundle {
            mesh: mesh_handler,
            material: materials.add(color), //| define its color
            transform: Transform::from_xyz(pos_x as f32, pos_y as f32, 0.0), //| define its pos in the screen
            ..Default::default()
        });
    }
}

...where GRID_SIZE is a u8 constant and its value is 16.

DefaultPlugins and Camera2dBundle are already initialized, but I get a plain gray screen.

Despite initializing camera and default plugins and adding the systems, it renders a blank screen.

blank screen

I'm kind of new to Bevy (not to Rust), so I was wondering if there's something I'm missing here.

Thanks in advance.


Environment

  • Bevy 0.13.2
  • Rust 1.76.0

Update 1

The projects has a CorePlugin where it sets up all the other plugins. Right now, it only has another plugin named GridPlugin.

Some files that might be related:

// main.rs
use bevy::app::App;
use oxidized_pixel_dungeon::core::CorePlugin;

fn main() {
    App::new().add_plugins(CorePlugin).run();
}
// core/mod.rs
use bevy::{
    app::{Plugin, PluginGroup, Startup},
    core_pipeline::core_2d::Camera2dBundle,
    ecs::system::Commands,
    log::LogPlugin,
    utils::tracing::field::debug,
    DefaultPlugins,
};

use crate::grid::GridPlugin;

pub struct CorePlugin;

impl Plugin for CorePlugin {
    fn build(&self, app: &mut bevy::prelude::App) {
        let default_plugins = DefaultPlugins.set(if cfg!(debug_assertions) {
            LogPlugin {
                level: bevy::log::Level::TRACE,
                filter: "info,wgpu_core=warn,wgpu_hal=warn,oxidized_pixel_dungeon=trace".into(),
                ..Default::default()
            }
        } else {
            LogPlugin {
                level: bevy::log::Level::INFO,
                filter: "info,wgpu_core=warn,wgpu_hal=warn".into(),
                ..Default::default()
            }
        });

        app.add_plugins(default_plugins)
            .add_systems(Startup, init_system)
            .add_plugins(GridPlugin);
    }
}

fn init_system(mut commands: Commands) {
    debug("Running CorePlugin::init_system");
    commands.spawn(Camera2dBundle::default());
}
// grid/mod.rs
pub mod components;
pub(crate) mod constants;
mod systems;

use bevy::{
    app::{Plugin, Startup},
    asset::Assets,
    ecs::system::{Commands, ResMut},
    log::debug,
    math::primitives::Rectangle,
    render::{color::Color, mesh::Mesh},
    sprite::{ColorMaterial, MaterialMesh2dBundle, Mesh2dHandle},
    transform::components::Transform,
};

use crate::grid::systems::{init_grids, paint_grids};

pub(super) struct GridPlugin;

impl Plugin for GridPlugin {
    fn build(&self, app: &mut bevy::prelude::App) {
        debug!("Initializing GridPlugin...");
        app.add_systems(Startup, (init_grids, paint_grids));
    }
}

Can share your app and camera setup code as well?

Updated.

Now that I think about it, I think it might be about the order of systems. As Bevy runs in parallel, the order of systems are not guaranteed. Let me try that.

Nope, app.add_systems(Startup, init_grids.after(paint_grids)); does not work either. Still, though, I think Camera2dBundle might initialize after GridPlugin. Gotta find a way to sequence "first CorePlugin then GridPlugin".

I think your problem is that entities are spawned after the startup schedule is done, and that only runs once. So your startup system gets an empty query. Either turn it into an update system or just spawn the meshes along with the grid.

Update system seems reasonable, I could query Grid with MaterialMesh2dBundle and paint it on the update. Let's see how it goes.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.