From d64cff7e90953c9366874aad4177701754071d75 Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Tue, 13 Dec 2022 02:43:01 +0200 Subject: [PATCH] fix: collision rebuilding --- src/game.rs | 10 ++--- src/game/camera.rs | 2 +- src/game/kinematic.rs | 10 ----- src/game/player.rs | 7 +--- src/terrain2d.rs | 84 ++++++++++++++++++++++++---------------- src/terrain2d/chunk2d.rs | 70 +++++++++++++++++++++------------ 6 files changed, 103 insertions(+), 80 deletions(-) diff --git a/src/game.rs b/src/game.rs index 776bf90..547695e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -4,7 +4,7 @@ use bevy_prototype_debug_lines::DebugLinesPlugin; use bevy_rapier2d::prelude::*; use crate::{ - terrain2d::{Terrain2D, Terrain2DPlugin, TerrainGen2D}, + terrain2d::{Chunk2D, Terrain2D, Terrain2DPlugin, TerrainGen2D}, util::Vector2I, }; @@ -30,7 +30,6 @@ pub fn init() { .add_plugin(Terrain2DPlugin) .add_plugin(PlayerPlugin) .add_startup_system(setup_debug_terrain) - .add_system(debug_controls) .run(); } @@ -47,11 +46,8 @@ fn debug_controls( fn setup_debug_terrain(mut commands: Commands, mut terrain: ResMut) { let terrain_gen = TerrainGen2D::new(432678); - // for y in 0..(WORLD_WIDTH / Chunk2D::SIZE_Y as i32) { - // for x in 0..(WORLD_WIDTH / Chunk2D::SIZE_X as i32) { - // DEBUG: - for y in 1..2 { - for x in 8..9 { + for y in 0..(WORLD_WIDTH / Chunk2D::SIZE_Y as i32) { + for x in 0..(WORLD_WIDTH / Chunk2D::SIZE_X as i32) { let position = Vector2I { x, y }; terrain.add_chunk(position, terrain_gen.gen_chunk(&position)); } diff --git a/src/game/camera.rs b/src/game/camera.rs index a16ecc6..cd1df8e 100644 --- a/src/game/camera.rs +++ b/src/game/camera.rs @@ -50,7 +50,7 @@ fn camera_setup(mut commands: Commands) { projection: OrthographicProjection { scaling_mode: ScalingMode::FixedHorizontal(WORLD_WIDTH as f32), window_origin: WindowOrigin::Center, - scale: 0.5, + scale: 1.0 / 2.0, ..default() }, camera_2d: Camera2d { diff --git a/src/game/kinematic.rs b/src/game/kinematic.rs index 06183a9..aba03f7 100644 --- a/src/game/kinematic.rs +++ b/src/game/kinematic.rs @@ -199,7 +199,6 @@ fn kinematic_movement( } if let Some(collider) = collider { let (_, rot, pos) = global_transform.to_scale_rotation_translation(); - // println!("{pos}"); let angle = rot.to_euler(EulerRot::YXZ).2; let mut shape = collider.clone(); shape.set_scale(Vec2::ONE * 0.9, 1); @@ -211,18 +210,9 @@ fn kinematic_movement( 2.0, QueryFilter::new().exclude_collider(entity), ) { - // println!( - // "Collision!\n\t{id:?} {name}\n\t{hit:?}", - // id = coll_entity, - // name = name_query.get(coll_entity).unwrap(), - // ); kinematic_state.on_ground = true; - // DEBUG: - println!("grounded"); } else { kinematic_state.on_ground = false; - // DEBUG: - println!("no ground"); } } } diff --git a/src/game/player.rs b/src/game/player.rs index 17a85ce..2f6f72f 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -61,9 +61,7 @@ pub fn player_spawn(mut commands: Commands) { let kinematic = KinematicBundle { collider: Collider::round_cuboid(4.0, 8.0, 1.0), transform: TransformBundle::from_transform(Transform::from_translation(Vec3::new( - // 256.0, 128.0, 0.0, - // DEBUG: - 256.0, 72.996, 0.0, + 256.0, 128.0, 0.0, ))), ..default() }; @@ -86,7 +84,6 @@ pub fn player_spawn(mut commands: Commands) { .insert(KinematicInput::default()) .insert(CameraFollow { priority: 1, - // movement: FollowMovement::Smooth(7.0), - movement: FollowMovement::Instant, + movement: FollowMovement::Smooth(18.0), }); } diff --git a/src/terrain2d.rs b/src/terrain2d.rs index 730a1cc..fc4ade3 100644 --- a/src/terrain2d.rs +++ b/src/terrain2d.rs @@ -3,7 +3,7 @@ use std::collections::{ HashMap, }; -use bevy::{prelude::*, render::camera::RenderTarget}; +use bevy::{input::mouse::MouseWheel, prelude::*, render::camera::RenderTarget}; use bevy_prototype_debug_lines::DebugLines; use bevy_rapier2d::prelude::*; @@ -26,15 +26,13 @@ impl Plugin for Terrain2DPlugin { fn build(&self, app: &mut App) { app.register_type::() .insert_resource(Terrain2D::new()) + .insert_resource(TerrainBrush2D::default()) .add_event::() .add_system(debug_painter) .add_system_to_stage( CoreStage::PostUpdate, dirty_rect_visualizer.before(emit_terrain_events), ) - // DEBUG: - .add_system_to_stage(CoreStage::First, first_log) - .add_system_to_stage(CoreStage::Last, last_log) .add_system_to_stage( CoreStage::PostUpdate, chunk_spawner.before(emit_terrain_events), @@ -51,40 +49,42 @@ impl Plugin for Terrain2DPlugin { } } -// DEBUG: -fn first_log() { - println!("start <"); +#[derive(Resource)] +struct TerrainBrush2D { + pub radius: i32, + pub tile: TexelID, } -// DEBUG: -fn last_log( - chunk_query: Query<(Entity, &TerrainChunk2D)>, - child_query: Query<&Children>, - collider_query: Query<&Collider>, -) { - println!("> end"); - for (entity, chunk) in chunk_query.iter() { - println!("chunk {entity:?} {:?}", chunk.index); - for children in child_query.get(entity).iter() { - for child in children.iter() { - if let Ok(collider) = collider_query.get(*child) { - if let Some(polyline) = collider.as_polyline() { - println!("\tcollider with {:?} points", polyline.indices().len()); - } - } - } - } +impl Default for TerrainBrush2D { + fn default() -> Self { + TerrainBrush2D { radius: 7, tile: 3 } } } +// REM: Dirty and hopefully temporary fn debug_painter( mut terrain: ResMut, mut debug_draw: ResMut, + mut brush: ResMut, windows: Res, - input: Res>, + mouse_input: Res>, + key_input: Res>, + mut mouse_wheel: EventReader, camera_query: Query<(&Camera, &GlobalTransform), With>, ) { - // REM: Dirty and hopefully temporary + let allow_painting = key_input.pressed(KeyCode::LControl); + + // Change brush + for event in mouse_wheel.iter() { + if allow_painting { + brush.radius = (brush.radius + event.y.round() as i32).clamp(1, 128); + } + } + + if !allow_painting { + return; + } + // https://bevy-cheatbook.github.io/cookbook/cursor2world.html#2d-games // get the camera info and transform // assuming there is exactly one main camera entity, so query::single() is OK @@ -117,13 +117,30 @@ fn debug_painter( return; }; + if key_input.just_pressed(KeyCode::Key1) { + brush.tile = 1; + } + if key_input.just_pressed(KeyCode::Key2) { + brush.tile = 2; + } + if key_input.just_pressed(KeyCode::Key3) { + brush.tile = 3; + } + + let colors = vec![ + Color::rgba(1.0, 0.25, 0.25, 1.0), + Color::rgba(0.25, 1.0, 0.25, 1.0), + Color::rgba(0.25, 0.25, 1.0, 1.0), + ]; + let origin = Vector2I::from(world_pos); - let radius: i32 = 7; + let radius = brush.radius; + let color = colors[brush.tile as usize % colors.len()]; let id = match ( - input.pressed(MouseButton::Left), - input.pressed(MouseButton::Right), + mouse_input.pressed(MouseButton::Left), + mouse_input.pressed(MouseButton::Right), ) { - (true, false) => 1, + (true, false) => brush.tile, (_, _) => 0, }; @@ -138,9 +155,10 @@ fn debug_painter( Vec3::from(pos) + Vec3::new(0.45, 0.45, 0.0), Vec3::from(pos) + Vec3::new(0.55, 0.55, 0.0), 0.0, - Color::rgba(1.0, 0.25, 0.25, 1.0), + color, ); - if input.pressed(MouseButton::Left) || input.pressed(MouseButton::Right) { + if mouse_input.pressed(MouseButton::Left) || mouse_input.pressed(MouseButton::Right) + { terrain.set_texel(&pos, id) } } diff --git a/src/terrain2d/chunk2d.rs b/src/terrain2d/chunk2d.rs index a0826f1..e54f700 100644 --- a/src/terrain2d/chunk2d.rs +++ b/src/terrain2d/chunk2d.rs @@ -408,7 +408,6 @@ pub fn chunk_spawner( match terrain_event { TerrainEvent2D::ChunkAdded(chunk_index) => { // Create unique handle for the image - // TODO: recycling image data would be nice let mut image = Image::new( Extent3d { width: Chunk2D::SIZE_X as u32, @@ -499,7 +498,8 @@ pub fn chunk_sprite_sync( let (chunk_index, rect) = match event { TerrainEvent2D::ChunkAdded(chunk_index) => { // The entity should not have the time to react to the event since it was just made - println!("[chunk_sprite_sync -> TerrainEvent2D::ChunkAdded] This probably shouldn't be firing, maybe the chunk was destroyed and immediately created? chunk: {chunk_index:?}"); + // REM: This gets called when new chunk is instantiated with brush + // println!("[chunk_sprite_sync -> TerrainEvent2D::ChunkAdded] This probably shouldn't be firing, maybe the chunk was destroyed and immediately created? chunk: {chunk_index:?}"); (chunk_index, None) } TerrainEvent2D::TexelsUpdated(chunk_index, rect) => (chunk_index, Some(*rect)), @@ -558,7 +558,8 @@ pub fn chunk_collision_sync( let chunk_index = match event { TerrainEvent2D::ChunkAdded(chunk_index) => { // The entity should not have the time to react to the event since it was just made - println!("[chunk_collision_sync -> TerrainEvent2D::ChunkAdded] This probably shouldn't be firing, maybe the chunk was destroyed and immediately created? chunk: {chunk_index:?}"); + // REM: This gets called when new chunk is instantiated with brush + // println!("[chunk_collision_sync -> TerrainEvent2D::ChunkAdded] This probably shouldn't be firing, maybe the chunk was destroyed and immediately created? chunk: {chunk_index:?}"); chunk_index } TerrainEvent2D::TexelsUpdated(chunk_index, _) => chunk_index, @@ -573,31 +574,52 @@ pub fn chunk_collision_sync( } } - for (entity, chunk) in updated_chunks.iter() { - // DEBUG: - println!("update chunk {:?}", chunk.index); - // Remove old colliders + // Kinda messy, partly due do how entity creatin is queued + for (entity, chunk_component) in updated_chunks.iter() { + let chunk = terrain.index_to_chunk(&chunk_component.index).unwrap(); + let new_islands = chunk.create_collision_data(); + + // Create new colliders + if let Ok(children) = child_query.get(*entity) { + // Chunk has children, new ones will be created and old ones components will be removed + for (index, island) in new_islands.iter().enumerate() { + if let Some(child) = children.get(index) { + // Replace collider + commands + .entity(*child) + .insert(Collider::polyline(island.clone(), None)); + } else { + // Create new child + commands.entity(*entity).with_children(|builder| { + builder + .spawn(Collider::polyline(island.clone(), None)) + .insert(TransformBundle::default()) + .insert(Name::new(format!("Island #{}", index))); + }); + } + } + } else { + // Chunk doesn't have a Children component yet + for (index, island) in new_islands.iter().enumerate() { + commands.entity(*entity).with_children(|builder| { + builder + .spawn(Collider::polyline(island.clone(), None)) + .insert(TransformBundle::default()) + .insert(Name::new(format!("Island #{}", index))); + }); + } + }; + + // Remove extra children. + // Leaving them seems to cause weird problems with rapier when re-adding the collider. The collider is ignored until something else is updated. for children in child_query.get(*entity) { - for child in children { + for (index, child) in children.iter().enumerate() { if let Ok(_) = collider_query.get(*child) { - commands.entity(*child).despawn_recursive() + if index >= new_islands.len() { + commands.entity(*child).despawn_recursive(); + } } } } - - let chunk = terrain.index_to_chunk(&chunk.index).unwrap(); - - // Add new colliders - let collision_islands = chunk.create_collision_data(); - commands.entity(*entity).with_children(|builder| { - let mut index = 1; - for island in collision_islands.iter() { - builder - .spawn(Collider::polyline(island.clone(), None)) - .insert(TransformBundle::default()) - .insert(Name::new(format!("Island #{index}"))); - index += 1; - } - }); } }