generated from 2ndbeam/bevy-template
feat: Implemented crates functionality
This commit is contained in:
parent
3dde5a729e
commit
d65ca6fe97
4 changed files with 105 additions and 26 deletions
|
|
@ -3,7 +3,7 @@ use bevy::prelude::*;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect, Default)]
|
||||||
pub struct Inventory {
|
pub struct Inventory {
|
||||||
pub size: UVec2,
|
pub size: UVec2,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::f32::consts::FRAC_PI_2;
|
||||||
|
|
||||||
use bevy::{ecs::relationship::RelatedSpawner, prelude::*, ui_widgets::{ControlOrientation, CoreScrollbarThumb, Scrollbar}};
|
use bevy::{ecs::relationship::RelatedSpawner, prelude::*, ui_widgets::{ControlOrientation, CoreScrollbarThumb, Scrollbar}};
|
||||||
|
|
||||||
use crate::{inventory::{ActiveInventory, Inventory, item::Item}, ui::{UiRoot, UiRotateEvent}};
|
use crate::{inventory::{ActiveInventory, Inventory, item::Item}, player::Player, ui::{UiRoot, UiRotateEvent}};
|
||||||
|
|
||||||
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";
|
||||||
|
|
@ -254,11 +254,13 @@ pub fn on_ui_rotate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui_manager_bundle(children: Vec<Entity>) -> impl Bundle {
|
fn ui_manager_bundle(children: Vec<Entity>, aligned_left: bool) -> impl Bundle {
|
||||||
|
let left = if aligned_left { Val::ZERO } else { percent(50.) };
|
||||||
(
|
(
|
||||||
UiInventoryManager,
|
UiInventoryManager,
|
||||||
Node {
|
Node {
|
||||||
position_type: PositionType::Absolute,
|
position_type: PositionType::Absolute,
|
||||||
|
left,
|
||||||
width: percent(50.),
|
width: percent(50.),
|
||||||
height: percent(100.),
|
height: percent(100.),
|
||||||
scrollbar_width: 8.,
|
scrollbar_width: 8.,
|
||||||
|
|
@ -353,6 +355,7 @@ fn inventory_slot_bundle(x: u32, y: u32, image: Handle<Image>) -> impl Bundle {
|
||||||
should_block_lower: true,
|
should_block_lower: true,
|
||||||
is_hoverable: true,
|
is_hoverable: true,
|
||||||
},
|
},
|
||||||
|
GlobalZIndex(1),
|
||||||
BackgroundColor::DEFAULT,
|
BackgroundColor::DEFAULT,
|
||||||
Name::new(format!("UiInventorySlot({x},{y})")),
|
Name::new(format!("UiInventorySlot({x},{y})")),
|
||||||
)
|
)
|
||||||
|
|
@ -376,7 +379,7 @@ 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(2),
|
||||||
Pickable {
|
Pickable {
|
||||||
should_block_lower: false,
|
should_block_lower: false,
|
||||||
is_hoverable: true,
|
is_hoverable: true,
|
||||||
|
|
@ -391,7 +394,9 @@ fn ui_item_bundle(item: &Item, item_entity: Entity, image: Handle<Image>) -> imp
|
||||||
pub fn setup_ui_inventory(
|
pub fn setup_ui_inventory(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
inventory_query: Query<(Entity, &Inventory, Option<&Children>), With<ActiveInventory>>,
|
inventory_query: Query<(Option<&ChildOf>, Entity, &Inventory, Option<&Children>)>,
|
||||||
|
player_query: Query<(), With<Player>>,
|
||||||
|
active_inventory_query: Query<Entity, With<ActiveInventory>>,
|
||||||
item_query: Query<&Item>,
|
item_query: Query<&Item>,
|
||||||
root_query: Query<Entity, With<UiRoot>>,
|
root_query: Query<Entity, With<UiRoot>>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -401,17 +406,30 @@ pub fn setup_ui_inventory(
|
||||||
};
|
};
|
||||||
let ui_slot_image: Handle<Image> = asset_server.load(UI_SLOT_ASSET_PATH);
|
let ui_slot_image: Handle<Image> = asset_server.load(UI_SLOT_ASSET_PATH);
|
||||||
let temp_item_image: Handle<Image> = asset_server.load(TEMP_ITEM_PATH);
|
let temp_item_image: Handle<Image> = asset_server.load(TEMP_ITEM_PATH);
|
||||||
let mut inventory_ids = Vec::new();
|
let (mut player_inventory_ids, mut active_inventory_ids) = (Vec::new(), Vec::new());
|
||||||
for (inventory_entity, inventory, children) in inventory_query.iter().sort::<Entity>() {
|
for (inventory_parent, inventory_entity, inventory, children) in inventory_query.iter().sort::<Entity>() {
|
||||||
|
let is_player = match inventory_parent {
|
||||||
|
Some(parent) => {
|
||||||
|
if player_query.get(parent.0)
|
||||||
|
.is_ok() { true }
|
||||||
|
else {
|
||||||
|
if active_inventory_query.get(inventory_entity)
|
||||||
|
.is_ok() { false }
|
||||||
|
else { continue; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
if active_inventory_query.get(inventory_entity)
|
||||||
|
.is_ok() { false }
|
||||||
|
else { continue; }
|
||||||
|
},
|
||||||
|
};
|
||||||
let items = match children {
|
let items = match children {
|
||||||
Some(children) => {
|
Some(children) => {
|
||||||
children.iter().filter_map(|item_entity| {
|
children.iter().filter_map(|item_entity| {
|
||||||
match item_query.get(item_entity) {
|
match item_query.get(item_entity) {
|
||||||
Ok(item) => Some((item, item_entity)),
|
Ok(item) => Some((item, item_entity)),
|
||||||
Err(err) => {
|
Err(_) => None,
|
||||||
warn!("Error querying item {item_entity}: {err}");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}).collect::<Vec<(&Item, Entity)>>()
|
}).collect::<Vec<(&Item, Entity)>>()
|
||||||
}
|
}
|
||||||
|
|
@ -437,10 +455,18 @@ pub fn setup_ui_inventory(
|
||||||
}
|
}
|
||||||
} }
|
} }
|
||||||
}).id();
|
}).id();
|
||||||
inventory_ids.push(inventory_id);
|
if is_player {
|
||||||
|
player_inventory_ids.push(inventory_id);
|
||||||
|
} else {
|
||||||
|
active_inventory_ids.push(inventory_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let inventory_manager = commands.spawn(ui_manager_bundle(inventory_ids)).id();
|
let player_inventory_manager = commands.spawn(ui_manager_bundle(player_inventory_ids, true)).id();
|
||||||
commands.entity(root).add_child(inventory_manager);
|
let active_inventory_manager = commands.spawn(ui_manager_bundle(active_inventory_ids, false)).id();
|
||||||
|
for entity in active_inventory_query {
|
||||||
|
commands.entity(entity).remove::<ActiveInventory>();
|
||||||
|
}
|
||||||
|
commands.entity(root).add_children(&[player_inventory_manager, active_inventory_manager]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_ui_inventory(
|
pub fn clear_ui_inventory(
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use bevy::{ecs::query::QueryFilter, prelude::*};
|
use bevy::{ecs::query::QueryFilter, prelude::*};
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
|
|
||||||
use crate::{PIXELS_PER_METER, player::Player};
|
use crate::{GameState, PIXELS_PER_METER, inventory::{ActiveInventory, Inventory, item::Item}, player::Player};
|
||||||
|
|
||||||
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.png";
|
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.png";
|
||||||
const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
|
const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
|
||||||
|
const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct MayInteract;
|
pub struct MayInteract;
|
||||||
|
|
@ -19,6 +20,10 @@ pub struct Wall;
|
||||||
#[require(Sprite, InteractiveObject)]
|
#[require(Sprite, InteractiveObject)]
|
||||||
pub struct Door(pub i8);
|
pub struct Door(pub i8);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[require(Sprite, InteractiveObject, Inventory)]
|
||||||
|
pub struct Crate;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Locked;
|
pub struct Locked;
|
||||||
|
|
||||||
|
|
@ -76,6 +81,42 @@ pub fn detect_interact_collisions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_crate_interact(
|
||||||
|
event: On<InteractionEvent>,
|
||||||
|
mut commands: Commands,
|
||||||
|
locked_query: Query<(), With<Locked>>,
|
||||||
|
crate_query: Query<(), With<Crate>>,
|
||||||
|
mut next_state: ResMut<NextState<GameState>>,
|
||||||
|
) {
|
||||||
|
if locked_query.get(event.entity).is_ok() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if crate_query.get(event.entity).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.entity(event.entity).insert(ActiveInventory);
|
||||||
|
next_state.set(GameState::Inventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn crate_bundle(image: Handle<Image>, position: Vec2, inventory_size: UVec2, items: Vec<Item>) -> impl Bundle {
|
||||||
|
(
|
||||||
|
Crate,
|
||||||
|
Transform::from_xyz(position.x, position.y - PIXELS_PER_METER * 0.5, 0.),
|
||||||
|
Sprite::from_image(image),
|
||||||
|
Inventory::new(inventory_size),
|
||||||
|
Observer::new(on_crate_interact),
|
||||||
|
Children::spawn((
|
||||||
|
SpawnIter(items.into_iter()),
|
||||||
|
Spawn((
|
||||||
|
Collider::cuboid(PIXELS_PER_METER, PIXELS_PER_METER),
|
||||||
|
Sensor,
|
||||||
|
Transform::from_xyz(0., PIXELS_PER_METER * 0.5, 0.),
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn on_door_interact(
|
fn on_door_interact(
|
||||||
event: On<InteractionEvent>,
|
event: On<InteractionEvent>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
@ -92,17 +133,15 @@ fn on_door_interact(
|
||||||
let was_opened = if collider_query.get(event.entity).is_ok() { false }
|
let was_opened = if collider_query.get(event.entity).is_ok() { false }
|
||||||
else if no_collider_query.get(event.entity).is_ok() { true }
|
else if no_collider_query.get(event.entity).is_ok() { true }
|
||||||
else {
|
else {
|
||||||
error!("on_door_interact fired but entity {} isn't door", event.entity);
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Ok((door, children)) = door_query.get(event.entity) else {
|
let Ok((door, children)) = door_query.get(event.entity) else {
|
||||||
error!("on_door_interact fired but entity {} has no children", event.entity);
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
for child in children {
|
for child in children {
|
||||||
if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
|
if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
|
||||||
let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
|
let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
|
||||||
else { (DOOR_OPENED_ASSET, door.0 as f32 * 8.) };
|
else { (DOOR_OPENED_ASSET, door.0 as f32 * PIXELS_PER_METER * 0.5) };
|
||||||
sprite.image = asset_server.load(image);
|
sprite.image = asset_server.load(image);
|
||||||
transform.translation.x = translation;
|
transform.translation.x = translation;
|
||||||
break;
|
break;
|
||||||
|
|
@ -159,9 +198,20 @@ pub fn setup_world(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
|
let items = vec![
|
||||||
|
Item::new_positioned(uvec2(1, 1), uvec2(0, 0)),
|
||||||
|
Item::new_positioned(uvec2(2, 1), uvec2(1, 0)),
|
||||||
|
Item::new_positioned(uvec2(3, 1), uvec2(3, 0)),
|
||||||
|
Item::new_positioned(uvec2(2, 2), uvec2(6, 0)),
|
||||||
|
Item::new_positioned(uvec2(6, 2), uvec2(0, 1)),
|
||||||
|
Item::new_positioned(uvec2(2, 3), uvec2(6, 2)),
|
||||||
|
Item::new_positioned(uvec2(4, 4), uvec2(0, 4)),
|
||||||
|
];
|
||||||
let door_image = asset_server.load(DOOR_CLOSED_ASSET);
|
let door_image = asset_server.load(DOOR_CLOSED_ASSET);
|
||||||
|
let crate_image = asset_server.load(CRATE_CLOSED_ASSET);
|
||||||
commands.spawn(door_bundle(door_image.clone(), vec2(16., 0.), true));
|
commands.spawn(door_bundle(door_image.clone(), vec2(16., 0.), true));
|
||||||
commands.spawn(door_bundle(door_image.clone(), vec2(48., 0.), false));
|
commands.spawn(door_bundle(door_image.clone(), vec2(48., 0.), false));
|
||||||
commands.spawn(door_bundle(door_image.clone(), vec2(80., 0.), false)).insert(Locked);
|
commands.spawn(door_bundle(door_image.clone(), vec2(80., 0.), false)).insert(Locked);
|
||||||
commands.spawn(wall_bundle(vec2(-16., 0.)));
|
commands.spawn(wall_bundle(vec2(-48., 0.)));
|
||||||
|
commands.spawn(crate_bundle(crate_image.clone(), vec2(-32., 0.), uvec2(8, 8), items));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use bevy::prelude::*;
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
use leafwing_input_manager::prelude::*;
|
use leafwing_input_manager::prelude::*;
|
||||||
|
|
||||||
use crate::{GameState, InputAction as Action, PIXELS_PER_METER, inventory::{ActiveInventory, Inventory, item::Item}, layout::{InteractionEvent, MayInteract}};
|
use crate::{GameState, InputAction as Action, PIXELS_PER_METER, inventory::{Inventory, item::Item}, layout::{InteractionEvent, MayInteract}};
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
|
|
@ -14,7 +14,7 @@ fn player_bundle(asset_server: &Res<AssetServer>) -> impl Bundle {
|
||||||
let image = asset_server.load("sprites/player/player.png");
|
let image = asset_server.load("sprites/player/player.png");
|
||||||
(
|
(
|
||||||
Player {
|
Player {
|
||||||
speed: 51.2,
|
speed: PIXELS_PER_METER * 0.8,
|
||||||
},
|
},
|
||||||
Sprite::from_image(image),
|
Sprite::from_image(image),
|
||||||
Transform::from_xyz(0f32, 0f32, 1f32),
|
Transform::from_xyz(0f32, 0f32, 1f32),
|
||||||
|
|
@ -27,9 +27,9 @@ fn player_bundle(asset_server: &Res<AssetServer>) -> impl Bundle {
|
||||||
ActiveEvents::COLLISION_EVENTS,
|
ActiveEvents::COLLISION_EVENTS,
|
||||||
Sleeping::disabled(),
|
Sleeping::disabled(),
|
||||||
children![
|
children![
|
||||||
(Inventory::new(UVec2::new(6, 2)), ActiveInventory),
|
Inventory::new(UVec2::new(6, 2)),
|
||||||
(Inventory::new(UVec2::new(5, 3)), ActiveInventory),
|
Inventory::new(UVec2::new(5, 3)),
|
||||||
(Inventory::new(UVec2::new(4, 4)), ActiveInventory),
|
Inventory::new(UVec2::new(4, 4)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ pub fn handle_input(
|
||||||
sprite.flip_x = direction < 0f32;
|
sprite.flip_x = direction < 0f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if action_state.just_pressed(&Action::Interact) {
|
if action_state.just_released(&Action::Interact) {
|
||||||
let mut action_happened = false;
|
let mut action_happened = false;
|
||||||
for interactable_id in interactables {
|
for interactable_id in interactables {
|
||||||
commands.trigger(InteractionEvent { entity: interactable_id });
|
commands.trigger(InteractionEvent { entity: interactable_id });
|
||||||
|
|
@ -99,13 +99,16 @@ pub fn handle_input(
|
||||||
if !action_happened {
|
if !action_happened {
|
||||||
commands.run_system_cached(try_insert_item);
|
commands.run_system_cached(try_insert_item);
|
||||||
}
|
}
|
||||||
|
action_state.reset(&Action::Interact);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GameState::Inventory => {
|
GameState::Inventory => {
|
||||||
let (_, mut action_state, _, _) = player;
|
let (_, mut action_state, _, _) = player;
|
||||||
if action_state.just_released(&Action::ToggleInventory) {
|
if action_state.just_released(&Action::ToggleInventory)
|
||||||
|
|| action_state.just_released(&Action::Interact) {
|
||||||
next_state.set(GameState::Running);
|
next_state.set(GameState::Running);
|
||||||
action_state.reset(&Action::ToggleInventory);
|
action_state.reset(&Action::ToggleInventory);
|
||||||
|
action_state.reset(&Action::Interact);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue