camera zooming

This commit is contained in:
2025-05-09 18:13:28 +01:00
parent e3ea662668
commit 31e6eb29b2
7 changed files with 96 additions and 64 deletions
+55
View File
@@ -1,13 +1,39 @@
use bevy::input::keyboard::KeyCode; use bevy::input::keyboard::KeyCode;
use bevy::input::mouse::{MouseScrollUnit, MouseWheel};
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::camera::{OrthographicProjection, Projection};
use crate::constants::TILE_SIZE; use crate::constants::TILE_SIZE;
use crate::{game, tilemap};
#[derive(Component)] #[derive(Component)]
pub struct PanningCamera { pub struct PanningCamera {
pub pan_speed: f32, pub pan_speed: f32,
} }
#[derive(Resource, Default)]
pub struct CameraMoved(pub bool);
pub fn camera_z_movement(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut z_index: ResMut<game::ZIndex>,
mut camera_moved: ResMut<CameraMoved>,
) {
camera_moved.0 = false;
if keyboard_input.just_pressed(KeyCode::ShiftLeft) {
z_index.0 -= 1.0;
z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
if keyboard_input.just_pressed(KeyCode::ShiftRight) {
z_index.0 += 1.0;
z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
}
pub fn camera_movement( pub fn camera_movement(
keyboard_input: Res<ButtonInput<KeyCode>>, keyboard_input: Res<ButtonInput<KeyCode>>,
mut query: Query<(&PanningCamera, &mut Transform), With<Camera>>, mut query: Query<(&PanningCamera, &mut Transform), With<Camera>>,
@@ -41,7 +67,36 @@ pub fn camera_movement(
pub fn spawn_panning_camera(mut commands: Commands) { pub fn spawn_panning_camera(mut commands: Commands) {
commands.spawn(( commands.spawn((
Camera2d, Camera2d,
Projection::Orthographic(OrthographicProjection {
scale: 1.0,
..OrthographicProjection::default_2d()
}),
Transform::from_xyz(0., 0., 10. * TILE_SIZE), Transform::from_xyz(0., 0., 10. * TILE_SIZE),
PanningCamera { pan_speed: 15.0 }, PanningCamera { pan_speed: 15.0 },
)); ));
} }
pub fn scroll_events(mut evr_scroll: EventReader<MouseWheel>, mut query: Query<&mut Projection>) {
const ZOOM_SENSITIVITY: f32 = 0.05;
const MIN_SCALE: f32 = 0.4;
const MAX_SCALE: f32 = 1.5;
for ev in evr_scroll.read() {
for mut projection_component in query.iter_mut() {
let current_scale = match &*projection_component {
Projection::Orthographic(ortho_proj) => ortho_proj.scale,
Projection::Perspective(_) => 1.0,
Projection::Custom(_custom_projection) => todo!(),
};
let new_scale = match ev.unit {
MouseScrollUnit::Line => current_scale + ev.y * ZOOM_SENSITIVITY,
MouseScrollUnit::Pixel => todo!(),
};
*projection_component = Projection::Orthographic(OrthographicProjection {
scale: new_scale.max(MIN_SCALE).min(MAX_SCALE),
..OrthographicProjection::default_2d()
});
}
}
}
+7 -6
View File
@@ -17,7 +17,7 @@ impl Citizen {
pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self { pub fn new(asset_server: &Res<AssetServer>, position: Vec3) -> Self {
Citizen { Citizen {
ambulatory: Ambulatory { ambulatory: Ambulatory {
walk_speed: 2., walk_speed: 0.,
run_speed: 6., run_speed: 6.,
target: None, target: None,
current_path: None, current_path: None,
@@ -197,7 +197,8 @@ pub fn citizen_movement(
if transform.translation.z / TILE_SIZE <= z_index.0 + 1.0 { if transform.translation.z / TILE_SIZE <= z_index.0 + 1.0 {
*visibility = Visibility::Visible; *visibility = Visibility::Visible;
let saturation = let saturation =
((z_index.0 - transform.translation.z / TILE_SIZE) / 8.0) ((z_index.0 - (transform.translation.z / TILE_SIZE) + 1.)
/ 8.0)
.clamp(0.0, 1.0); .clamp(0.0, 1.0);
sprite.color = sprite.color =
@@ -324,7 +325,7 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec<Vec3> {
) { ) {
(1, 0, 0) | (0, 1, 0) | (0, 0, 1) => 10, // Orthogonal movement (1 axis) (1, 0, 0) | (0, 1, 0) | (0, 0, 1) => 10, // Orthogonal movement (1 axis)
(1, 1, 0) | (1, 0, 1) | (0, 1, 1) => 14, // Diagonal movement (2 axes) (1, 1, 0) | (1, 0, 1) | (0, 1, 1) => 14, // Diagonal movement (2 axes)
(1, 1, 1) => 17, // Full 3D diagonal movement (3 axes) (1, 1, 1) => 20, // Full 3D diagonal movement (3 axes)
_ => continue, // Invalid movement _ => continue, // Invalid movement
}; };
@@ -405,9 +406,9 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res<AssetServer>) {
// Spawn a handful of citizens // Spawn a handful of citizens
for _ in 0..100 { for _ in 0..100 {
let x: f32 = rng.random_range(-8.0..8.0); let x: f32 = rng.random_range(-5.0..5.0);
let y: f32 = rng.random_range(-8.0..8.0); let y: f32 = rng.random_range(-5.0..5.0);
let mut position = Vec3::new(x.round(), y.round(), 30.0) * TILE_SIZE; let mut position = Vec3::new(x.round(), y.round(), 35.0) * TILE_SIZE;
position.z += 0.1; position.z += 0.1;
commands.spawn(Citizen::new(&asset_server, position)); commands.spawn(Citizen::new(&asset_server, position));
+1 -1
View File
@@ -1,4 +1,4 @@
pub const PIXEL_RATIO: f32 = 2.0; pub const PIXEL_RATIO: f32 = 1.0;
pub const TILE_PIXELS: u32 = 16; pub const TILE_PIXELS: u32 = 16;
pub const TILE_SIZE: f32 = TILE_PIXELS as f32 * PIXEL_RATIO; pub const TILE_SIZE: f32 = TILE_PIXELS as f32 * PIXEL_RATIO;
pub const ITILE_SIZE: i32 = TILE_SIZE as i32; pub const ITILE_SIZE: i32 = TILE_SIZE as i32;
+6 -8
View File
@@ -42,19 +42,17 @@ fn main() {
) )
.add_systems( .add_systems(
FixedUpdate, FixedUpdate,
( (tiles::build_quilted_terrain_sprites, camera::scroll_events),
camera::camera_movement,
cursor::move_cursor,
tiles::build_quilted_terrain_sprites,
),
) )
.init_resource::<tile::CameraMoved>() .init_resource::<camera::CameraMoved>()
.add_systems( .add_systems(
Update, Update,
( (
tile::camera_z_movement, camera::camera_z_movement,
camera::camera_movement,
cursor::move_cursor,
tile::update_tile_visibility tile::update_tile_visibility
.run_if(|camera_moved: Res<tile::CameraMoved>| camera_moved.0), .run_if(|camera_moved: Res<camera::CameraMoved>| camera_moved.0),
), ),
) )
.run(); .run();
-23
View File
@@ -47,29 +47,6 @@ impl Default for FixtureTile {
} }
} }
#[derive(Resource, Default)]
pub struct CameraMoved(pub bool);
pub fn camera_z_movement(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut z_index: ResMut<game::ZIndex>,
mut camera_moved: ResMut<CameraMoved>,
) {
camera_moved.0 = false;
if keyboard_input.just_pressed(KeyCode::ShiftLeft) {
z_index.0 -= 1.0;
z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
if keyboard_input.just_pressed(KeyCode::ShiftRight) {
z_index.0 += 1.0;
z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
}
#[derive(Resource, Default, Clone)] #[derive(Resource, Default, Clone)]
pub struct TileMap { pub struct TileMap {
pub floor_tiles: HashMap<IVec3, (u32, bool, bool, u8, [u32; 8])>, // id, opaque, walkable, astar_weight, visible_range pub floor_tiles: HashMap<IVec3, (u32, bool, bool, u8, [u32; 8])>, // id, opaque, walkable, astar_weight, visible_range
+8 -7
View File
@@ -11,8 +11,8 @@ use noise::{NoiseFn, Perlin};
pub const CHUNK_SIZE: i32 = 8; pub const CHUNK_SIZE: i32 = 8;
pub const Z_BELOW: f32 = 3.0; pub const Z_BELOW: f32 = 20.0;
pub const Z_ABOVE: f32 = 5.0; pub const Z_ABOVE: f32 = 10.0;
pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; // MAX 255 DO NOT EXCEED pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; // MAX 255 DO NOT EXCEED
#[derive(Resource)] #[derive(Resource)]
@@ -164,6 +164,7 @@ fn generate_chunks_from_algo(
) { ) {
let is_empty = events.is_empty(); let is_empty = events.is_empty();
let start = Instant::now(); let start = Instant::now();
let count = events.len();
let cave_noise = Perlin::new(0); let cave_noise = Perlin::new(0);
for event in events.read() { for event in events.read() {
@@ -204,9 +205,9 @@ fn generate_chunks_from_algo(
let noise_value = cave_noise.get([ let noise_value = cave_noise.get([
world_x as f64 * 0.05, world_x as f64 * 0.05,
world_y as f64 * 0.05, world_y as f64 * 0.05,
z as f64 * (0.15 * -z as f64), z as f64 * 0.05,
]); ]);
if noise_value < -0.65 { if noise_value < -0.85 {
FloorTilePrefab::air(position).spawn(&mut commands); FloorTilePrefab::air(position).spawn(&mut commands);
tilemap tilemap
.floor_tiles .floor_tiles
@@ -278,13 +279,13 @@ fn generate_chunks_from_algo(
} }
} }
if !is_empty { if !is_empty {
println!("Chunks loaded in {:.2?}", start.elapsed()); println!("{} chunks loaded in {:.2?}", count, start.elapsed());
} }
} }
fn setup_initial_chunks(mut event_writer: EventWriter<LoadChunkEvent>) { fn setup_initial_chunks(mut event_writer: EventWriter<LoadChunkEvent>) {
for x in -10..=10 { for x in -16..=16 {
for y in -10..=10 { for y in -9..=9 {
event_writer.write(LoadChunkEvent { event_writer.write(LoadChunkEvent {
chunk_position: IVec2::new(x, y), chunk_position: IVec2::new(x, y),
}); });
+19 -19
View File
@@ -328,26 +328,26 @@ fn blit_texture_with_alpha(
} }
// This system handles updating quilted sprites when the world changes // This system handles updating quilted sprites when the world changes
pub fn update_quilts_on_world_change( // pub fn update_quilts_on_world_change(
mut commands: Commands, // mut commands: Commands,
mut quilt_cache: ResMut<QuiltCache>, // mut quilt_cache: ResMut<QuiltCache>,
mut cwss: ResMut<CurrentWorldSpriteState>, // mut cwss: ResMut<CurrentWorldSpriteState>,
// Add any resources or queries that indicate world changes // Add any resources or queries that indicate world changes
// For example, if you have a WorldChangeEvent: // For example, if you have a WorldChangeEvent:
// mut world_changes: EventReader<WorldChangeEvent>, // mut world_changes: EventReader<WorldChangeEvent>,
) { // ) {
// Example: Check for world changes // Example: Check for world changes
// if !world_changes.is_empty() { // if !world_changes.is_empty() {
// for event in world_changes.iter() { // for event in world_changes.iter() {
// quilt_cache.dirty_indices.push(event.z_index); // quilt_cache.dirty_indices.push(event.z_index);
// } // }
// cwss.state = WorldSpriteState::WaitingForRender; // cwss.state = WorldSpriteState::WaitingForRender;
// } // }
// Alternatively, if you have specific systems that modify the world, // Alternatively, if you have specific systems that modify the world,
// you could have them set cwss.state = WorldSpriteState::WaitingForRender // you could have them set cwss.state = WorldSpriteState::WaitingForRender
// and add affected z-indices to quilt_cache.dirty_indices // and add affected z-indices to quilt_cache.dirty_indices
} // }
#[derive(Bundle)] #[derive(Bundle)]
pub struct FloorTilePrefab { pub struct FloorTilePrefab {