fix: collision rebuilding
parent
354187caad
commit
d64cff7e90
10
src/game.rs
10
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<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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
for children in child_query.get(*entity) {
|
||||
for child in children {
|
||||
if let Ok(_) = collider_query.get(*child) {
|
||||
commands.entity(*child).despawn_recursive()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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();
|
||||
|
||||
let chunk = terrain.index_to_chunk(&chunk.index).unwrap();
|
||||
|
||||
// Add new colliders
|
||||
let collision_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| {
|
||||
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;
|
||||
}
|
||||
.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 (index, child) in children.iter().enumerate() {
|
||||
if let Ok(_) = collider_query.get(*child) {
|
||||
if index >= new_islands.len() {
|
||||
commands.entity(*child).despawn_recursive();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue