This commit is contained in:
2024-11-01 18:03:45 +02:00
commit 156be812fc
5 changed files with 4881 additions and 0 deletions

166
src/main.rs Normal file
View File

@@ -0,0 +1,166 @@
use bevy::{prelude::*, utils::HashSet};
use bevy_ecs_ldtk::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_plugins(LdtkPlugin)
.insert_resource(LevelSelection::index(0))
.register_ldtk_entity::<PlayerBundle>("Player")
.init_resource::<LevelWalls>()
.register_ldtk_entity::<GoalBundle>("Goal")
.register_ldtk_int_cell::<WallBundle>(1)
.add_systems(
Update,
(
move_player_from_input,
translate_grid_coords_entities,
cache_wall_locations,
check_goal,
),
)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let mut camera = Camera2dBundle::default();
camera.projection.scale = 0.5;
camera.transform.translation.x += 1280.0 / 4.0;
camera.transform.translation.y += 720.0 / 4.0;
commands.spawn(camera);
commands.spawn(LdtkWorldBundle {
ldtk_handle: asset_server.load("shocked-miguel.ldtk"),
..Default::default()
});
}
#[derive(Default, Component)]
struct Player;
#[derive(Default, Bundle, LdtkEntity)]
struct PlayerBundle {
player: Player,
#[sprite_sheet_bundle]
sprite_bundle: LdtkSpriteSheetBundle,
#[grid_coords]
grid_coords: GridCoords,
}
#[derive(Default, Component)]
struct Goal;
#[derive(Default, Bundle, LdtkEntity)]
struct GoalBundle {
goal: Goal,
#[sprite_sheet_bundle]
sprite_sheet_bundle: LdtkSpriteSheetBundle,
#[grid_coords]
grid_coords: GridCoords,
}
fn move_player_from_input(
mut players: Query<&mut GridCoords, With<Player>>,
input: Res<ButtonInput<KeyCode>>,
level_walls: Res<LevelWalls>,
) {
let movement_direction = if input.just_pressed(KeyCode::KeyW) {
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 player_grid_coords in players.iter_mut() {
let destination = *player_grid_coords + movement_direction;
if !level_walls.in_wall(&destination) {
*player_grid_coords = destination;
}
}
}
const GRID_SIZE: i32 = 16;
fn translate_grid_coords_entities(
mut grid_coords_entities: Query<(&mut Transform, &GridCoords), Changed<GridCoords>>,
) {
for (mut transform, grid_coords) in grid_coords_entities.iter_mut() {
transform.translation =
bevy_ecs_ldtk::utils::grid_coords_to_translation(*grid_coords, IVec2::splat(GRID_SIZE))
.extend(transform.translation.z);
}
}
#[derive(Default, Component)]
struct Wall;
#[derive(Default, Bundle, LdtkIntCell)]
struct WallBundle {
wall: Wall,
}
#[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 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 new_level_walls = LevelWalls {
wall_locations,
level_width: level.px_wid / GRID_SIZE,
level_height: level.px_hei / GRID_SIZE,
};
*level_walls = new_level_walls;
}
}
}
fn check_goal(
level_selection: ResMut<LevelSelection>,
players: Query<&GridCoords, (With<Player>, Changed<GridCoords>)>,
goals: Query<&GridCoords, With<Goal>>,
) {
if players
.iter()
.zip(goals.iter())
.any(|(player_grid_coords, goal_grid_coords)| player_grid_coords == goal_grid_coords)
{
let indices = match level_selection.into_inner() {
LevelSelection::Indices(indices) => indices,
_ => panic!("level selection should always be Indices in this game"),
};
indices.level += 1;
}
}

69
src/player.rs Normal file
View File

@@ -0,0 +1,69 @@
use bevy::prelude::*;
use bevy_ecs_ldtk::prelude::*;
use bevy_rapier2d::dynamics::Velocity;
use crate::{climbing::Climber, inventory::Inventory};
use crate::{colliders::ColliderBundle, ground_detection::GroundDetection};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, Component)]
pub struct Player;
#[derive(Clone, Default, Bundle, LdtkEntity)]
pub struct PlayerBundle {
#[sprite_bundle("player.png")]
pub sprite_bundle: SpriteBundle,
#[from_entity_instance]
pub collider_bundle: ColliderBundle,
pub player: Player,
#[worldly]
pub worldly: Worldly,
pub climber: Climber,
pub ground_detection: GroundDetection,
// Build Items Component manually by using `impl From<&EntityInstance>`
#[from_entity_instance]
items: Inventory,
// The whole EntityInstance can be stored directly as an EntityInstance component
#[from_entity_instance]
entity_instance: EntityInstance,
}
pub fn player_movement(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<(&mut Velocity, &mut Climber, &GroundDetection), With<Player>>,
) {
for (mut velocity, mut climber, ground_detection) in &mut query {
let right = if input.pressed(KeyCode::KeyD) { 1. } else { 0. };
let left = if input.pressed(KeyCode::KeyA) { 1. } else { 0. };
velocity.linvel.x = (right - left) * 200.;
if climber.intersecting_climbables.is_empty() {
climber.climbing = false;
} else if input.just_pressed(KeyCode::KeyW) || input.just_pressed(KeyCode::KeyS) {
climber.climbing = true;
}
if climber.climbing {
let up = if input.pressed(KeyCode::KeyW) { 1. } else { 0. };
let down = if input.pressed(KeyCode::KeyS) { 1. } else { 0. };
velocity.linvel.y = (up - down) * 200.;
}
if input.just_pressed(KeyCode::Space) && (ground_detection.on_ground || climber.climbing) {
velocity.linvel.y = 500.;
climber.climbing = false;
}
}
}
pub struct PlayerPlugin;
impl Plugin for PlayerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, player_movement)
.register_ldtk_entity::<PlayerBundle>("Player");
}
}