| 1 |
|
| 2 |
|
| 3 |
use bevy::{ |
| 4 |
camera::visibility::RenderLayers, camera::RenderTarget, color::palettes::css::GRAY, prelude::*, |
| 5 |
render::render_resource::TextureFormat, window::WindowResized, |
| 6 |
}; |
| 7 |
|
| 8 |
|
| 9 |
const RES_WIDTH: u32 = 160; |
| 10 |
|
| 11 |
|
| 12 |
const RES_HEIGHT: u32 = 90; |
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
const PIXEL_PERFECT_LAYERS: RenderLayers = RenderLayers::layer(0); |
| 17 |
|
| 18 |
|
| 19 |
const HIGH_RES_LAYERS: RenderLayers = RenderLayers::layer(1); |
| 20 |
|
| 21 |
fn main() { |
| 22 |
App::new() |
| 23 |
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) |
| 24 |
.add_systems(Startup, (setup_camera, setup_sprite, setup_mesh)) |
| 25 |
.add_systems(Update, (rotate, fit_canvas)) |
| 26 |
.run(); |
| 27 |
} |
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
#[derive(Component)] |
| 32 |
struct Canvas; |
| 33 |
|
| 34 |
|
| 35 |
#[derive(Component)] |
| 36 |
struct InGameCamera; |
| 37 |
|
| 38 |
|
| 39 |
#[derive(Component)] |
| 40 |
struct OuterCamera; |
| 41 |
|
| 42 |
#[derive(Component)] |
| 43 |
struct Rotate; |
| 44 |
|
| 45 |
fn setup_sprite(mut commands: Commands, asset_server: Res<AssetServer>) { |
| 46 |
|
| 47 |
commands.spawn(( |
| 48 |
Sprite::from_image(asset_server.load("pixel/bevy_pixel_dark.png")), |
| 49 |
Transform::from_xyz(-45., 20., 2.), |
| 50 |
Rotate, |
| 51 |
PIXEL_PERFECT_LAYERS, |
| 52 |
)); |
| 53 |
|
| 54 |
|
| 55 |
commands.spawn(( |
| 56 |
Sprite::from_image(asset_server.load("pixel/bevy_pixel_light.png")), |
| 57 |
Transform::from_xyz(-45., -20., 2.), |
| 58 |
Rotate, |
| 59 |
HIGH_RES_LAYERS, |
| 60 |
)); |
| 61 |
} |
| 62 |
|
| 63 |
|
| 64 |
fn setup_mesh( |
| 65 |
mut commands: Commands, |
| 66 |
mut meshes: ResMut<Assets<Mesh>>, |
| 67 |
mut materials: ResMut<Assets<ColorMaterial>>, |
| 68 |
) { |
| 69 |
commands.spawn(( |
| 70 |
Mesh2d(meshes.add(Capsule2d::default())), |
| 71 |
MeshMaterial2d(materials.add(Color::BLACK)), |
| 72 |
Transform::from_xyz(25., 0., 2.).with_scale(Vec3::splat(32.)), |
| 73 |
Rotate, |
| 74 |
PIXEL_PERFECT_LAYERS, |
| 75 |
)); |
| 76 |
} |
| 77 |
|
| 78 |
fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) { |
| 79 |
|
| 80 |
let canvas = |
| 81 |
Image::new_target_texture(RES_WIDTH, RES_HEIGHT, TextureFormat::Bgra8UnormSrgb, None); |
| 82 |
|
| 83 |
let image_handle = images.add(canvas); |
| 84 |
|
| 85 |
|
| 86 |
commands.spawn(( |
| 87 |
Camera2d, |
| 88 |
Camera { |
| 89 |
|
| 90 |
order: -1, |
| 91 |
clear_color: ClearColorConfig::Custom(GRAY.into()), |
| 92 |
..default() |
| 93 |
}, |
| 94 |
RenderTarget::Image(image_handle.clone().into()), |
| 95 |
Msaa::Off, |
| 96 |
InGameCamera, |
| 97 |
PIXEL_PERFECT_LAYERS, |
| 98 |
)); |
| 99 |
|
| 100 |
|
| 101 |
commands.spawn((Sprite::from_image(image_handle), Canvas, HIGH_RES_LAYERS)); |
| 102 |
|
| 103 |
|
| 104 |
|
| 105 |
commands.spawn((Camera2d, Msaa::Off, OuterCamera, HIGH_RES_LAYERS)); |
| 106 |
} |
| 107 |
|
| 108 |
|
| 109 |
fn rotate(time: Res<Time>, mut transforms: Query<&mut Transform, With<Rotate>>) { |
| 110 |
for mut transform in &mut transforms { |
| 111 |
let dt = time.delta_secs(); |
| 112 |
transform.rotate_z(dt); |
| 113 |
} |
| 114 |
} |
| 115 |
|
| 116 |
|
| 117 |
fn fit_canvas( |
| 118 |
mut resize_messages: MessageReader<WindowResized>, |
| 119 |
mut projection: Single<&mut Projection, With<OuterCamera>>, |
| 120 |
) { |
| 121 |
let Projection::Orthographic(projection) = &mut **projection else { |
| 122 |
return; |
| 123 |
}; |
| 124 |
for window_resized in resize_messages.read() { |
| 125 |
let h_scale = window_resized.width / RES_WIDTH as f32; |
| 126 |
let v_scale = window_resized.height / RES_HEIGHT as f32; |
| 127 |
projection.scale = 1. / h_scale.min(v_scale).floor(); |
| 128 |
} |
| 129 |
} |