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 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));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue