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:
2024-11-07 00:00:42 +02:00
parent fac0e40de5
commit b85a5463a9
9 changed files with 410 additions and 120 deletions

View File

@@ -1,9 +1,7 @@
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
use crate::player::Player;
pub struct CameraPlugin;
impl Plugin for CameraPlugin {

View File

@@ -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;
}
}
}

View File

@@ -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 &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
}
}

View File

@@ -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.");
}
}

View File

@@ -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;

View File

@@ -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;
// }
// }
// }