From a2ff8f1ebb1deb91542e24cb73a025ef85253390 Mon Sep 17 00:00:00 2001 From: core Date: Mon, 7 Jul 2025 20:17:02 -0400 Subject: [PATCH] feat: drag --- Cargo.lock | 27 +++++++++++ crates/unified/Cargo.toml | 4 +- crates/unified/src/client/key_input.rs | 9 +++- crates/unified/src/client/mod.rs | 10 ++-- .../client/{incoming_parts.rs => parts.rs} | 48 +++++++++++++++++-- crates/unified/src/client_plugins.rs | 5 ++ crates/unified/src/clientevent.rs | 25 ++++++++++ crates/unified/src/ecs.rs | 2 - crates/unified/src/lib.rs | 1 + crates/unified/src/server/gravity.rs | 3 +- crates/unified/src/server/mod.rs | 3 ++ crates/unified/src/server/part_dragging.rs | 41 ++++++++++++++++ crates/unified/src/shared_plugins.rs | 7 ++- 13 files changed, 170 insertions(+), 15 deletions(-) rename crates/unified/src/client/{incoming_parts.rs => parts.rs} (54%) create mode 100644 crates/unified/src/clientevent.rs create mode 100644 crates/unified/src/server/part_dragging.rs diff --git a/Cargo.lock b/Cargo.lock index 70fb7f398344d0ba6da3b15ba076f8ae058542fb..e1081e95c2f7bcfacb7358d18175c3839d182981 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,6 +1011,30 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "bevy_dev_tools" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a3abf7b8e0148ef4f74a4ec63f4efb00a3d62aba361921a5d259410556d5" +dependencies = [ + "bevy_app 0.16.1", + "bevy_asset 0.16.1", + "bevy_color", + "bevy_diagnostic 0.16.1", + "bevy_ecs 0.16.1", + "bevy_input 0.16.1", + "bevy_picking", + "bevy_reflect 0.16.1", + "bevy_render 0.16.1", + "bevy_state", + "bevy_text", + "bevy_time 0.16.1", + "bevy_ui 0.16.1", + "bevy_utils 0.16.1", + "bevy_window 0.16.1", + "tracing", +] + [[package]] name = "bevy_diagnostic" version = "0.13.2" @@ -1427,6 +1451,7 @@ dependencies = [ "bevy_color", "bevy_core_pipeline 0.16.1", "bevy_derive 0.16.1", + "bevy_dev_tools", "bevy_diagnostic 0.16.1", "bevy_ecs 0.16.1", "bevy_gizmos 0.16.1", @@ -2043,11 +2068,13 @@ dependencies = [ "bevy_ecs 0.16.1", "bevy_image", "bevy_math 0.16.1", + "bevy_picking", "bevy_platform", "bevy_reflect 0.16.1", "bevy_render 0.16.1", "bevy_transform 0.16.1", "bevy_utils 0.16.1", + "bevy_window 0.16.1", "bitflags 2.9.1", "bytemuck", "derive_more 1.0.0", diff --git a/crates/unified/Cargo.toml b/crates/unified/Cargo.toml index 9dc9b997274a61275061b92290346f7b1c9cc42b..d58cf11c9695650e83caf382c1b6a4c812b7a365 100644 --- a/crates/unified/Cargo.toml +++ b/crates/unified/Cargo.toml @@ -24,7 +24,9 @@ bevy = { version = "0.16", default-features = false, features = [ "bevy_log", "x11", "wayland", - "multi_threaded" + "multi_threaded", + "bevy_dev_tools", + "bevy_sprite_picking_backend" ] } bevy_rapier2d = { version = "0.30", features = ["serde-serialize", "simd-stable"] } bevy_common_assets = { version = "0.13", features = ["toml"] } diff --git a/crates/unified/src/client/key_input.rs b/crates/unified/src/client/key_input.rs index 76c3a4b20045f7595017fe3ab5380e625ac2240f..dfd1538a69a2284c44de49e5dbec9000c8471bc2 100644 --- a/crates/unified/src/client/key_input.rs +++ b/crates/unified/src/client/key_input.rs @@ -3,6 +3,8 @@ use bevy::{ ecs::{event::EventWriter, system::Res}, input::{ButtonInput, keyboard::KeyCode}, }; +use bevy::dev_tools::picking_debug::DebugPickingMode; +use bevy::log::info; use bevy::prelude::ResMut; use bevy_rapier2d::render::DebugRenderContext; use crate::ecs::ThrustEvent; @@ -14,11 +16,16 @@ pub fn key_input_plugin(app: &mut App) { fn debug_render_keybind( keys: Res>, - mut debug_render: ResMut + mut debug_render: ResMut, + mut picking_debug_mode: ResMut ) { if keys.just_pressed(KeyCode::F3) { debug_render.enabled = !debug_render.enabled; } + if keys.just_pressed(KeyCode::F4) { + *picking_debug_mode = DebugPickingMode::Noisy; + info!("{:?}", picking_debug_mode); + } } diff --git a/crates/unified/src/client/mod.rs b/crates/unified/src/client/mod.rs index 33cc766e367966b473e0d832080301d146bb051e..0ad5df76c9cf6931a0ac7b13646a4912c6a606fd 100644 --- a/crates/unified/src/client/mod.rs +++ b/crates/unified/src/client/mod.rs @@ -1,6 +1,6 @@ mod colors; mod incoming_particles; -mod incoming_parts; +mod parts; mod key_input; mod net; mod starfield; @@ -10,7 +10,7 @@ mod zoom; mod particles; use crate::client::incoming_particles::replicated_particles_plugin; -use crate::client::incoming_parts::incoming_parts_plugin; +use crate::client::parts::parts_plugin; use planet::incoming_planets::incoming_planets_plugin; use crate::client::key_input::key_input_plugin; use crate::client::starfield::starfield_plugin; @@ -21,6 +21,7 @@ use aeronet_websocket::client::WebSocketClient; use bevy::core_pipeline::bloom::Bloom; use bevy::core_pipeline::fxaa::Fxaa; use bevy::core_pipeline::tonemapping::DebandDither; +use bevy::dev_tools::picking_debug::DebugPickingMode; use bevy::prelude::*; use bevy::window::PrimaryWindow; use bevy_replicon::shared::server_entity_map::ServerEntityMap; @@ -50,12 +51,13 @@ impl Plugin for ClientPlugin { .add_systems(Update, find_me) .add_systems(Update, set_config) .add_plugins((incoming_planets_plugin, indicators_plugin)) - .add_plugins(incoming_parts_plugin) + .add_plugins(parts_plugin) .add_plugins(key_input_plugin) .add_plugins(starfield_plugin) .add_plugins(ui_plugin) .add_plugins(replicated_particles_plugin) - .add_plugins(zoom_plugin); + .add_plugins(zoom_plugin) + .insert_resource(DebugPickingMode::Disabled); } } diff --git a/crates/unified/src/client/incoming_parts.rs b/crates/unified/src/client/parts.rs similarity index 54% rename from crates/unified/src/client/incoming_parts.rs rename to crates/unified/src/client/parts.rs index cf3a4b1c8ed86444315f16022f5fce6a6d8f8ea7..7ba19129b13ef2593ee353ffe4295435c8efbdb2 100644 --- a/crates/unified/src/client/incoming_parts.rs +++ b/crates/unified/src/client/parts.rs @@ -1,10 +1,15 @@ -use crate::ecs::Part; +use std::fmt::Debug; +use crate::ecs::{CursorWorldCoordinates, Part}; use bevy::prelude::*; use bevy_rapier2d::dynamics::MassProperties; use bevy_rapier2d::prelude::{AdditionalMassProperties, ReadMassProperties, RigidBody}; +use crate::client::Me; +use crate::clientevent::{PartDragControlEvent, PartDragEvent}; -pub fn incoming_parts_plugin(app: &mut App) { - app.add_systems(Update, (handle_incoming_parts, handle_updated_parts)); +pub fn parts_plugin(app: &mut App) { + app.insert_resource(DragResource(None)); + app.add_systems(Update, (handle_incoming_parts, handle_updated_parts, send_drag)); + app.add_observer(on_part_release); } fn handle_incoming_parts( @@ -25,7 +30,10 @@ fn handle_incoming_parts( principal_inertia: 7.5, })) .insert(ReadMassProperties::default()) - .insert(RigidBody::Dynamic); + .insert(RigidBody::Dynamic) + .insert(Pickable::default()) + .observe(on_part_click); + info!(?new_part, ?new_entity, "prepared new part"); } } @@ -51,3 +59,35 @@ fn handle_updated_parts( info!(?updated_part, ?updated_entity, "updated part"); } } + +#[derive(Resource)] +struct DragResource(Option); + + +fn on_part_click(ev: Trigger>, sprites: Query<&Sprite, Without>, mut drag: ResMut, mut events: EventWriter) { + if ev.button != PointerButton::Primary { return; }; + let Ok(sprite) = sprites.get(ev.target()) else { return; }; + + drag.0 = Some(ev.target()); + + events.write(PartDragControlEvent::Start(ev.target())); + + info!(?sprite, ?ev, "start drag"); +} +fn on_part_release(ev: Trigger>, mut drag: ResMut, mut events: EventWriter) { + if ev.button != PointerButton::Primary { return; }; + + if let Some(e) = drag.0 { + events.write(PartDragControlEvent::Stop(e)); + } + + drag.0 = None; + + info!(?ev, "stop drag"); +} +fn send_drag(drag: ResMut, coords: Res, mut events: EventWriter) { + let Some(dragging) = drag.0 else { return; }; + let Some(coordinates) = coords.0 else { return; }; + + events.write(PartDragEvent(dragging, coordinates)); +} \ No newline at end of file diff --git a/crates/unified/src/client_plugins.rs b/crates/unified/src/client_plugins.rs index 210918e1bcc0101f6e6d3efdc91a1d5e8250debf..fb7851f13012bb970d2d244e825d633121f81da9 100644 --- a/crates/unified/src/client_plugins.rs +++ b/crates/unified/src/client_plugins.rs @@ -3,7 +3,10 @@ use aeronet_replicon::client::AeronetRepliconClientPlugin; use aeronet_websocket::client::WebSocketClientPlugin; use bevy::DefaultPlugins; use bevy::app::{PluginGroup, PluginGroupBuilder}; +use bevy::dev_tools::picking_debug::DebugPickingPlugin; use bevy::log::LogPlugin; +use bevy::prelude::MeshPickingPlugin; +use bevy::sprite::prelude::SpritePickingPlugin; use bevy_enoki::EnokiPlugin; use bevy_rapier2d::prelude::RapierDebugRenderPlugin; use bevy_replicon::RepliconPlugins; @@ -19,6 +22,8 @@ impl PluginGroup for ClientPluginGroup { .add(WebSocketClientPlugin) .add(AeronetRepliconClientPlugin) .add(EnokiPlugin) + .add(MeshPickingPlugin) + .add(DebugPickingPlugin) .add(ClientPlugin { server: self.server, }) diff --git a/crates/unified/src/clientevent.rs b/crates/unified/src/clientevent.rs new file mode 100644 index 0000000000000000000000000000000000000000..062bb3925bc6010d7ad64de65492be5ffd69b820 --- /dev/null +++ b/crates/unified/src/clientevent.rs @@ -0,0 +1,25 @@ +use bevy::ecs::entity::MapEntities; +use bevy::math::Vec2; +use bevy::prelude::{Entity, EntityMapper, Event}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Event, Serialize, Clone)] +pub struct PartDragEvent(pub Entity, pub Vec2); +impl MapEntities for PartDragEvent { + fn map_entities(&mut self, entity_mapper: &mut E) { + self.0 = entity_mapper.get_mapped(self.0); + } +} +#[derive(Debug, Deserialize, Event, Serialize, Clone)] +pub enum PartDragControlEvent { + Start(Entity), + Stop(Entity) +} +impl MapEntities for PartDragControlEvent { + fn map_entities(&mut self, entity_mapper: &mut E) { + match self { + PartDragControlEvent::Start(entity) => *entity = entity_mapper.get_mapped(*entity), + PartDragControlEvent::Stop(entity) => *entity = entity_mapper.get_mapped(*entity), + } + } +} \ No newline at end of file diff --git a/crates/unified/src/ecs.rs b/crates/unified/src/ecs.rs index f4e06c112f40f44b97e3696e58421aa4be594375..3d1f173e4d45c13f079c35683fe12c9b7a485954 100644 --- a/crates/unified/src/ecs.rs +++ b/crates/unified/src/ecs.rs @@ -21,8 +21,6 @@ pub struct StarfieldBack; #[derive(Resource, Default)] pub struct CursorWorldCoordinates(pub Option); -#[derive(Debug, Default, Deserialize, Event, Serialize)] -pub struct SendBallHere(pub Vec2); #[derive(Debug, Deserialize, Event, Serialize)] pub enum ThrustEvent { diff --git a/crates/unified/src/lib.rs b/crates/unified/src/lib.rs index dd7a9431dff4d18f4968c150de3c3cc320601e95..11a7f0b8eaaf916559f7cd9705dc1afadb8ba63a 100644 --- a/crates/unified/src/lib.rs +++ b/crates/unified/src/lib.rs @@ -27,3 +27,4 @@ pub mod server_plugins; pub mod shared_plugins; #[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))] pub mod particle_editor; +pub mod clientevent; diff --git a/crates/unified/src/server/gravity.rs b/crates/unified/src/server/gravity.rs index 7ede414a1f82140a8eaa447b835b2eb2eebdc80f..e0653dc6f6831e06af65c968a12edd003324df32 100644 --- a/crates/unified/src/server/gravity.rs +++ b/crates/unified/src/server/gravity.rs @@ -4,13 +4,14 @@ use crate::server::world_config::WorldConfigResource; use bevy::math::FloatPow; use bevy::prelude::*; use bevy_rapier2d::prelude::*; +use crate::server::part_dragging::BeingDragged; pub fn newtonian_gravity_plugin(app: &mut App) { app.add_systems(Update, update_gravity); } fn update_gravity( - mut part_query: Query<(&Transform, &ReadMassProperties, &mut ExternalForce), With>, + mut part_query: Query<(&Transform, &ReadMassProperties, &mut ExternalForce), (With, Without)>, planet_query: Query<(&Transform, &ReadMassProperties), With>, world_config: Res, ) { diff --git a/crates/unified/src/server/mod.rs b/crates/unified/src/server/mod.rs index 39b4edb15c36fd2a92ea538738b6a10b242515e4..a1ad5ae7ddfe0120a444bd49715df106b267f3bd 100644 --- a/crates/unified/src/server/mod.rs +++ b/crates/unified/src/server/mod.rs @@ -3,6 +3,7 @@ pub mod planets; pub mod player; mod world_config; mod earth_parts; +mod part_dragging; use crate::server::gravity::newtonian_gravity_plugin; use crate::server::planets::planets_plugin; @@ -17,6 +18,7 @@ use bevy::prelude::*; use bevy_replicon::prelude::Replicated; use std::net::SocketAddr; use crate::server::earth_parts::spawn_parts_plugin; +use crate::server::part_dragging::part_dragging_plugin; pub struct ServerPlugin { pub bind: SocketAddr, @@ -44,6 +46,7 @@ impl Plugin for ServerPlugin { .add_plugins(world_config_plugin) .add_plugins(newtonian_gravity_plugin) .add_plugins(player_management_plugin) + .add_plugins(part_dragging_plugin) .add_plugins(spawn_parts_plugin); } } diff --git a/crates/unified/src/server/part_dragging.rs b/crates/unified/src/server/part_dragging.rs new file mode 100644 index 0000000000000000000000000000000000000000..43076d352ced133bd44e81748e5c1e986e3cbd47 --- /dev/null +++ b/crates/unified/src/server/part_dragging.rs @@ -0,0 +1,41 @@ +use bevy::prelude::*; +use bevy_rapier2d::prelude::{Collider, ColliderDisabled}; +use bevy_replicon::prelude::FromClient; +use crate::clientevent::{PartDragControlEvent, PartDragEvent}; +use crate::ecs::Part; + +pub fn part_dragging_plugin(app: &mut App) { + app.add_systems(Update, (handle_dragged_parts, handle_start_stop_drag)); +} + +// todo: attachment + +#[derive(Component)] +pub struct BeingDragged; + +fn handle_dragged_parts(mut transforms: Query<&mut Transform, (With)>, mut events: EventReader>) { + for event in events.read() { + let Ok(mut transform) = transforms.get_mut(event.0) else { continue; }; + transform.translation = (event.1, 0.0).into(); + } +} +fn handle_start_stop_drag(mut colliders: Query<&Collider, With>, mut events: EventReader>, mut commands: Commands) { + for event in events.read() { + let entity = match &event.event { + PartDragControlEvent::Start(e) => *e, + PartDragControlEvent::Stop(e) => *e, + }; + let Ok(_) = colliders.get(entity) else { continue; }; + match &event.event { + PartDragControlEvent::Start(_) => { + commands.entity(entity) + .insert(BeingDragged) + .insert(ColliderDisabled); + }, + PartDragControlEvent::Stop(_) => { + commands.entity(entity).remove::() + .remove::(); + } + } + } +} \ No newline at end of file diff --git a/crates/unified/src/shared_plugins.rs b/crates/unified/src/shared_plugins.rs index 480649010325e15a608ee1905a142eb5a30781d3..017fa2ef781622efa6bae0789a1f2f6d008ccfec 100644 --- a/crates/unified/src/shared_plugins.rs +++ b/crates/unified/src/shared_plugins.rs @@ -1,9 +1,10 @@ use crate::config::planet::Planet; -use crate::ecs::{Ball, Ground, Part, Particles, Player, SendBallHere, ThrustEvent}; +use crate::ecs::{Ball, Ground, Part, Particles, Player, ThrustEvent}; use bevy::app::{App, PluginGroup, PluginGroupBuilder}; use bevy::prelude::*; use bevy_rapier2d::prelude::*; use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt}; +use crate::clientevent::{PartDragControlEvent, PartDragEvent}; pub struct SharedPluginGroup; @@ -17,8 +18,10 @@ impl PluginGroup for SharedPluginGroup { } pub fn register_everything(app: &mut App) { - app.add_client_event::(Channel::Ordered) + app .add_client_event::(Channel::Ordered) + .add_mapped_client_event::(Channel::Unreliable) + .add_mapped_client_event::(Channel::Ordered) .replicate::() .replicate::() .replicate::()