generated from 2ndbeam/bevy-template
feat: Layout changes
- Added padlock layout object - Added lockpick item - Added ItemImage component - Tilemap colliders are now creating from the tiles themselves - Removed items from container bundle - Removed colliders from tilemap bundle - Removed setup_player system
This commit is contained in:
parent
5157450ced
commit
42cfd14214
10 changed files with 175 additions and 57 deletions
20
src/inventory/item/lockpick.rs
Normal file
20
src/inventory/item/lockpick.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// TODO: replace with proper sprite
|
||||||
|
const LOCKPICK_SPRITE: &'static str = "sprites/items/choco_bar.png";
|
||||||
|
|
||||||
|
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||||
|
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||||
|
#[require(Item)]
|
||||||
|
pub struct Lockpick;
|
||||||
|
|
||||||
|
pub fn lockpick_bundle(asset_server: &Res<AssetServer>, position: UVec2) -> impl Bundle {
|
||||||
|
let image = asset_server.load(LOCKPICK_SPRITE);
|
||||||
|
(
|
||||||
|
Item::new_positioned(uvec2(1, 1), position),
|
||||||
|
ItemImage(image),
|
||||||
|
Lockpick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -2,14 +2,32 @@ use std::mem::swap;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub mod lockpick;
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Deref, DerefMut, PartialEq, Eq, Default, Clone, Reflect)]
|
||||||
|
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||||
|
pub struct ItemImage(Handle<Image>);
|
||||||
|
|
||||||
|
// TODO: get rid of Option in position, it's no longer needed
|
||||||
#[derive(Component, Clone, Debug, Reflect, PartialEq, Eq)]
|
#[derive(Component, Clone, Debug, Reflect, PartialEq, Eq)]
|
||||||
#[reflect(Component, Clone, Debug, PartialEq)]
|
#[reflect(Component, Clone, Debug, PartialEq)]
|
||||||
|
#[require(ItemImage)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub size: UVec2,
|
pub size: UVec2,
|
||||||
pub position: Option<UVec2>,
|
pub position: Option<UVec2>,
|
||||||
pub rotated: bool,
|
pub rotated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Item {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
size: uvec2(1, 1),
|
||||||
|
position: Some(uvec2(0, 0)),
|
||||||
|
rotated: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
pub fn new(size: UVec2) -> Self {
|
pub fn new(size: UVec2) -> Self {
|
||||||
Self { size, position: None, rotated: false }
|
Self { size, position: None, rotated: false }
|
||||||
|
|
@ -7,7 +7,6 @@ use crate::{
|
||||||
inventory::{
|
inventory::{
|
||||||
ActiveInventory,
|
ActiveInventory,
|
||||||
Inventory,
|
Inventory,
|
||||||
item::Item,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -42,7 +41,6 @@ pub fn container_bundle(
|
||||||
asset_server: &Res<AssetServer>,
|
asset_server: &Res<AssetServer>,
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
inventory_size: UVec2,
|
inventory_size: UVec2,
|
||||||
items: Vec<Item>,
|
|
||||||
) -> impl Bundle {
|
) -> impl Bundle {
|
||||||
let image = asset_server.load(CRATE_CLOSED_ASSET);
|
let image = asset_server.load(CRATE_CLOSED_ASSET);
|
||||||
(
|
(
|
||||||
|
|
@ -51,7 +49,6 @@ pub fn container_bundle(
|
||||||
Sprite::from_image(image),
|
Sprite::from_image(image),
|
||||||
Inventory::new(inventory_size),
|
Inventory::new(inventory_size),
|
||||||
Children::spawn((
|
Children::spawn((
|
||||||
SpawnIter(items.into_iter()),
|
|
||||||
Spawn((
|
Spawn((
|
||||||
Collider::cuboid(meters(1.), meters(1.)),
|
Collider::cuboid(meters(1.), meters(1.)),
|
||||||
Sensor,
|
Sensor,
|
||||||
|
|
|
||||||
66
src/layout/lock.rs
Normal file
66
src/layout/lock.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_rapier2d::prelude::*;
|
||||||
|
|
||||||
|
use crate::{inventory::{Inventory, item::lockpick::Lockpick}, meters, player::Player};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const PADLOCK_IMAGE_PATH: &'static str = "sprites/interactive/padlock.png";
|
||||||
|
|
||||||
|
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||||
|
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||||
|
#[require(InteractiveObject)]
|
||||||
|
pub struct Padlock;
|
||||||
|
|
||||||
|
pub fn on_padlock_interaction(
|
||||||
|
event: On<InteractionEvent>,
|
||||||
|
mut commands: Commands,
|
||||||
|
query: Query<(&ChildOf, Entity), With<Padlock>>,
|
||||||
|
inventory_query: Query<(&Children, &ChildOf), With<Inventory>>,
|
||||||
|
player_query: Query<(), With<Player>>,
|
||||||
|
lockpick_query: Query<(), With<Lockpick>>,
|
||||||
|
) {
|
||||||
|
let Ok((parent, lock_id)) = query.get(event.entity) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// find lockpick in player inventory
|
||||||
|
let mut lockpick_id = None;
|
||||||
|
for (items, inventory_parent) in inventory_query {
|
||||||
|
if player_query.get(inventory_parent.0).is_err() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for item_id in items {
|
||||||
|
if lockpick_query.get(*item_id).is_ok() {
|
||||||
|
lockpick_id = Some(*item_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let Some(lockpick_id) = lockpick_id else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
commands.entity(parent.0).remove::<Locked>();
|
||||||
|
commands.entity(lock_id).despawn();
|
||||||
|
commands.entity(lockpick_id).despawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn padlock_bundle(asset_server: &Res<AssetServer>, facing_left: bool) -> impl Bundle {
|
||||||
|
let image = asset_server.load(PADLOCK_IMAGE_PATH);
|
||||||
|
let sign = if facing_left { -1. } else { 1. };
|
||||||
|
(
|
||||||
|
Padlock,
|
||||||
|
Sprite {
|
||||||
|
image,
|
||||||
|
flip_x: facing_left,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(meters(sign * 0.125), meters(0.), 0.),
|
||||||
|
Children::spawn_one((
|
||||||
|
Transform::from_xyz(meters(sign * 0.1875), 0., 0.),
|
||||||
|
Collider::cuboid(meters(0.1875), meters(1.)),
|
||||||
|
Sensor,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ use bevy::prelude::*;
|
||||||
|
|
||||||
pub mod container;
|
pub mod container;
|
||||||
pub mod door;
|
pub mod door;
|
||||||
|
pub mod lock;
|
||||||
pub mod stairs;
|
pub mod stairs;
|
||||||
pub mod systems;
|
pub mod systems;
|
||||||
pub mod tilemap;
|
pub mod tilemap;
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,16 @@ use bevy::{
|
||||||
ecs::query::QueryFilter,
|
ecs::query::QueryFilter,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
meters,
|
meters,
|
||||||
inventory::item::Item,
|
inventory::item::lockpick::lockpick_bundle,
|
||||||
player::Player,
|
player::{
|
||||||
|
Player,
|
||||||
|
player_bundle,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -61,20 +65,19 @@ pub fn detect_interact_collisions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lock_door(
|
||||||
|
mut commands: Commands,
|
||||||
|
query: Query<&ChildOf, Added<lock::Padlock>>,
|
||||||
|
) {
|
||||||
|
for parent in query {
|
||||||
|
commands.entity(parent.0).insert(Locked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setup_world(
|
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)),
|
|
||||||
];
|
|
||||||
|
|
||||||
// floor 1F
|
// floor 1F
|
||||||
let mut tiles = (0..16).map(|x| {
|
let mut tiles = (0..16).map(|x| {
|
||||||
(0, uvec2(x, 1))
|
(0, uvec2(x, 1))
|
||||||
|
|
@ -102,46 +105,53 @@ pub fn setup_world(
|
||||||
|
|
||||||
// wall connectors
|
// wall connectors
|
||||||
tiles.extend([
|
tiles.extend([
|
||||||
(13, 4), (11, 4), (9, 4),
|
(4, 8), (4, 4), (11, 8), (11, 4),
|
||||||
].iter().map(|(x, y)| {
|
].iter().map(|(x, y)| {
|
||||||
(2, uvec2(*x, *y))
|
(2, uvec2(*x, *y))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let colliders: Vec<(Collider, Vec2)> = vec![
|
commands.spawn(tilemap::tilemap_bundle(&asset_server, uvec2(16, 10), tiles));
|
||||||
// 1F
|
|
||||||
(Collider::cuboid(meters(0.5), meters(1.5)),
|
commands.spawn(door::door_bundle(&asset_server, vec2(meters(-3.5), 0.), false));
|
||||||
vec2(meters(0.5), meters(-4.5))),
|
commands.spawn(door::door_bundle(&asset_server, vec2(meters(-3.5), meters(4.)), false))
|
||||||
(Collider::cuboid(meters(0.5), meters(1.5)),
|
.with_child(lock::padlock_bundle(&asset_server, false));
|
||||||
vec2(meters(15.5), meters(-4.5))),
|
commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), 0.), true))
|
||||||
// 2F
|
.with_child(lock::padlock_bundle(&asset_server, true));
|
||||||
(Collider::cuboid(meters(0.5), meters(1.5)),
|
commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), meters(4.)), true));
|
||||||
vec2(meters(0.5), meters(-0.5))),
|
|
||||||
(Collider::cuboid(meters(0.5), meters(1.5)),
|
|
||||||
vec2(meters(15.5), meters(-0.5))),
|
|
||||||
];
|
|
||||||
|
|
||||||
commands.spawn(door::door_bundle(&asset_server, vec2(meters(1.5), 0.), true));
|
|
||||||
commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), 0.), false));
|
|
||||||
commands.spawn(door::door_bundle(&asset_server, vec2(meters(5.5), 0.), false)).insert(Locked);
|
|
||||||
commands.spawn(container::container_bundle(&asset_server, vec2(meters(-2.), 0.), uvec2(8, 8), items.clone()));
|
|
||||||
commands.spawn(container::container_bundle(&asset_server, vec2(meters(2.), meters(4.)), uvec2(10, 8), items.clone()));
|
|
||||||
commands.spawn(tilemap::tilemap_bundle(&asset_server, uvec2(16, 16), tiles, colliders));
|
|
||||||
commands.spawn(stairs::stairs_bundle(
|
commands.spawn(stairs::stairs_bundle(
|
||||||
&asset_server,
|
&asset_server,
|
||||||
vec2(meters(-5.), 0.),
|
vec2(meters(0.), 0.),
|
||||||
Some(vec2(meters(2.), meters(4.))),
|
Some(vec2(meters(2.), meters(4.))),
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
commands.spawn(stairs::stairs_bundle(
|
commands.spawn(stairs::stairs_bundle(
|
||||||
&asset_server,
|
&asset_server,
|
||||||
vec2(meters(-5.), meters(4.)),
|
vec2(meters(0.), meters(4.)),
|
||||||
Some(vec2(meters(2.), meters(4.))),
|
|
||||||
Some(vec2(meters(-2.), meters(-4.))),
|
|
||||||
));
|
|
||||||
commands.spawn(stairs::stairs_bundle(
|
|
||||||
&asset_server,
|
|
||||||
vec2(meters(-5.), meters(8.)),
|
|
||||||
None,
|
None,
|
||||||
Some(vec2(meters(-2.), meters(-4.))),
|
Some(vec2(meters(-2.), meters(-4.))),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
commands.spawn(container::container_bundle(
|
||||||
|
&asset_server,
|
||||||
|
vec2(meters(-6.), 0.),
|
||||||
|
uvec2(1, 1),
|
||||||
|
));
|
||||||
|
commands.spawn(container::container_bundle(
|
||||||
|
&asset_server,
|
||||||
|
vec2(meters(-6.), meters(4.)),
|
||||||
|
uvec2(1, 1),
|
||||||
|
)).with_child(lockpick_bundle(&asset_server, UVec2::ZERO));
|
||||||
|
commands.spawn(container::container_bundle(
|
||||||
|
&asset_server,
|
||||||
|
vec2(meters(6.), 0.),
|
||||||
|
uvec2(1, 1),
|
||||||
|
));
|
||||||
|
commands.spawn(container::container_bundle(
|
||||||
|
&asset_server,
|
||||||
|
vec2(meters(6.), meters(4.)),
|
||||||
|
uvec2(1, 1),
|
||||||
|
)).with_child(lockpick_bundle(&asset_server, UVec2::ZERO));
|
||||||
|
|
||||||
|
commands.spawn(player_bundle(&asset_server, vec2(meters(-6.), 0.)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ pub fn tilemap_bundle(
|
||||||
asset_server: &Res<AssetServer>,
|
asset_server: &Res<AssetServer>,
|
||||||
size: UVec2,
|
size: UVec2,
|
||||||
tiles: Vec<(u16, UVec2)>,
|
tiles: Vec<(u16, UVec2)>,
|
||||||
colliders: Vec<(Collider, Vec2)>,
|
|
||||||
) -> impl Bundle {
|
) -> impl Bundle {
|
||||||
let tile_data: Vec<Option<TileData>> = (0..size.element_product()).map(|xy| {
|
let tile_data: Vec<Option<TileData>> = (0..size.element_product()).map(|xy| {
|
||||||
if let Some((id, _)) = tiles.iter().find(|(_, pos)| pos.x == xy % size.x && pos.y == xy / size.x) {
|
if let Some((id, _)) = tiles.iter().find(|(_, pos)| pos.x == xy % size.x && pos.y == xy / size.x) {
|
||||||
|
|
@ -47,15 +46,25 @@ pub fn tilemap_bundle(
|
||||||
},
|
},
|
||||||
Transform::from_xyz(0., meters(0.5) * size.y as f32 - meters(3.), 0.),
|
Transform::from_xyz(0., meters(0.5) * size.y as f32 - meters(3.), 0.),
|
||||||
TilemapChunkTileData(tile_data),
|
TilemapChunkTileData(tile_data),
|
||||||
Children::spawn(SpawnIter(colliders.into_iter()
|
Children::spawn(SpawnIter(tiles.into_iter().map(move |(_, pos)| {(
|
||||||
.map(move |(collider, pos)| {(
|
// TODO: optimize colliders
|
||||||
collider.clone(),
|
Collider::cuboid(meters(0.5), meters(0.5)),
|
||||||
Transform::from_xyz(
|
Transform::from_xyz(
|
||||||
pos.x - meters(0.5) * size.x as f32,
|
meters(0.5 + pos.x as f32 - (size.x as f32 * 0.5)),
|
||||||
pos.y,
|
meters(0.5 + pos.y as f32 - (size.y as f32 * 0.5)),
|
||||||
0.
|
0.,
|
||||||
),
|
),
|
||||||
)})
|
)})
|
||||||
)),
|
)),
|
||||||
|
//Children::spawn(SpawnIter(colliders.into_iter()
|
||||||
|
// .map(move |(collider, pos)| {(
|
||||||
|
// collider.clone(),
|
||||||
|
// Transform::from_xyz(
|
||||||
|
// pos.x - meters(0.5) * size.x as f32,
|
||||||
|
// pos.y - meters(0.5) * size.y as f32,
|
||||||
|
// 0.
|
||||||
|
// ),
|
||||||
|
// )})
|
||||||
|
//)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,11 @@ impl Plugin for ExpeditionPlugin {
|
||||||
.add_systems(Startup, (
|
.add_systems(Startup, (
|
||||||
setup_global,
|
setup_global,
|
||||||
layout::systems::setup_world,
|
layout::systems::setup_world,
|
||||||
player::systems::setup_player,
|
|
||||||
))
|
))
|
||||||
.add_systems(Update, (
|
.add_systems(Update, (
|
||||||
insert_entity_name,
|
insert_entity_name,
|
||||||
layout::systems::detect_interact_collisions,
|
layout::systems::detect_interact_collisions,
|
||||||
|
layout::systems::lock_door,
|
||||||
player::systems::handle_input,
|
player::systems::handle_input,
|
||||||
ui::update_window_size,
|
ui::update_window_size,
|
||||||
ui::handle_input,
|
ui::handle_input,
|
||||||
|
|
@ -102,6 +102,7 @@ impl Plugin for ExpeditionPlugin {
|
||||||
.add_observer(ui::inventory::observers::on_ui_rotate)
|
.add_observer(ui::inventory::observers::on_ui_rotate)
|
||||||
.add_observer(layout::container::on_container_interact)
|
.add_observer(layout::container::on_container_interact)
|
||||||
.add_observer(layout::door::on_door_interact)
|
.add_observer(layout::door::on_door_interact)
|
||||||
|
.add_observer(layout::lock::on_padlock_interaction)
|
||||||
.add_observer(layout::stairs::on_stairs_interact);
|
.add_observer(layout::stairs::on_stairs_interact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,18 @@ impl Default for Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn player_bundle(asset_server: &Res<AssetServer>) -> impl Bundle {
|
pub fn player_bundle(asset_server: &Res<AssetServer>, position: Vec2) -> impl Bundle {
|
||||||
let image = asset_server.load("sprites/player/player.png");
|
let image = asset_server.load("sprites/player/player.png");
|
||||||
(
|
(
|
||||||
Player::default(),
|
Player::default(),
|
||||||
Sprite::from_image(image),
|
Sprite::from_image(image),
|
||||||
Transform::from_xyz(0f32, 0f32, 1f32),
|
Transform::from_xyz(position.x, position.y, 1f32),
|
||||||
Action::default_input_map(),
|
Action::default_input_map(),
|
||||||
Name::new("Player"),
|
Name::new("Player"),
|
||||||
RigidBody::KinematicPositionBased,
|
RigidBody::KinematicPositionBased,
|
||||||
KinematicCharacterController::default(),
|
KinematicCharacterController::default(),
|
||||||
ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC,
|
ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_STATIC,
|
||||||
Collider::cuboid(meters(0.5), meters(1.)),
|
Collider::cuboid(meters(0.3), meters(0.9)),
|
||||||
ActiveEvents::COLLISION_EVENTS,
|
ActiveEvents::COLLISION_EVENTS,
|
||||||
Sleeping::disabled(),
|
Sleeping::disabled(),
|
||||||
children![
|
children![
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ use crate::{
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn setup_player(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
||||||
commands.spawn(player_bundle(&asset_server));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_input(
|
pub fn handle_input(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue