feat: unfinished item drag and drop

This commit is contained in:
Alexey 2026-03-11 17:29:27 +03:00
commit 0add3e4c20
9 changed files with 191 additions and 15 deletions

133
Cargo.lock generated
View file

@ -273,6 +273,24 @@ dependencies = [
"futures-lite", "futures-lite",
] ]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix 1.1.3",
"slab",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "async-lock" name = "async-lock"
version = "3.4.2" version = "3.4.2"
@ -921,6 +939,7 @@ dependencies = [
"bevy_post_process", "bevy_post_process",
"bevy_ptr", "bevy_ptr",
"bevy_reflect", "bevy_reflect",
"bevy_remote",
"bevy_render", "bevy_render",
"bevy_scene", "bevy_scene",
"bevy_shader", "bevy_shader",
@ -1201,6 +1220,31 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "bevy_remote"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0129e24bf3e281dd52996a9290c35f8c4821ca09e1ce8b8b222671e1ad9da0c"
dependencies = [
"anyhow",
"async-channel",
"async-io",
"bevy_app",
"bevy_asset",
"bevy_derive",
"bevy_ecs",
"bevy_log",
"bevy_platform",
"bevy_reflect",
"bevy_tasks",
"bevy_utils",
"http-body-util",
"hyper",
"serde",
"serde_json",
"smol-hyper",
]
[[package]] [[package]]
name = "bevy_render" name = "bevy_render"
version = "0.18.0" version = "0.18.0"
@ -1394,6 +1438,7 @@ checksum = "990ffedd374dd2c4fe8f0fd4bcefd5617d1ee59164b6c3fcc356a69b48e26e8e"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"async-executor", "async-executor",
"async-io",
"async-task", "async-task",
"atomic-waker", "atomic-waker",
"bevy_platform", "bevy_platform",
@ -2816,6 +2861,72 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
dependencies = [
"atomic-waker",
"bytes",
"futures-channel",
"futures-core",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
"smallvec",
"tokio",
]
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.9" version = "0.25.9"
@ -4367,6 +4478,19 @@ dependencies = [
"xkeysym", "xkeysym",
] ]
[[package]]
name = "smol-hyper"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7428a49d323867702cd12b97b08a6b0104f39ec13b49117911f101271321bc1a"
dependencies = [
"async-executor",
"async-io",
"futures-io",
"hyper",
"pin-project-lite",
]
[[package]] [[package]]
name = "smol_str" name = "smol_str"
version = "0.2.2" version = "0.2.2"
@ -4582,6 +4706,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"pin-project-lite",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.9.12+spec-1.1.0" version = "0.9.12+spec-1.1.0"

View file

@ -6,7 +6,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
bevy = { version = "0.18.0", features = ["debug"] } bevy = { version = "0.18.0", features = ["bevy_remote", "debug"] }
bevy_common_assets = { version = "0.15.0", features = ["toml"] } bevy_common_assets = { version = "0.15.0", features = ["toml"] }
bevy_input = { version = "0.18.0", features = ["serde", "serialize"] } bevy_input = { version = "0.18.0", features = ["serde", "serialize"] }
leafwing-input-manager = "0.20.0" leafwing-input-manager = "0.20.0"

View file

@ -2,7 +2,7 @@ use std::mem::swap;
use bevy::prelude::*; use bevy::prelude::*;
#[derive(Component, Clone, Debug)] #[derive(Component, Clone, Debug, Reflect)]
pub struct Item { pub struct Item {
pub size: UVec2, pub size: UVec2,
pub position: Option<UVec2>, pub position: Option<UVec2>,

View file

@ -3,13 +3,13 @@ use bevy::prelude::*;
pub mod item; pub mod item;
pub mod ui; pub mod ui;
#[derive(Component)] #[derive(Component, Reflect)]
pub struct Inventory { pub struct Inventory {
pub size: UVec2, pub size: UVec2,
} }
/// Marker that this inventory will show up when UI is built /// Marker that this inventory will show up when UI is built
#[derive(Component)] #[derive(Component, Reflect)]
pub struct ActiveInventory; pub struct ActiveInventory;
impl Inventory { impl Inventory {

View file

@ -1,25 +1,45 @@
use std::f32::consts::FRAC_PI_2; use std::f32::consts::FRAC_PI_2;
use bevy::prelude::*; use bevy::{ecs::component::{ComponentId, ComponentIdFor}, prelude::*};
use crate::{inventory::{ActiveInventory, Inventory, item::Item}, ui::{UiRoot, WindowSize}}; use crate::{inventory::{ActiveInventory, Inventory, item::Item}, ui::{UiRoot, WindowSize}};
const UI_SLOT_ASSET_PATH: &'static str = "sprites/ui/inventory_slot.png"; const UI_SLOT_ASSET_PATH: &'static str = "sprites/ui/inventory_slot.png";
const TEMP_ITEM_PATH: &'static str = "sprites/items/choco_bar.png"; const TEMP_ITEM_PATH: &'static str = "sprites/items/choco_bar.png";
#[derive(Component)] #[derive(Component, Reflect)]
#[require(Node)] #[require(Node)]
pub struct UiInventory; pub struct UiInventory;
#[derive(Component)] #[derive(Component, Reflect)]
#[require(Node, ImageNode)] #[require(Node, ImageNode)]
pub struct UiInventorySlot(UVec2); pub struct UiInventorySlot(UVec2);
#[derive(Component)] #[derive(Component, Reflect)]
#[require(Node, ImageNode)] #[require(Node, ImageNode)]
pub struct UiItem(Entity); pub struct UiItem(Entity);
fn ui_inventory_bundle(inventory: &Inventory, window_size: &Res<WindowSize>) -> impl Bundle { fn on_item_drag_drop(
event: On<Pointer<DragDrop>>,
ui_item_query: Query<&UiItem>,
slot_query: Query<(&UiInventorySlot, Option<&Children>)>,
) {
info!("dragdrop {} {}", event.dropped, event.event_target());
let Ok(ui_item) = ui_item_query.get(event.dropped) else {
return;
};
let Ok((slot, _)) = slot_query.get(event.event_target()) else {
return;
};
info!("Item {:?} dropped on {:?}", ui_item.0, slot.0);
}
fn ui_inventory_bundle(
inventory: &Inventory,
window_size: &Res<WindowSize>,
_slot_id: ComponentId,
) -> impl Bundle {
let window_ratio = window_size.aspect_ratio(); let window_ratio = window_size.aspect_ratio();
let (width, height) = { let (width, height) = {
if window_ratio >= 1. { if window_ratio >= 1. {
@ -41,6 +61,10 @@ fn ui_inventory_bundle(inventory: &Inventory, window_size: &Res<WindowSize>) ->
grid_auto_rows: vec![GridTrack::percent(100. / inventory.size.y as f32)], grid_auto_rows: vec![GridTrack::percent(100. / inventory.size.y as f32)],
..default() ..default()
}, },
Pickable::IGNORE,
GlobalZIndex::default(),
Observer::new(on_item_drag_drop)
// .with_component(slot_id),
) )
} }
@ -60,6 +84,10 @@ fn inventory_slot_bundle(x: u32, y: u32, image: Handle<Image>) -> impl Bundle {
align_items: AlignItems::Start, align_items: AlignItems::Start,
..default() ..default()
}, },
Pickable {
should_block_lower: true,
is_hoverable: true,
},
) )
} }
@ -97,6 +125,10 @@ fn ui_item_bundle(item: &Item, item_entity: Entity, image: Handle<Image>) -> imp
BackgroundColor(Color::hsla(0., 0., 0., 0.5)), BackgroundColor(Color::hsla(0., 0., 0., 0.5)),
ui_transform, ui_transform,
GlobalZIndex(1), GlobalZIndex(1),
Pickable {
should_block_lower: false,
is_hoverable: true,
},
) )
} }
@ -106,6 +138,7 @@ pub fn setup_ui_inventory(
inventory_query: Query<(&Inventory, Option<&Children>), With<ActiveInventory>>, inventory_query: Query<(&Inventory, Option<&Children>), With<ActiveInventory>>,
item_query: Query<&Item>, item_query: Query<&Item>,
root_query: Query<Entity, With<UiRoot>>, root_query: Query<Entity, With<UiRoot>>,
slot_id: ComponentIdFor<UiInventorySlot>,
window_size: Res<WindowSize>, window_size: Res<WindowSize>,
) { ) {
let Ok(root) = root_query.single() else { let Ok(root) = root_query.single() else {
@ -129,7 +162,7 @@ pub fn setup_ui_inventory(
} }
None => Vec::new(), None => Vec::new(),
}; };
let inventory_entity = commands.spawn(ui_inventory_bundle(inventory, &window_size)) let inventory_entity = commands.spawn(ui_inventory_bundle(inventory, &window_size, slot_id.get()))
.with_children(|commands| { .with_children(|commands| {
for x in 0..inventory.size.x { for y in 0..inventory.size.y { for x in 0..inventory.size.x { for y in 0..inventory.size.y {
let mut slot_commands = commands.spawn(inventory_slot_bundle(x, y, ui_slot_image.clone())); let mut slot_commands = commands.spawn(inventory_slot_bundle(x, y, ui_slot_image.clone()));
@ -139,7 +172,8 @@ pub fn setup_ui_inventory(
} }
} } } }
}).id(); }).id();
commands.entity(root).add_child(inventory_entity); commands.entity(root)
.add_child(inventory_entity);
// for simplicity we'll show only first inventory // for simplicity we'll show only first inventory
break; break;

View file

@ -73,6 +73,12 @@ impl Plugin for ExpeditionPlugin {
ui::update_window_size, ui::update_window_size,
)) ))
.add_systems(OnEnter(GameState::Inventory), inventory::ui::setup_ui_inventory) .add_systems(OnEnter(GameState::Inventory), inventory::ui::setup_ui_inventory)
.add_systems(OnExit(GameState::Inventory), inventory::ui::clear_ui_inventory); .add_systems(OnExit(GameState::Inventory), inventory::ui::clear_ui_inventory)
.register_type::<inventory::Inventory>()
.register_type::<inventory::ActiveInventory>()
.register_type::<inventory::item::Item>()
.register_type::<inventory::ui::UiItem>()
.register_type::<inventory::ui::UiInventorySlot>()
.register_type::<inventory::ui::UiInventory>();
} }
} }

View file

@ -1,4 +1,4 @@
use bevy::prelude::*; use bevy::{prelude::*, remote::{RemotePlugin, http::RemoteHttpPlugin}};
use expedition_demo::ExpeditionPlugin; use expedition_demo::ExpeditionPlugin;
@ -6,5 +6,7 @@ fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_plugins(ExpeditionPlugin) .add_plugins(ExpeditionPlugin)
.add_plugins(RemotePlugin::default())
.add_plugins(RemoteHttpPlugin::default())
.run(); .run();
} }

View file

@ -3,7 +3,7 @@ use leafwing_input_manager::prelude::*;
use crate::{GameState, InputAction as Action, inventory::{ActiveInventory, Inventory, item::Item}}; use crate::{GameState, InputAction as Action, inventory::{ActiveInventory, Inventory, item::Item}};
#[derive(Component)] #[derive(Component, Reflect)]
pub struct Player { pub struct Player {
// px/s // px/s
speed: f32, speed: f32,

View file

@ -1,6 +1,6 @@
use bevy::{prelude::*, window::WindowResized}; use bevy::{prelude::*, window::WindowResized};
#[derive(Component)] #[derive(Component, Reflect)]
#[require(Node)] #[require(Node)]
pub struct UiRoot; pub struct UiRoot;
@ -24,6 +24,7 @@ impl UiRoot {
justify_self: JustifySelf::Center, justify_self: JustifySelf::Center,
..default() ..default()
}, },
Pickable::IGNORE,
) )
} }
} }