generated from 2ndbeam/bevy-template
refactor!: Split everything into submodules
- Bump version to 0.2.0 - Split every module to systems, observers, plugins, etc - Renamed Crate to Container - Removed Wall component - Removed try_insert_item system - Removed inventory::ui module
This commit is contained in:
parent
3094a8af13
commit
7c386d4128
20 changed files with 830 additions and 772 deletions
|
|
@ -0,0 +1,62 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::{
|
||||
GameState,
|
||||
PIXELS_PER_METER,
|
||||
inventory::{
|
||||
ActiveInventory,
|
||||
Inventory,
|
||||
item::Item,
|
||||
},
|
||||
};
|
||||
|
||||
const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, InteractiveObject, Inventory)]
|
||||
pub struct Container;
|
||||
|
||||
fn on_container_interact(
|
||||
event: On<InteractionEvent>,
|
||||
mut commands: Commands,
|
||||
locked_query: Query<(), With<Locked>>,
|
||||
crate_query: Query<(), With<Container>>,
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn container_bundle(
|
||||
asset_server: &Res<AssetServer>,
|
||||
position: Vec2,
|
||||
inventory_size: UVec2,
|
||||
items: Vec<Item>
|
||||
) -> impl Bundle {
|
||||
let image = asset_server.load(CRATE_CLOSED_ASSET);
|
||||
(
|
||||
Container,
|
||||
Transform::from_xyz(position.x, position.y - PIXELS_PER_METER * 0.5, 0.),
|
||||
Sprite::from_image(image),
|
||||
Inventory::new(inventory_size),
|
||||
Observer::new(on_container_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.),
|
||||
)),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::PIXELS_PER_METER;
|
||||
|
||||
use super::*;
|
||||
|
||||
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.png";
|
||||
const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, InteractiveObject)]
|
||||
pub struct Door(pub i8);
|
||||
|
||||
fn on_door_interact(
|
||||
event: On<InteractionEvent>,
|
||||
mut commands: Commands,
|
||||
locked_query: Query<(), With<Locked>>,
|
||||
collider_query: Query<(), (With<Door>, With<Collider>)>,
|
||||
no_collider_query: Query<(), (With<Door>, Without<Collider>)>,
|
||||
door_query: Query<(&Door, &Children)>,
|
||||
mut sprite_query: Query<(&mut Sprite, &mut Transform)>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
if locked_query.get(event.entity).is_ok() {
|
||||
return;
|
||||
}
|
||||
let was_opened = if collider_query.get(event.entity).is_ok() { false }
|
||||
else if no_collider_query.get(event.entity).is_ok() { true }
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok((door, children)) = door_query.get(event.entity) else {
|
||||
return;
|
||||
};
|
||||
for child in children {
|
||||
if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
|
||||
let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
|
||||
else { (DOOR_OPENED_ASSET, door.0 as f32 * PIXELS_PER_METER * 0.5) };
|
||||
sprite.image = asset_server.load(image);
|
||||
transform.translation.x = translation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if was_opened {
|
||||
commands.entity(event.entity).insert(door_collider());
|
||||
} else {
|
||||
commands.entity(event.entity).remove::<Collider>();
|
||||
}
|
||||
}
|
||||
|
||||
fn door_collider() -> Collider {
|
||||
Collider::cuboid(PIXELS_PER_METER * 0.06125, PIXELS_PER_METER)
|
||||
}
|
||||
|
||||
pub fn door_bundle(asset_server: &Res<AssetServer>, position: Vec2, facing_left: bool) -> impl Bundle {
|
||||
let direction = if facing_left { -1 } else { 1 };
|
||||
let image = asset_server.load(DOOR_CLOSED_ASSET);
|
||||
(
|
||||
Door(direction),
|
||||
Transform::from_xyz(position.x, position.y, 0.),
|
||||
Name::new(format!("Door ({}, {})", position.x, position.y)),
|
||||
door_collider(),
|
||||
Observer::new(on_door_interact),
|
||||
children![
|
||||
(
|
||||
Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER),
|
||||
Sensor,
|
||||
Transform::from_xyz(0., 0., 0.),
|
||||
),
|
||||
(
|
||||
Sprite {
|
||||
image,
|
||||
flip_x: facing_left,
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(0., 0., 0.),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
@ -1,33 +1,15 @@
|
|||
use bevy::{ecs::query::QueryFilter, prelude::*};
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::{GameState, PIXELS_PER_METER, inventory::{ActiveInventory, Inventory, item::Item}, player::Player};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub mod container;
|
||||
pub mod door;
|
||||
pub mod systems;
|
||||
|
||||
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.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)]
|
||||
pub struct MayInteract;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct InteractiveObject;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Wall;
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, InteractiveObject)]
|
||||
pub struct Door(pub i8);
|
||||
|
||||
#[derive(Component)]
|
||||
#[require(Sprite, InteractiveObject, Inventory)]
|
||||
pub struct Crate;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Locked;
|
||||
|
||||
|
|
@ -35,187 +17,3 @@ pub struct Locked;
|
|||
pub struct InteractionEvent {
|
||||
pub entity: Entity,
|
||||
}
|
||||
|
||||
fn get_interactive_id<F: QueryFilter>(
|
||||
tested_id: Entity,
|
||||
interactive_query: Query<(), F>,
|
||||
parent_query: Query<&ChildOf, With<Collider>>,
|
||||
) -> Option<Entity> {
|
||||
if interactive_query.get(tested_id).is_ok() {
|
||||
return Some(tested_id);
|
||||
}
|
||||
let Ok(parent_id) = parent_query.get(tested_id) else {
|
||||
return None;
|
||||
};
|
||||
match interactive_query.get(parent_id.0) {
|
||||
Ok(_) => Some(parent_id.0),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_interact_collisions(
|
||||
mut commands: Commands,
|
||||
mut collision_events: MessageReader<CollisionEvent>,
|
||||
player_query: Query<(), With<Player>>,
|
||||
interactive_query1: Query<(), (With<InteractiveObject>, Without<MayInteract>)>,
|
||||
interactive_query2: Query<(), (With<InteractiveObject>, With<MayInteract>)>,
|
||||
parent_query: Query<&ChildOf, With<Collider>>,
|
||||
) {
|
||||
for collision_event in collision_events.read() {
|
||||
match collision_event {
|
||||
CollisionEvent::Started(first, second, _) => {
|
||||
let Some(interactive_id) = get_interactive_id(*first, interactive_query1, parent_query) else {
|
||||
continue;
|
||||
};
|
||||
if player_query.get(*second).is_err() {
|
||||
continue;
|
||||
}
|
||||
commands.entity(interactive_id).insert(MayInteract);
|
||||
},
|
||||
CollisionEvent::Stopped(first, second, _) => {
|
||||
let Some(interactive_id) = get_interactive_id(*first, interactive_query2, parent_query) else {
|
||||
continue;
|
||||
};
|
||||
if player_query.get(*second).is_err() {
|
||||
continue;
|
||||
}
|
||||
commands.entity(interactive_id).remove::<MayInteract>();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
event: On<InteractionEvent>,
|
||||
mut commands: Commands,
|
||||
locked_query: Query<(), With<Locked>>,
|
||||
collider_query: Query<(), (With<Door>, With<Collider>)>,
|
||||
no_collider_query: Query<(), (With<Door>, Without<Collider>)>,
|
||||
door_query: Query<(&Door, &Children)>,
|
||||
mut sprite_query: Query<(&mut Sprite, &mut Transform)>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
if locked_query.get(event.entity).is_ok() {
|
||||
return;
|
||||
}
|
||||
let was_opened = if collider_query.get(event.entity).is_ok() { false }
|
||||
else if no_collider_query.get(event.entity).is_ok() { true }
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok((door, children)) = door_query.get(event.entity) else {
|
||||
return;
|
||||
};
|
||||
for child in children {
|
||||
if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
|
||||
let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
|
||||
else { (DOOR_OPENED_ASSET, door.0 as f32 * PIXELS_PER_METER * 0.5) };
|
||||
sprite.image = asset_server.load(image);
|
||||
transform.translation.x = translation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if was_opened {
|
||||
commands.entity(event.entity).insert(door_collider());
|
||||
} else {
|
||||
commands.entity(event.entity).remove::<Collider>();
|
||||
}
|
||||
}
|
||||
|
||||
fn door_collider() -> Collider {
|
||||
Collider::cuboid(PIXELS_PER_METER * 0.06125, PIXELS_PER_METER)
|
||||
}
|
||||
|
||||
fn door_bundle(image: Handle<Image>, position: Vec2, facing_left: bool) -> impl Bundle {
|
||||
let direction = if facing_left { -1 } else { 1 };
|
||||
(
|
||||
Door(direction),
|
||||
Transform::from_xyz(position.x, position.y, 0.),
|
||||
Name::new(format!("Door ({}, {})", position.x, position.y)),
|
||||
door_collider(),
|
||||
Observer::new(on_door_interact),
|
||||
children![
|
||||
(
|
||||
Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER),
|
||||
Sensor,
|
||||
Transform::from_xyz(0., 0., 0.),
|
||||
),
|
||||
(
|
||||
Sprite {
|
||||
image,
|
||||
flip_x: facing_left,
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(0., 0., 0.),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
fn wall_bundle(position: Vec2) -> impl Bundle {
|
||||
(
|
||||
Wall,
|
||||
Transform::from_xyz(position.x, position.y, 0.),
|
||||
Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER),
|
||||
Name::new(format!("Wall ({}, {})", position.x, position.y)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn setup_world(
|
||||
mut commands: Commands,
|
||||
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 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(48., 0.), false));
|
||||
commands.spawn(door_bundle(door_image.clone(), vec2(80., 0.), false)).insert(Locked);
|
||||
commands.spawn(wall_bundle(vec2(-48., 0.)));
|
||||
commands.spawn(crate_bundle(crate_image.clone(), vec2(-32., 0.), uvec2(8, 8), items));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
use bevy::{
|
||||
ecs::query::QueryFilter,
|
||||
prelude::*,
|
||||
};
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::{
|
||||
inventory::item::Item,
|
||||
player::Player,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn get_interactive_id<F: QueryFilter>(
|
||||
tested_id: Entity,
|
||||
interactive_query: Query<(), F>,
|
||||
parent_query: Query<&ChildOf, With<Collider>>,
|
||||
) -> Option<Entity> {
|
||||
if interactive_query.get(tested_id).is_ok() {
|
||||
return Some(tested_id);
|
||||
}
|
||||
let Ok(parent_id) = parent_query.get(tested_id) else {
|
||||
return None;
|
||||
};
|
||||
match interactive_query.get(parent_id.0) {
|
||||
Ok(_) => Some(parent_id.0),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_interact_collisions(
|
||||
mut commands: Commands,
|
||||
mut collision_events: MessageReader<CollisionEvent>,
|
||||
player_query: Query<(), With<Player>>,
|
||||
interactive_query1: Query<(), (With<InteractiveObject>, Without<MayInteract>)>,
|
||||
interactive_query2: Query<(), (With<InteractiveObject>, With<MayInteract>)>,
|
||||
parent_query: Query<&ChildOf, With<Collider>>,
|
||||
) {
|
||||
for collision_event in collision_events.read() {
|
||||
match collision_event {
|
||||
CollisionEvent::Started(first, second, _) => {
|
||||
let Some(interactive_id) = get_interactive_id(*first, interactive_query1, parent_query) else {
|
||||
continue;
|
||||
};
|
||||
if player_query.get(*second).is_err() {
|
||||
continue;
|
||||
}
|
||||
commands.entity(interactive_id).insert(MayInteract);
|
||||
},
|
||||
CollisionEvent::Stopped(first, second, _) => {
|
||||
let Some(interactive_id) = get_interactive_id(*first, interactive_query2, parent_query) else {
|
||||
continue;
|
||||
};
|
||||
if player_query.get(*second).is_err() {
|
||||
continue;
|
||||
}
|
||||
commands.entity(interactive_id).remove::<MayInteract>();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_world(
|
||||
mut commands: Commands,
|
||||
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)),
|
||||
];
|
||||
commands.spawn(door::door_bundle(&asset_server, vec2(16., 0.), true));
|
||||
commands.spawn(door::door_bundle(&asset_server, vec2(48., 0.), false));
|
||||
commands.spawn(door::door_bundle(&asset_server, vec2(80., 0.), false)).insert(Locked);
|
||||
commands.spawn(container::container_bundle(&asset_server, vec2(-32., 0.), uvec2(8, 8), items));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue