Merge branch 'camera-fuckery'
This commit is contained in:
@@ -69,33 +69,57 @@ pub fn camera_fit_inside_current_level(
|
||||
}
|
||||
}
|
||||
}
|
||||
const CAMERA_DECAY_RATE: f32 = 2.;
|
||||
fn update_camera(
|
||||
mut camera_query: Query<
|
||||
(
|
||||
&mut bevy::render::camera::OrthographicProjection,
|
||||
&mut Transform,
|
||||
),
|
||||
Without<Player>,
|
||||
>,
|
||||
player_query: Query<&Transform, With<Player>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
|
||||
if let Ok(Transform {
|
||||
translation: player_translation,
|
||||
..
|
||||
}) = player_query.get_single()
|
||||
{
|
||||
let player_translation = *player_translation;
|
||||
use crate::player::Player;
|
||||
|
||||
let (mut orthographic_projection, mut camera_transform) = camera_query.single_mut();
|
||||
let Vec3 { x, y, .. } = player_translation;
|
||||
let direction = Vec3::new(x, y, camera_transform.translation.z);
|
||||
pub struct CameraPlugin;
|
||||
|
||||
// Applies a smooth effect to camera movement using stable interpolation
|
||||
// between the camera position and the player position on the x and y axes.
|
||||
camera_transform
|
||||
.translation += direction;
|
||||
impl Plugin for CameraPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
// Spawn in the camera on startup, only 1 instance
|
||||
app.add_systems(Startup, spawn_camera);
|
||||
|
||||
// We're so good..
|
||||
// Update the camera position ever game loop iteration
|
||||
app.add_systems(Update, move_camera);
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_camera(mut commands: Commands) {
|
||||
let mut camera = Camera2dBundle::default();
|
||||
// Bigger decimal, further it zooms in
|
||||
// 0.5 is closer to level than 1.0
|
||||
camera.projection.scale = 0.3;
|
||||
|
||||
commands.spawn(camera);
|
||||
}
|
||||
|
||||
// THIS NEEDS TO BE IN THE UPDATE LOOP LOL
|
||||
// We don't need to get player position to snap
|
||||
// when spawning, it does that here lol
|
||||
// - Mjork
|
||||
fn move_camera(
|
||||
mut camera: Query<&mut Transform, (With<Camera2d>, Without<Player>)>,
|
||||
player: Query<&Transform, (With<Player>, Without<Camera2d>)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
// Get the only instance of camera
|
||||
let Ok(mut camera) = camera.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Get the only instance of player MIGUEL
|
||||
let Ok(player) = player.get_single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Smoothly follow the player
|
||||
let player_position = Vec3::new(
|
||||
player.translation.x,
|
||||
player.translation.y,
|
||||
camera.translation.z,
|
||||
);
|
||||
camera.translation = camera
|
||||
.translation
|
||||
.lerp(player_position, time.delta_seconds() * 2.0);
|
||||
}
|
||||
|
||||
35
src/goal.rs
Normal file
35
src/goal.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
|
||||
use crate::player::Player;
|
||||
|
||||
#[derive(Default, Component)]
|
||||
pub struct Goal;
|
||||
|
||||
#[derive(Default, Bundle, LdtkEntity)]
|
||||
pub struct GoalBundle {
|
||||
goal: Goal,
|
||||
#[sprite_sheet_bundle]
|
||||
sprite_sheet_bundle: LdtkSpriteSheetBundle,
|
||||
#[grid_coords]
|
||||
grid_coords: GridCoords,
|
||||
}
|
||||
|
||||
pub 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;
|
||||
}
|
||||
}
|
||||
10
src/level_structure.rs
Normal file
10
src/level_structure.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
|
||||
#[derive(Default, Component)]
|
||||
pub struct Wall;
|
||||
|
||||
#[derive(Default, Bundle, LdtkIntCell)]
|
||||
pub struct WallBundle {
|
||||
wall: Wall,
|
||||
}
|
||||
25
src/logging.rs
Normal file
25
src/logging.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::player::Player;
|
||||
|
||||
// Yes I used chatgpt for this
|
||||
// sue me, it's a mundane task :P
|
||||
// - Mjork
|
||||
pub fn log_positions(
|
||||
camera_query: Query<&Transform, With<Camera2d>>,
|
||||
player_query: Query<&Transform, With<Player>>,
|
||||
) {
|
||||
// Log camera position
|
||||
if let Ok(camera_transform) = camera_query.get_single() {
|
||||
info!("Camera Position: {:?}", camera_transform.translation);
|
||||
} else {
|
||||
info!("Camera not found or multiple cameras detected.");
|
||||
}
|
||||
|
||||
// Log player position
|
||||
if let Ok(player_transform) = player_query.get_single() {
|
||||
info!("Player Position: {:?}", player_transform.translation);
|
||||
} else {
|
||||
info!("Player not found or multiple players detected.");
|
||||
}
|
||||
}
|
||||
65
src/main.rs
65
src/main.rs
@@ -1,9 +1,18 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
use player::{Player, PlayerPlugin};
|
||||
use camera::CameraPlugin;
|
||||
use logging::log_positions;
|
||||
use player::PlayerPlugin;
|
||||
use goal::{GoalBundle, check_goal};
|
||||
use level_structure::WallBundle;
|
||||
|
||||
mod camera;
|
||||
mod logging;
|
||||
mod player;
|
||||
mod goal;
|
||||
mod level_structure;
|
||||
|
||||
pub const GRID_SIZE: i32 = 16;
|
||||
|
||||
const LEVEL_SET: [&str; 2] = [
|
||||
"ee0d1601-73f0-11ef-b8f3-190460ac7628",
|
||||
@@ -14,43 +23,28 @@ fn main() {
|
||||
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
|
||||
.add_plugins(LdtkPlugin)
|
||||
.add_plugins(PlayerPlugin)
|
||||
.add_systems(Update, (translate_grid_coords_entities, check_goal))
|
||||
.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)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let mut camera = Camera2dBundle::default();
|
||||
let level_set = LevelSet::from_iids(LEVEL_SET);
|
||||
camera.projection.scale = 1.111;
|
||||
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"),
|
||||
level_set,
|
||||
level_set: LEVEL_SET,
|
||||
// transform: Transform::from_xyz(-256., -144., 0.),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
#[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,
|
||||
}
|
||||
|
||||
pub const GRID_SIZE: i32 = 16;
|
||||
|
||||
fn translate_grid_coords_entities(
|
||||
mut grid_coords_entities: Query<(&mut Transform, &GridCoords), Changed<GridCoords>>,
|
||||
) {
|
||||
@@ -64,33 +58,4 @@ fn translate_grid_coords_entities(
|
||||
#[derive(Default, Component)]
|
||||
struct Wall;
|
||||
|
||||
#[derive(Default, Bundle, LdtkIntCell)]
|
||||
struct WallBundle {
|
||||
wall: Wall,
|
||||
}
|
||||
|
||||
fn check_goal(
|
||||
level_selection: ResMut<LevelSelection>,
|
||||
players: Query<&GridCoords, (With<Player>, Changed<GridCoords>)>,
|
||||
goals: Query<&GridCoords, With<Goal>>,
|
||||
mut camera_query: Query<
|
||||
(
|
||||
&mut bevy::render::camera::OrthographicProjection,
|
||||
&mut Transform,
|
||||
),
|
||||
Without<Player>,
|
||||
>,
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use bevy::{prelude::*, utils::HashSet};
|
||||
use bevy_ecs_ldtk::prelude::*;
|
||||
|
||||
use crate::{Wall, GRID_SIZE};
|
||||
use crate::level_structure::Wall;
|
||||
|
||||
#[derive(Default, Component)]
|
||||
pub(crate) struct Player;
|
||||
@@ -36,7 +36,8 @@ fn move_player_from_input(
|
||||
input: Res<ButtonInput<KeyCode>>,
|
||||
level_walls: Res<LevelWalls>,
|
||||
) {
|
||||
let movement_direction = if input.just_pressed(KeyCode::KeyW) {
|
||||
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)
|
||||
@@ -86,8 +87,8 @@ fn cache_wall_locations(
|
||||
|
||||
let new_level_walls = LevelWalls {
|
||||
wall_locations,
|
||||
level_width: level.px_wid / GRID_SIZE,
|
||||
level_height: level.px_hei / GRID_SIZE,
|
||||
level_width: level.px_wid / crate::GRID_SIZE,
|
||||
level_height: level.px_hei / crate::GRID_SIZE,
|
||||
};
|
||||
|
||||
*level_walls = new_level_walls;
|
||||
|
||||
Reference in New Issue
Block a user