feat: debug terrain painting
parent
2bdc5182a5
commit
4968e5a802
|
|
@ -13,6 +13,7 @@ pub struct GameCameraPlugin;
|
||||||
impl Plugin for GameCameraPlugin {
|
impl Plugin for GameCameraPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_inspectable::<CameraFollow>()
|
app.register_inspectable::<CameraFollow>()
|
||||||
|
.register_type::<GameCamera>()
|
||||||
.add_startup_system(camera_setup)
|
.add_startup_system(camera_setup)
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, camera_system);
|
.add_system_to_stage(CoreStage::PostUpdate, camera_system);
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +32,10 @@ impl Default for FollowMovement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Component, Reflect, Inspectable)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct GameCamera;
|
||||||
|
|
||||||
#[derive(Default, Component, Reflect, Inspectable)]
|
#[derive(Default, Component, Reflect, Inspectable)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct CameraFollow {
|
pub struct CameraFollow {
|
||||||
|
|
@ -55,6 +60,7 @@ fn camera_setup(mut commands: Commands) {
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
GameCamera,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ use std::collections::{
|
||||||
HashMap,
|
HashMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
render::{camera::RenderTarget, view::window},
|
||||||
|
};
|
||||||
use bevy_prototype_debug_lines::DebugLines;
|
use bevy_prototype_debug_lines::DebugLines;
|
||||||
|
|
||||||
mod chunk2d;
|
mod chunk2d;
|
||||||
|
|
@ -14,7 +17,10 @@ pub use chunk2d::*;
|
||||||
pub use terrain_gen2d::*;
|
pub use terrain_gen2d::*;
|
||||||
pub use texel2d::*;
|
pub use texel2d::*;
|
||||||
|
|
||||||
use crate::util::{math::*, Vector2I};
|
use crate::{
|
||||||
|
game::camera::GameCamera,
|
||||||
|
util::{math::*, Vector2I},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Terrain2DPlugin;
|
pub struct Terrain2DPlugin;
|
||||||
|
|
||||||
|
|
@ -23,6 +29,7 @@ impl Plugin for Terrain2DPlugin {
|
||||||
app.register_type::<TerrainChunk2D>()
|
app.register_type::<TerrainChunk2D>()
|
||||||
.insert_resource(Terrain2D::new())
|
.insert_resource(Terrain2D::new())
|
||||||
.add_event::<TerrainEvent2D>()
|
.add_event::<TerrainEvent2D>()
|
||||||
|
.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),
|
||||||
|
|
@ -34,6 +41,70 @@ impl Plugin for Terrain2DPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn debug_painter(
|
||||||
|
mut terrain: ResMut<Terrain2D>,
|
||||||
|
windows: Res<Windows>,
|
||||||
|
input: Res<Input<MouseButton>>,
|
||||||
|
camera_query: Query<(&Camera, &GlobalTransform), With<GameCamera>>,
|
||||||
|
) {
|
||||||
|
if !input.pressed(MouseButton::Left) && !input.pressed(MouseButton::Right) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REM: Dirty and hopefully temporary
|
||||||
|
// 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
|
||||||
|
let (camera, camera_transform) = camera_query.single();
|
||||||
|
|
||||||
|
// get the window that the camera is displaying to (or the primary window)
|
||||||
|
let window = if let RenderTarget::Window(id) = camera.target {
|
||||||
|
windows.get(id).unwrap()
|
||||||
|
} else {
|
||||||
|
windows.get_primary().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if the cursor is inside the window and get its position
|
||||||
|
let world_pos = if let Some(screen_pos) = window.cursor_position() {
|
||||||
|
// get the size of the window
|
||||||
|
let window_size = Vec2::new(window.width() as f32, window.height() as f32);
|
||||||
|
|
||||||
|
// convert screen position [0..resolution] to ndc [-1..1] (gpu coordinates)
|
||||||
|
let ndc = (screen_pos / window_size) * 2.0 - Vec2::ONE;
|
||||||
|
|
||||||
|
// matrix for undoing the projection and camera transform
|
||||||
|
let ndc_to_world = camera_transform.compute_matrix() * camera.projection_matrix().inverse();
|
||||||
|
|
||||||
|
// use it to convert ndc to world-space coordinates
|
||||||
|
let world_pos = ndc_to_world.project_point3(ndc.extend(-1.0));
|
||||||
|
|
||||||
|
// reduce it to a 2D value
|
||||||
|
world_pos.truncate()
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let origin = Vector2I::from(world_pos);
|
||||||
|
let radius: i32 = 12;
|
||||||
|
let id = match (
|
||||||
|
input.pressed(MouseButton::Left),
|
||||||
|
input.pressed(MouseButton::Right),
|
||||||
|
) {
|
||||||
|
(true, false) => 1,
|
||||||
|
(_, _) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for y in origin.y - (radius - 1)..origin.y + radius {
|
||||||
|
for x in origin.x - (radius - 1)..origin.x + radius {
|
||||||
|
let dx = (x - origin.x).abs();
|
||||||
|
let dy = (y - origin.y).abs();
|
||||||
|
if dx * dx + dy * dy <= (radius - 1) * (radius - 1) {
|
||||||
|
terrain.set_texel(&Vector2I { x, y }, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Visualize dirty rects
|
Visualize dirty rects
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,8 @@ pub fn chunk_collision_sync(
|
||||||
(With<TerrainChunkCollisionSync2D>, Changed<TerrainChunk2D>),
|
(With<TerrainChunkCollisionSync2D>, Changed<TerrainChunk2D>),
|
||||||
>,
|
>,
|
||||||
chunk_query: Query<(Entity, &TerrainChunk2D), With<TerrainChunkCollisionSync2D>>,
|
chunk_query: Query<(Entity, &TerrainChunk2D), With<TerrainChunkCollisionSync2D>>,
|
||||||
child_collider_query: Query<&Children, With<Collider>>,
|
child_query: Query<&Children>,
|
||||||
|
collider_query: Query<&Collider>,
|
||||||
) {
|
) {
|
||||||
let mut updated_chunks = vec![];
|
let mut updated_chunks = vec![];
|
||||||
|
|
||||||
|
|
@ -585,9 +586,11 @@ pub fn chunk_collision_sync(
|
||||||
|
|
||||||
for (entity, chunk) in updated_chunks.iter() {
|
for (entity, chunk) in updated_chunks.iter() {
|
||||||
// Remove old colliders
|
// Remove old colliders
|
||||||
for children in child_collider_query.get(*entity) {
|
for children in child_query.get(*entity) {
|
||||||
for child in children {
|
for child in children {
|
||||||
commands.entity(*child).despawn_recursive()
|
if let Ok(_) = collider_query.get(*child) {
|
||||||
|
commands.entity(*child).despawn_recursive()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,15 @@ impl Vector2I {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Vec2> for Vector2I {
|
||||||
|
fn from(vec: Vec2) -> Self {
|
||||||
|
Self {
|
||||||
|
x: vec.x as i32,
|
||||||
|
y: vec.y as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vector2I> for Vec2 {
|
impl From<Vector2I> for Vec2 {
|
||||||
fn from(vec: Vector2I) -> Self {
|
fn from(vec: Vector2I) -> Self {
|
||||||
Vec2 {
|
Vec2 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue