WHAT HAPPENED AFTER A LONG STREAM
Player movement :D Uh..miguel not paralysed Failed to flip miguel when turning Please help
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
|
||||
use crate::player::Player;
|
||||
|
||||
|
||||
pub struct CameraPlugin;
|
||||
|
||||
impl Plugin for CameraPlugin {
|
||||
|
||||
11
src/goal.rs
11
src/goal.rs
@@ -17,13 +17,16 @@ pub struct GoalBundle {
|
||||
|
||||
pub fn check_goal(
|
||||
level_selection: ResMut<LevelSelection>,
|
||||
players: Query<&GridCoords, (With<Player>, Changed<GridCoords>)>,
|
||||
goals: Query<&GridCoords, With<Goal>>,
|
||||
players: Query<&Transform, (With<Player>, Changed<Transform>)>,
|
||||
goals: Query<&Transform, With<Goal>>,
|
||||
) {
|
||||
if players
|
||||
.iter()
|
||||
.zip(goals.iter())
|
||||
.any(|(player_grid_coords, goal_grid_coords)| player_grid_coords == goal_grid_coords)
|
||||
.any(|(player_grid_coords, goal_grid_coords)| {
|
||||
((player_grid_coords.translation.x - goal_grid_coords.translation.x).abs() <= 3.)
|
||||
&& ((player_grid_coords.translation.y - goal_grid_coords.translation.y).abs() <= 3.)
|
||||
})
|
||||
{
|
||||
let indices = match level_selection.into_inner() {
|
||||
LevelSelection::Indices(indices) => indices,
|
||||
@@ -32,4 +35,4 @@ pub fn check_goal(
|
||||
|
||||
indices.level += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
use bevy::prelude::*;
|
||||
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;
|
||||
@@ -8,3 +15,148 @@ pub struct Wall;
|
||||
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 ¤t_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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::player::Player;
|
||||
use crate::{goal::Goal, player::Player};
|
||||
|
||||
// Yes I used chatgpt for this
|
||||
// sue me, it's a mundane task :P
|
||||
@@ -8,6 +8,7 @@ use crate::player::Player;
|
||||
pub fn log_positions(
|
||||
camera_query: Query<&Transform, With<Camera2d>>,
|
||||
player_query: Query<&Transform, With<Player>>,
|
||||
goals: Query<&Transform, With<Goal>>,
|
||||
) {
|
||||
// Log camera position
|
||||
if let Ok(camera_transform) = camera_query.get_single() {
|
||||
@@ -22,4 +23,9 @@ pub fn log_positions(
|
||||
} else {
|
||||
info!("Player not found or multiple players detected.");
|
||||
}
|
||||
if let Ok(goal_transform) = goals.get_single() {
|
||||
info!("Player Position: {:?}", goal_transform.translation);
|
||||
} else {
|
||||
info!("Player not found or multiple players detected.");
|
||||
}
|
||||
}
|
||||
|
||||
41
src/main.rs
41
src/main.rs
@@ -1,29 +1,49 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
use bevy_rapier2d::plugin::{NoUserData, RapierConfiguration, RapierPhysicsPlugin, TimestepMode};
|
||||
use camera::CameraPlugin;
|
||||
use goal::{check_goal, GoalBundle};
|
||||
use ground_detection::GroundDetectionPlugin;
|
||||
use level_structure::{WallBundle, WallPlugin};
|
||||
use logging::log_positions;
|
||||
use player::PlayerPlugin;
|
||||
use goal::{GoalBundle, check_goal};
|
||||
use level_structure::WallBundle;
|
||||
|
||||
mod camera;
|
||||
mod colliders;
|
||||
mod goal;
|
||||
mod ground_detection;
|
||||
mod level_structure;
|
||||
mod logging;
|
||||
mod player;
|
||||
mod goal;
|
||||
mod level_structure;
|
||||
|
||||
pub const GRID_SIZE: i32 = 16;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
|
||||
.add_plugins(LdtkPlugin)
|
||||
.add_plugins((
|
||||
LdtkPlugin,
|
||||
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0),
|
||||
))
|
||||
.insert_resource(RapierConfiguration {
|
||||
gravity: Vec2::new(0.0, -2000.0),
|
||||
physics_pipeline_active: true,
|
||||
query_pipeline_active: true,
|
||||
timestep_mode: TimestepMode::Variable {
|
||||
max_dt: 1.0 / 60.0,
|
||||
time_scale: 1.0,
|
||||
substeps: 1,
|
||||
},
|
||||
scaled_shape_subdivision: 10,
|
||||
force_update_from_transform_changes: false,
|
||||
})
|
||||
.add_plugins(PlayerPlugin)
|
||||
.add_plugins(WallPlugin)
|
||||
.add_plugins(GroundDetectionPlugin)
|
||||
.insert_resource(LevelSelection::index(0))
|
||||
.register_ldtk_entity::<GoalBundle>("Goal")
|
||||
.register_ldtk_int_cell::<WallBundle>(1)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, camera::camera_fit_inside_current_level)
|
||||
.add_plugins(CameraPlugin)
|
||||
.add_systems(Update, (translate_grid_coords_entities, check_goal))
|
||||
.add_systems(Update, log_positions)
|
||||
@@ -33,14 +53,10 @@ fn main() {
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(LdtkWorldBundle {
|
||||
ldtk_handle: asset_server.load("shocked-miguel.ldtk"),
|
||||
level_set: LEVEL_SET,
|
||||
// transform: Transform::from_xyz(-256., -144., 0.),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn translate_grid_coords_entities(
|
||||
mut grid_coords_entities: Query<(&mut Transform, &GridCoords), Changed<GridCoords>>,
|
||||
) {
|
||||
@@ -50,8 +66,3 @@ fn translate_grid_coords_entities(
|
||||
.extend(transform.translation.z);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Component)]
|
||||
struct Wall;
|
||||
|
||||
|
||||
|
||||
176
src/player.rs
176
src/player.rs
@@ -1,11 +1,28 @@
|
||||
use bevy::{prelude::*, utils::HashSet};
|
||||
use bevy::prelude::*;
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
use bevy_rapier2d::dynamics::Velocity;
|
||||
|
||||
use crate::level_structure::Wall;
|
||||
use crate::{colliders::ColliderBundle, ground_detection::GroundDetection, level_structure::Wall};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
#[derive(Default)]
|
||||
enum Facing {
|
||||
LEFT,
|
||||
#[default]
|
||||
RIGHT,
|
||||
}
|
||||
#[derive(Default, Component)]
|
||||
pub(crate) struct Player;
|
||||
|
||||
pub(crate) struct Player {
|
||||
direction: Facing,
|
||||
}
|
||||
impl Player {
|
||||
fn swap_direction(&mut self) {
|
||||
match self.direction {
|
||||
Facing::RIGHT => self.direction = Facing::LEFT,
|
||||
Facing::LEFT => self.direction = Facing::RIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Default, Bundle, LdtkEntity)]
|
||||
struct PlayerBundle {
|
||||
player: Player,
|
||||
@@ -13,85 +30,112 @@ struct PlayerBundle {
|
||||
sprite_bundle: LdtkSpriteSheetBundle,
|
||||
#[grid_coords]
|
||||
grid_coords: GridCoords,
|
||||
pub ground_detection: GroundDetection,
|
||||
#[from_entity_instance]
|
||||
pub collider_bundle: ColliderBundle,
|
||||
}
|
||||
|
||||
#[derive(Default, Resource)]
|
||||
struct LevelWalls {
|
||||
wall_locations: HashSet<GridCoords>,
|
||||
level_width: i32,
|
||||
level_height: i32,
|
||||
}
|
||||
impl LevelWalls {
|
||||
fn in_wall(&self, grid_coords: &GridCoords) -> bool {
|
||||
grid_coords.x < 0
|
||||
|| grid_coords.y < 0
|
||||
|| grid_coords.x >= self.level_width
|
||||
|| grid_coords.y >= self.level_height
|
||||
|| self.wall_locations.contains(grid_coords)
|
||||
}
|
||||
}
|
||||
// #[derive(Default, Resource)]
|
||||
// struct LevelWalls {
|
||||
// wall_locations: HashSet<GridCoords>,
|
||||
// level_width: i32,
|
||||
// level_height: i32,
|
||||
// }
|
||||
// impl LevelWalls {
|
||||
// fn in_wall(&self, grid_coords: &GridCoords) -> bool {
|
||||
// grid_coords.x < 0
|
||||
// || grid_coords.y < 0
|
||||
// || grid_coords.x >= self.level_width
|
||||
// || grid_coords.y >= self.level_height
|
||||
// || self.wall_locations.contains(grid_coords)
|
||||
// }
|
||||
// }
|
||||
|
||||
fn move_player_from_input(
|
||||
mut players: Query<&mut GridCoords, With<Player>>,
|
||||
fn player_movement(
|
||||
mut query: Query<(&mut Velocity, &GroundDetection, &Player), With<Player>>,
|
||||
input: Res<ButtonInput<KeyCode>>,
|
||||
level_walls: Res<LevelWalls>,
|
||||
) {
|
||||
let movement_direction =
|
||||
if input.just_pressed(KeyCode::KeyW) || input.just_pressed(KeyCode::Space) {
|
||||
GridCoords::new(0, 1)
|
||||
} else if input.just_pressed(KeyCode::KeyA) {
|
||||
GridCoords::new(-1, 0)
|
||||
} else if input.just_pressed(KeyCode::KeyS) {
|
||||
GridCoords::new(0, -1)
|
||||
} else if input.just_pressed(KeyCode::KeyD) {
|
||||
GridCoords::new(1, 0)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
// let movement_direction =
|
||||
// if input.just_pressed(KeyCode::KeyW) || input.just_pressed(KeyCode::Space) {
|
||||
// GridCoords::new(0, 1)
|
||||
// } else if input.just_pressed(KeyCode::KeyA) {
|
||||
// GridCoords::new(-1, 0)
|
||||
// } else if input.just_pressed(KeyCode::KeyS) {
|
||||
// GridCoords::new(0, -1)
|
||||
// } else if input.just_pressed(KeyCode::KeyD) {
|
||||
// GridCoords::new(1, 0)
|
||||
// } else {
|
||||
// return;
|
||||
// };
|
||||
for (mut velocity, ground_detection, player) in &mut query {
|
||||
// let right = if input.pressed(KeyCode::KeyD) { 1. } else { 0. };
|
||||
// let left = if input.pressed(KeyCode::KeyA) { 1. } else { 0. };
|
||||
|
||||
for mut player_grid_coords in players.iter_mut() {
|
||||
let destination = *player_grid_coords + movement_direction;
|
||||
if !level_walls.in_wall(&destination) {
|
||||
*player_grid_coords = destination;
|
||||
let right = if input.pressed(KeyCode::KeyD) {
|
||||
// match player.direction {
|
||||
// Facing::LEFT => player.direction = Facing::RIGHT,
|
||||
// Facing::RIGHT => (),
|
||||
|
||||
// }
|
||||
1.
|
||||
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
let left = if input.pressed(KeyCode::KeyA) {
|
||||
1.
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
velocity.linvel.x = (right - left) * 200.;
|
||||
|
||||
if input.just_pressed(KeyCode::Space) && ground_detection.on_ground {
|
||||
velocity.linvel.y = 500.;
|
||||
}
|
||||
}
|
||||
|
||||
// for mut player_grid_coords in players.iter_mut() {
|
||||
// let destination = *player_grid_coords + movement_direction;
|
||||
// if !level_walls.in_wall(&destination) {
|
||||
// *player_grid_coords = destination;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct PlayerPlugin;
|
||||
|
||||
impl Plugin for PlayerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, (move_player_from_input, cache_wall_locations))
|
||||
.register_ldtk_entity::<PlayerBundle>("Player")
|
||||
.init_resource::<LevelWalls>();
|
||||
app.add_systems(Update, player_movement)
|
||||
.register_ldtk_entity::<PlayerBundle>("Player");
|
||||
}
|
||||
}
|
||||
|
||||
fn cache_wall_locations(
|
||||
mut level_walls: ResMut<LevelWalls>,
|
||||
mut level_events: EventReader<LevelEvent>,
|
||||
walls: Query<&GridCoords, With<Wall>>,
|
||||
ldtk_project_entities: Query<&Handle<LdtkProject>>,
|
||||
ldtk_project_assets: Res<Assets<LdtkProject>>,
|
||||
) {
|
||||
for level_event in level_events.read() {
|
||||
if let LevelEvent::Spawned(level_iid) = level_event {
|
||||
let ldtk_project = ldtk_project_assets
|
||||
.get(ldtk_project_entities.single())
|
||||
.expect("LdtkProject should be loaded when level is spawned");
|
||||
let level = ldtk_project
|
||||
.get_raw_level_by_iid(level_iid.get())
|
||||
.expect("spawned level should exist in project");
|
||||
// fn cache_wall_locations(
|
||||
// mut level_walls: ResMut<LevelWalls>,
|
||||
// mut level_events: EventReader<LevelEvent>,
|
||||
// walls: Query<&GridCoords, With<Wall>>,
|
||||
// ldtk_project_entities: Query<&Handle<LdtkProject>>,
|
||||
// ldtk_project_assets: Res<Assets<LdtkProject>>,
|
||||
// ) {
|
||||
// for level_event in level_events.read() {
|
||||
// if let LevelEvent::Spawned(level_iid) = level_event {
|
||||
// let ldtk_project = ldtk_project_assets
|
||||
// .get(ldtk_project_entities.single())
|
||||
// .expect("LdtkProject should be loaded when level is spawned");
|
||||
// let level = ldtk_project
|
||||
// .get_raw_level_by_iid(level_iid.get())
|
||||
// .expect("spawned level should exist in project");
|
||||
|
||||
let wall_locations = walls.iter().copied().collect();
|
||||
// let wall_locations = walls.iter().copied().collect();
|
||||
|
||||
let new_level_walls = LevelWalls {
|
||||
wall_locations,
|
||||
level_width: level.px_wid / crate::GRID_SIZE,
|
||||
level_height: level.px_hei / crate::GRID_SIZE,
|
||||
};
|
||||
// let new_level_walls = LevelWalls {
|
||||
// wall_locations,
|
||||
// level_width: level.px_wid / crate::GRID_SIZE,
|
||||
// level_height: level.px_hei / crate::GRID_SIZE,
|
||||
// };
|
||||
|
||||
*level_walls = new_level_walls;
|
||||
}
|
||||
}
|
||||
}
|
||||
// *level_walls = new_level_walls;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user