Files
shocked-miguel/src/level_structure.rs
LinlyBoi b85a5463a9 WHAT HAPPENED AFTER A LONG STREAM
Player movement :D
Uh..miguel not paralysed
Failed to flip miguel when turning
Please help
2024-11-07 00:00:42 +02:00

163 lines
6.3 KiB
Rust

use bevy::{
prelude::*,
utils::{HashMap, HashSet},
};
use bevy_ecs_ldtk::prelude::*;
use bevy_rapier2d::{
dynamics::RigidBody,
geometry::{Collider, Friction},
};
#[derive(Default, Component)]
pub struct Wall;
#[derive(Default, Bundle, LdtkIntCell)]
pub struct WallBundle {
wall: Wall,
}
pub fn spawn_wall_collision(
mut commands: Commands,
wall_query: Query<(&GridCoords, &Parent), Added<Wall>>,
parent_query: Query<&Parent, Without<Wall>>,
level_query: Query<(Entity, &LevelIid)>,
ldtk_projects: Query<&Handle<LdtkProject>>,
ldtk_project_assets: Res<Assets<LdtkProject>>,
) {
/// Represents a wide wall that is 1 tile tall
/// Used to spawn wall collisions
#[derive(Clone, Eq, PartialEq, Debug, Default, Hash)]
struct Plate {
left: i32,
right: i32,
}
/// A simple rectangle type representing a wall of any size
struct Rect {
left: i32,
right: i32,
top: i32,
bottom: i32,
}
let mut level_to_wall_locations: HashMap<Entity, HashSet<GridCoords>> = HashMap::new();
wall_query.iter().for_each(|(&grid_coords, parent)| {
if let Ok(grandparent) = parent_query.get(parent.get()) {
level_to_wall_locations
.entry(grandparent.get())
.or_default()
.insert(grid_coords);
}
});
if !wall_query.is_empty() {
level_query.iter().for_each(|(level_entity, level_iid)| {
if let Some(level_walls) = level_to_wall_locations.get(&level_entity) {
let ldtk_project = ldtk_project_assets
.get(ldtk_projects.single())
.expect("Project has to be loaded at this point");
let level = ldtk_project
.as_standalone()
.get_loaded_level_by_iid(&level_iid.to_string())
.expect("Spawned level should exist in LDtk project");
let LayerInstance {
c_wid: width,
c_hei: height,
grid_size,
..
} = level.layer_instances()[0];
let mut plate_stack: Vec<Vec<Plate>> = Vec::new();
for y in 0..height {
let mut row_plates: Vec<Plate> = Vec::new();
let mut plate_start = None;
for x in 0..width + 1 {
match (plate_start, level_walls.contains(&GridCoords { x, y })) {
(Some(s), false) => {
row_plates.push(Plate {
left: s,
right: x - 1,
});
plate_start = None;
}
(None, true) => plate_start = Some(x),
_ => (),
}
}
plate_stack.push(row_plates);
}
// combine "plates" into rectangles across multiple rows
let mut rect_builder: HashMap<Plate, Rect> = HashMap::new();
let mut prev_row: Vec<Plate> = Vec::new();
let mut wall_rects: Vec<Rect> = Vec::new();
// an extra empty row so the algorithm "finishes" the rects that touch the top edge
plate_stack.push(Vec::new());
for (y, current_row) in plate_stack.into_iter().enumerate() {
for prev_plate in &prev_row {
if !current_row.contains(prev_plate) {
//remove rect so the same plate starts a new rectangle
if let Some(rect) = rect_builder.remove(prev_plate) {
wall_rects.push(rect);
}
}
}
for plate in &current_row {
rect_builder
.entry(plate.clone())
.and_modify(|e| e.top += 1)
.or_insert(Rect {
bottom: y as i32,
top: y as i32,
left: plate.left,
right: plate.right,
});
}
prev_row = current_row;
}
commands.entity(level_entity).with_children(|level| {
// Spawn colliders for every rectangle..
// Making the collider a child of the level serves two purposes:
// 1. Adjusts the transforms to be relative to the level for free
// 2. the colliders will be despawned automatically when levels unload
for wall_rect in wall_rects {
level
.spawn_empty()
.insert(Collider::cuboid(
(wall_rect.right as f32 - wall_rect.left as f32 + 1.)
* grid_size as f32
/ 2.,
(wall_rect.top as f32 - wall_rect.bottom as f32 + 1.)
* grid_size as f32
/ 2.,
))
.insert(RigidBody::Fixed)
.insert(Friction::new(1.0))
.insert(Transform::from_xyz(
(wall_rect.left + wall_rect.right + 1) as f32 * grid_size as f32
/ 2.,
(wall_rect.bottom + wall_rect.top + 1) as f32 * grid_size as f32
/ 2.,
0.,
//this averages xy so it in center yes )
))
.insert(GlobalTransform::default());
}
});
}
});
}
}
pub struct WallPlugin;
impl Plugin for WallPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, spawn_wall_collision)
.register_ldtk_int_cell::<WallBundle>(1) //dirt
.register_ldtk_int_cell::<WallBundle>(3); //stone
}
}