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 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<Terrain2D>) {
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));
}

View File

@ -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 {

View File

@ -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");
}
}
}

View File

@ -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),
});
}

View File

@ -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::<TerrainChunk2D>()
.insert_resource(Terrain2D::new())
.insert_resource(TerrainBrush2D::default())
.add_event::<TerrainEvent2D>()
.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<Terrain2D>,
mut debug_draw: ResMut<DebugLines>,
mut brush: ResMut<TerrainBrush2D>,
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>>,
) {
// 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)
}
}

View File

@ -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;
}
});
}
}