generated from 2ndbeam/bevy-template
feat: lighting system
- Added lamp bundle - Moved door collider to its children - Updated level structure
This commit is contained in:
parent
3cddecf592
commit
08751ff12f
12 changed files with 134 additions and 31 deletions
|
|
@ -3,20 +3,9 @@ use std::collections::HashMap;
|
|||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
LoadingState,
|
||||
meters,
|
||||
item::lockpick::lockpick_bundle,
|
||||
layout::{
|
||||
Level,
|
||||
LevelAssetHandle,
|
||||
asset::structs::*,
|
||||
container::container_bundle,
|
||||
door::door_bundle,
|
||||
lock::padlock_bundle,
|
||||
stairs::stairs_bundle,
|
||||
tilemap::tilemap_bundle
|
||||
},
|
||||
player::player_bundle,
|
||||
LoadingState, item::lockpick::lockpick_bundle, layout::{
|
||||
Level, LevelAssetHandle, asset::structs::*, container::container_bundle, door::door_bundle, light::lamp_bundle, lock::padlock_bundle, stairs::stairs_bundle, tilemap::tilemap_bundle
|
||||
}, meters, player::player_bundle
|
||||
};
|
||||
|
||||
pub mod structs;
|
||||
|
|
@ -107,5 +96,10 @@ pub fn load_level (
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for LampData { pos, intensity, radius } in level.interactive.lamps.iter() {
|
||||
let pos = vec2(meters(pos.x), meters(pos.y - 0.5));
|
||||
parent.spawn(lamp_bundle(&asset_server, pos, *intensity, meters(*radius)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,6 +176,31 @@ impl From<ContainerDataInner> for ContainerData {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn default_intensity() -> f32 { 2. }
|
||||
|
||||
pub(super) fn default_radius() -> f32 { 4. }
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Clone, Reflect)]
|
||||
#[reflect(Debug, Default, Deserialize, Serialize, PartialEq, Clone)]
|
||||
pub(super) struct LampDataInner {
|
||||
#[serde(flatten)]
|
||||
pub pos: Pos,
|
||||
#[serde(default = "default_intensity")]
|
||||
pub intensity: f32,
|
||||
#[serde(default = "default_radius")]
|
||||
pub radius: f32,
|
||||
}
|
||||
|
||||
impl From<LampDataInner> for LampData {
|
||||
fn from(LampDataInner { pos, intensity, radius }: LampDataInner) -> Self {
|
||||
Self {
|
||||
pos: pos.into(),
|
||||
intensity,
|
||||
radius,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Clone, Reflect)]
|
||||
#[reflect(Debug, Default, Deserialize, Serialize, PartialEq, Clone)]
|
||||
pub(super) struct InteractiveInner {
|
||||
|
|
@ -186,15 +211,18 @@ pub(super) struct InteractiveInner {
|
|||
pub stairs: Option<Vec<StairsData>>,
|
||||
#[serde(default)]
|
||||
pub containers: Option<Vec<ContainerData>>,
|
||||
#[serde(default)]
|
||||
pub lamps: Option<Vec<LampData>>,
|
||||
}
|
||||
|
||||
impl From<InteractiveInner> for Interactive {
|
||||
fn from(InteractiveInner { player, doors, stairs, containers }: InteractiveInner) -> Self {
|
||||
fn from(InteractiveInner { player, doors, stairs, containers, lamps }: InteractiveInner) -> Self {
|
||||
Self {
|
||||
player: player.into(),
|
||||
doors: doors.unwrap_or_default(),
|
||||
stairs: stairs.unwrap_or_default(),
|
||||
containers: containers.unwrap_or_default(),
|
||||
lamps: lamps.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ pub struct ContainerData {
|
|||
pub items: Vec<ItemData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Clone, Reflect)]
|
||||
#[reflect(Debug, Default, Deserialize, Serialize, PartialEq, Clone)]
|
||||
#[serde(from = "inner::LampDataInner")]
|
||||
pub struct LampData {
|
||||
pub pos: Vec2,
|
||||
pub intensity: f32,
|
||||
pub radius: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Clone, Reflect)]
|
||||
#[reflect(Debug, Default, Deserialize, Serialize, PartialEq, Clone)]
|
||||
#[serde(from = "inner::InteractiveInner")]
|
||||
|
|
@ -78,6 +87,8 @@ pub struct Interactive {
|
|||
pub stairs: Vec<StairsData>,
|
||||
#[serde(default)]
|
||||
pub containers: Vec<ContainerData>,
|
||||
#[serde(default)]
|
||||
pub lamps: Vec<LampData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, Clone, Deref, DerefMut, Reflect)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_light_2d::prelude::*;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::meters;
|
||||
|
|
@ -13,6 +14,11 @@ const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
|
|||
#[require(Sprite, InteractiveObject)]
|
||||
pub struct Door(pub i8);
|
||||
|
||||
#[derive(Component, Clone, Copy, Reflect, Default, PartialEq, Eq, Debug)]
|
||||
#[reflect(Component, Clone, Default, PartialEq, Debug)]
|
||||
#[require(Collider, LightOccluder2d)]
|
||||
pub struct DoorCollider;
|
||||
|
||||
impl Default for Door {
|
||||
fn default() -> Self {
|
||||
Self(1)
|
||||
|
|
@ -23,26 +29,22 @@ pub 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)>,
|
||||
door_collider_query: Query<Entity, With<DoorCollider>>,
|
||||
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;
|
||||
};
|
||||
let maybe_door_collider = children.iter()
|
||||
.find_map(|id| if let Ok(id) = door_collider_query.get(id) { Some(id) } else { None } );
|
||||
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.) }
|
||||
let (image, translation) = if maybe_door_collider.is_none() { (DOOR_CLOSED_ASSET, 0.) }
|
||||
else { (DOOR_OPENED_ASSET, door.0 as f32 * meters(0.5)) };
|
||||
sprite.image = asset_server.load(image);
|
||||
transform.translation.x = translation;
|
||||
|
|
@ -50,15 +52,20 @@ pub fn on_door_interact(
|
|||
}
|
||||
}
|
||||
|
||||
if was_opened {
|
||||
commands.entity(event.entity).insert(door_collider());
|
||||
if let Some(id) = maybe_door_collider {
|
||||
commands.entity(id).despawn();
|
||||
} else {
|
||||
commands.entity(event.entity).remove::<Collider>();
|
||||
commands.entity(event.entity).with_child(door_collider_bundle());
|
||||
}
|
||||
}
|
||||
|
||||
fn door_collider() -> Collider {
|
||||
Collider::cuboid(meters(0.06125), meters(1.))
|
||||
pub fn door_collider_bundle() -> impl Bundle {
|
||||
let size = vec2(meters(0.06125), meters(1.));
|
||||
(
|
||||
DoorCollider,
|
||||
Collider::cuboid(size.x, size.y),
|
||||
LightOccluder2d { shape: LightOccluder2dShape::Rectangle { half_size: size } },
|
||||
)
|
||||
}
|
||||
|
||||
pub fn door_bundle(asset_server: &Res<AssetServer>, position: Vec2, facing_left: bool) -> impl Bundle {
|
||||
|
|
@ -68,8 +75,8 @@ pub fn door_bundle(asset_server: &Res<AssetServer>, position: Vec2, facing_left:
|
|||
Door(direction),
|
||||
Transform::from_xyz(position.x, position.y, 0.),
|
||||
Name::new(format!("Door ({}, {})", position.x, position.y)),
|
||||
door_collider(),
|
||||
children![
|
||||
door_collider_bundle(),
|
||||
(
|
||||
Collider::cuboid(meters(0.5), meters(1.)),
|
||||
Sensor,
|
||||
|
|
|
|||
26
src/layout/light.rs
Normal file
26
src/layout/light.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_light_2d::prelude::*;
|
||||
|
||||
use crate::meters;
|
||||
|
||||
const LAMP_IMAGE_PATH: &'static str = "sprites/interactive/lamp.png";
|
||||
|
||||
#[derive(Component, Clone, Copy, Default, Reflect, Debug, PartialEq, Eq)]
|
||||
#[reflect(Component, Clone, Default, Debug, PartialEq)]
|
||||
#[require(Transform)]
|
||||
pub struct Lamp;
|
||||
|
||||
pub fn lamp_bundle(asset_server: &Res<AssetServer>, pos: Vec2, intensity: f32, radius: f32) -> impl Bundle {
|
||||
let image = asset_server.load(LAMP_IMAGE_PATH);
|
||||
(
|
||||
SpotLight2d {
|
||||
intensity,
|
||||
radius,
|
||||
source_width: meters(0.5),
|
||||
cast_shadows: true,
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(pos.x, pos.y, 0.),
|
||||
Sprite::from_image(image),
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ use bevy::prelude::*;
|
|||
pub mod asset;
|
||||
pub mod container;
|
||||
pub mod door;
|
||||
pub mod light;
|
||||
pub mod lock;
|
||||
pub mod stairs;
|
||||
pub mod systems;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use bevy::{
|
|||
TilemapChunkTileData,
|
||||
},
|
||||
};
|
||||
use bevy_light_2d::prelude::*;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use crate::meters;
|
||||
|
|
@ -58,10 +59,13 @@ pub fn tilemap_bundle(
|
|||
let mut rect = rect.as_rect();
|
||||
rect.max = vec2(rect.max.x + 1., rect.max.y + 1.);
|
||||
|
||||
let (width, height) = (rect.width(), rect.height());
|
||||
let (width, height) = (meters(rect.width() * 0.5), meters(rect.height() * 0.5));
|
||||
let offset = rect.center();
|
||||
(
|
||||
Collider::cuboid(meters(width * 0.5), meters(height * 0.5)),
|
||||
Collider::cuboid(width, height),
|
||||
LightOccluder2d {
|
||||
shape: LightOccluder2dShape::Rectangle { half_size: vec2(width, height) },
|
||||
},
|
||||
Transform::from_xyz(
|
||||
meters(offset.x - size.x as f32 * 0.5),
|
||||
meters(offset.y - size.y as f32 * 0.5),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use bevy_rapier2d::{
|
|||
prelude::*,
|
||||
rapier::prelude::IntegrationParameters,
|
||||
};
|
||||
use bevy_light_2d::prelude::*;
|
||||
use clap::Parser;
|
||||
|
||||
pub mod input;
|
||||
|
|
@ -60,6 +61,9 @@ pub fn camera_bundle() -> impl Bundle {
|
|||
scale: 1.,
|
||||
..OrthographicProjection::default_2d()
|
||||
}),
|
||||
Light2d {
|
||||
ambient_light: AmbientLight2d { brightness: 0.25, ..default() }
|
||||
},
|
||||
Name::new("Camera2d"),
|
||||
)
|
||||
}
|
||||
|
|
@ -101,6 +105,7 @@ impl Plugin for ExpeditionPlugin {
|
|||
TomlAssetPlugin::<layout::asset::structs::LevelAsset>::new(&["toml"]),
|
||||
input::plugin::InputAssetPlugin::<input::InputAction>::default(),
|
||||
input::plugin::InputAssetPlugin::<input::UiAction>::default(),
|
||||
Light2dPlugin,
|
||||
))
|
||||
.init_state::<LoadingState>()
|
||||
.init_state::<GameState>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue