//! Shows how to create graphics that snap to the pixel grid by rendering to a texture in 2D use bevy::{ prelude::*, render::{ camera::RenderTarget, render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, view::RenderLayers, }, sprite::MaterialMesh2dBundle, window::WindowResized, }; /// In-game resolution width. const RES_WIDTH: u32 = 160; /// In-game resolution height. const RES_HEIGHT: u32 = 90; /// Default render layers for pixel-perfect rendering. /// You can skip adding this component, as this is the default. const PIXEL_PERFECT_LAYERS: RenderLayers = RenderLayers::layer(0); /// Render layers for high-resolution rendering. const HIGH_RES_LAYERS: RenderLayers = RenderLayers::layer(1); fn main() { App::new() .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_systems(Startup, (setup_camera, setup_sprite, setup_mesh)) .add_systems(Update, (rotate, fit_canvas)) .run(); } /// Low-resolution texture that contains the pixel-perfect world. /// Canvas itself is rendered to the high-resolution world. #[derive(Component)] struct Canvas; /// Camera that renders the pixel-perfect world to the [`Canvas`]. #[derive(Component)] struct InGameCamera; /// Camera that renders the [`Canvas`] (and other graphics on [`HIGH_RES_LAYERS`]) to the screen. #[derive(Component)] struct OuterCamera; #[derive(Component)] struct Rotate; fn setup_sprite(mut commands: Commands, asset_server: Res) { // the sample sprite that will be rendered to the pixel-perfect canvas commands.spawn(( SpriteBundle { texture: asset_server.load("pixel/bevy_pixel_dark.png"), transform: Transform::from_xyz(-40., 20., 2.), ..default() }, Rotate, PIXEL_PERFECT_LAYERS, )); // the sample sprite that will be rendered to the high-res "outer world" commands.spawn(( SpriteBundle { texture: asset_server.load("pixel/bevy_pixel_light.png"), transform: Transform::from_xyz(-40., -20., 2.), ..default() }, Rotate, HIGH_RES_LAYERS, )); } /// Spawns a capsule mesh on the pixel-perfect layer. fn setup_mesh( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(( MaterialMesh2dBundle { mesh: meshes.add(Capsule2d::default()).into(), transform: Transform::from_xyz(40., 0., 2.).with_scale(Vec3::splat(32.)), material: materials.add(Color::BLACK), ..default() }, Rotate, PIXEL_PERFECT_LAYERS, )); } fn setup_camera(mut commands: Commands, mut images: ResMut>) { let canvas_size = Extent3d { width: RES_WIDTH, height: RES_HEIGHT, ..default() }; // this Image serves as a canvas representing the low-resolution game screen let mut canvas = Image { texture_descriptor: TextureDescriptor { label: None, size: canvas_size, dimension: TextureDimension::D2, format: TextureFormat::Bgra8UnormSrgb, mip_level_count: 1, sample_count: 1, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT, view_formats: &[], }, ..default() }; // fill image.data with zeroes canvas.resize(canvas_size); let image_handle = images.add(canvas); // this camera renders whatever is on `PIXEL_PERFECT_LAYERS` to the canvas commands.spawn(( Camera2dBundle { camera: Camera { // render before the "main pass" camera order: -1, target: RenderTarget::Image(image_handle.clone()), ..default() }, msaa: Msaa::Off, ..default() }, InGameCamera, PIXEL_PERFECT_LAYERS, )); // spawn the canvas commands.spawn(( SpriteBundle { texture: image_handle, ..default() }, Canvas, HIGH_RES_LAYERS, )); // the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen. // here, the canvas and one of the sample sprites will be rendered by this camera commands.spawn(( Camera2dBundle { msaa: Msaa::Off, ..default() }, OuterCamera, HIGH_RES_LAYERS, )); } /// Rotates entities to demonstrate grid snapping. fn rotate(time: Res