fix: collision rebuilding

fix/collision-refresh
hheik 2022-12-13 02:43:01 +02:00
parent 354187caad
commit d64cff7e90
6 changed files with 103 additions and 80 deletions

View File

@ -4,7 +4,7 @@ use bevy_prototype_debug_lines::DebugLinesPlugin;
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use crate::{ use crate::{
terrain2d::{Terrain2D, Terrain2DPlugin, TerrainGen2D}, terrain2d::{Chunk2D, Terrain2D, Terrain2DPlugin, TerrainGen2D},
util::Vector2I, util::Vector2I,
}; };
@ -30,7 +30,6 @@ pub fn init() {
.add_plugin(Terrain2DPlugin) .add_plugin(Terrain2DPlugin)
.add_plugin(PlayerPlugin) .add_plugin(PlayerPlugin)
.add_startup_system(setup_debug_terrain) .add_startup_system(setup_debug_terrain)
.add_system(debug_controls)
.run(); .run();
} }
@ -47,11 +46,8 @@ fn debug_controls(
fn setup_debug_terrain(mut commands: Commands, mut terrain: ResMut<Terrain2D>) { fn setup_debug_terrain(mut commands: Commands, mut terrain: ResMut<Terrain2D>) {
let terrain_gen = TerrainGen2D::new(432678); let terrain_gen = TerrainGen2D::new(432678);
// for y in 0..(WORLD_WIDTH / Chunk2D::SIZE_Y as i32) { for y in 0..(WORLD_WIDTH / Chunk2D::SIZE_Y as i32) {
// for x in 0..(WORLD_WIDTH / Chunk2D::SIZE_X as i32) { for x in 0..(WORLD_WIDTH / Chunk2D::SIZE_X as i32) {
// DEBUG:
for y in 1..2 {
for x in 8..9 {
let position = Vector2I { x, y }; let position = Vector2I { x, y };
terrain.add_chunk(position, terrain_gen.gen_chunk(&position)); terrain.add_chunk(position, terrain_gen.gen_chunk(&position));
} }

View File

@ -50,7 +50,7 @@ fn camera_setup(mut commands: Commands) {
projection: OrthographicProjection { projection: OrthographicProjection {
scaling_mode: ScalingMode::FixedHorizontal(WORLD_WIDTH as f32), scaling_mode: ScalingMode::FixedHorizontal(WORLD_WIDTH as f32),
window_origin: WindowOrigin::Center, window_origin: WindowOrigin::Center,
scale: 0.5, scale: 1.0 / 2.0,
..default() ..default()
}, },
camera_2d: Camera2d { camera_2d: Camera2d {

View File

@ -199,7 +199,6 @@ fn kinematic_movement(
} }
if let Some(collider) = collider { if let Some(collider) = collider {
let (_, rot, pos) = global_transform.to_scale_rotation_translation(); let (_, rot, pos) = global_transform.to_scale_rotation_translation();
// println!("{pos}");
let angle = rot.to_euler(EulerRot::YXZ).2; let angle = rot.to_euler(EulerRot::YXZ).2;
let mut shape = collider.clone(); let mut shape = collider.clone();
shape.set_scale(Vec2::ONE * 0.9, 1); shape.set_scale(Vec2::ONE * 0.9, 1);
@ -211,18 +210,9 @@ fn kinematic_movement(
2.0, 2.0,
QueryFilter::new().exclude_collider(entity), 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; kinematic_state.on_ground = true;
// DEBUG:
println!("grounded");
} else { } else {
kinematic_state.on_ground = false; kinematic_state.on_ground = false;
// DEBUG:
println!("no ground");
} }
} }
} }

View File

@ -61,9 +61,7 @@ pub fn player_spawn(mut commands: Commands) {
let kinematic = KinematicBundle { let kinematic = KinematicBundle {
collider: Collider::round_cuboid(4.0, 8.0, 1.0), collider: Collider::round_cuboid(4.0, 8.0, 1.0),
transform: TransformBundle::from_transform(Transform::from_translation(Vec3::new( transform: TransformBundle::from_transform(Transform::from_translation(Vec3::new(
// 256.0, 128.0, 0.0, 256.0, 128.0, 0.0,
// DEBUG:
256.0, 72.996, 0.0,
))), ))),
..default() ..default()
}; };
@ -86,7 +84,6 @@ pub fn player_spawn(mut commands: Commands) {
.insert(KinematicInput::default()) .insert(KinematicInput::default())
.insert(CameraFollow { .insert(CameraFollow {
priority: 1, priority: 1,
// movement: FollowMovement::Smooth(7.0), movement: FollowMovement::Smooth(18.0),
movement: FollowMovement::Instant,
}); });
} }

View File

@ -3,7 +3,7 @@ use std::collections::{
HashMap, HashMap,
}; };
use bevy::{prelude::*, render::camera::RenderTarget}; use bevy::{input::mouse::MouseWheel, prelude::*, render::camera::RenderTarget};
use bevy_prototype_debug_lines::DebugLines; use bevy_prototype_debug_lines::DebugLines;
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
@ -26,15 +26,13 @@ impl Plugin for Terrain2DPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.register_type::<TerrainChunk2D>() app.register_type::<TerrainChunk2D>()
.insert_resource(Terrain2D::new()) .insert_resource(Terrain2D::new())
.insert_resource(TerrainBrush2D::default())
.add_event::<TerrainEvent2D>() .add_event::<TerrainEvent2D>()
.add_system(debug_painter) .add_system(debug_painter)
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
dirty_rect_visualizer.before(emit_terrain_events), 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( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
chunk_spawner.before(emit_terrain_events), chunk_spawner.before(emit_terrain_events),
@ -51,40 +49,42 @@ impl Plugin for Terrain2DPlugin {
} }
} }
// DEBUG: #[derive(Resource)]
fn first_log() { struct TerrainBrush2D {
println!("start <"); pub radius: i32,
pub tile: TexelID,
} }
// DEBUG: impl Default for TerrainBrush2D {
fn last_log( fn default() -> Self {
chunk_query: Query<(Entity, &TerrainChunk2D)>, TerrainBrush2D { radius: 7, tile: 3 }
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());
}
}
}
}
} }
} }
// REM: Dirty and hopefully temporary
fn debug_painter( fn debug_painter(
mut terrain: ResMut<Terrain2D>, mut terrain: ResMut<Terrain2D>,
mut debug_draw: ResMut<DebugLines>, mut debug_draw: ResMut<DebugLines>,
mut brush: ResMut<TerrainBrush2D>,
windows: Res<Windows>, windows: Res<Windows>,
input: Res<Input<MouseButton>>, mouse_input: Res<Input<MouseButton>>,
key_input: Res<Input<KeyCode>>,
mut mouse_wheel: EventReader<MouseWheel>,
camera_query: Query<(&Camera, &GlobalTransform), With<GameCamera>>, camera_query: Query<(&Camera, &GlobalTransform), With<GameCamera>>,
) { ) {
// 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 // https://bevy-cheatbook.github.io/cookbook/cursor2world.html#2d-games
// get the camera info and transform // get the camera info and transform
// assuming there is exactly one main camera entity, so query::single() is OK // assuming there is exactly one main camera entity, so query::single() is OK
@ -117,13 +117,30 @@ fn debug_painter(
return; 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 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 ( let id = match (
input.pressed(MouseButton::Left), mouse_input.pressed(MouseButton::Left),
input.pressed(MouseButton::Right), mouse_input.pressed(MouseButton::Right),
) { ) {
(true, false) => 1, (true, false) => brush.tile,
(_, _) => 0, (_, _) => 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.45, 0.45, 0.0),
Vec3::from(pos) + Vec3::new(0.55, 0.55, 0.0), Vec3::from(pos) + Vec3::new(0.55, 0.55, 0.0),
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) terrain.set_texel(&pos, id)
} }
} }

View File

@ -408,7 +408,6 @@ pub fn chunk_spawner(
match terrain_event { match terrain_event {
TerrainEvent2D::ChunkAdded(chunk_index) => { TerrainEvent2D::ChunkAdded(chunk_index) => {
// Create unique handle for the image // Create unique handle for the image
// TODO: recycling image data would be nice
let mut image = Image::new( let mut image = Image::new(
Extent3d { Extent3d {
width: Chunk2D::SIZE_X as u32, width: Chunk2D::SIZE_X as u32,
@ -499,7 +498,8 @@ pub fn chunk_sprite_sync(
let (chunk_index, rect) = match event { let (chunk_index, rect) = match event {
TerrainEvent2D::ChunkAdded(chunk_index) => { TerrainEvent2D::ChunkAdded(chunk_index) => {
// The entity should not have the time to react to the event since it was just made // 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) (chunk_index, None)
} }
TerrainEvent2D::TexelsUpdated(chunk_index, rect) => (chunk_index, Some(*rect)), TerrainEvent2D::TexelsUpdated(chunk_index, rect) => (chunk_index, Some(*rect)),
@ -558,7 +558,8 @@ pub fn chunk_collision_sync(
let chunk_index = match event { let chunk_index = match event {
TerrainEvent2D::ChunkAdded(chunk_index) => { TerrainEvent2D::ChunkAdded(chunk_index) => {
// The entity should not have the time to react to the event since it was just made // 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 chunk_index
} }
TerrainEvent2D::TexelsUpdated(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() { // Kinda messy, partly due do how entity creatin is queued
// DEBUG: for (entity, chunk_component) in updated_chunks.iter() {
println!("update chunk {:?}", chunk.index); let chunk = terrain.index_to_chunk(&chunk_component.index).unwrap();
// Remove old colliders 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 children in child_query.get(*entity) {
for child in children { for (index, child) in children.iter().enumerate() {
if let Ok(_) = collider_query.get(*child) { 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;
}
});
} }
} }