Skip to content

Commit

Permalink
feat: Enhanced Collider Functionality and Dynamic Camera Tracking
Browse files Browse the repository at this point in the history
-The camera now tracks the player dynamically according to the screen
map configuration.
-The screen map details include:
  - Fixed screens, where the camera remains static and does not follow
  the player. Additionally, the camera handling at screen edges is
  improved to prevent unwanted following.
  - Screen transitions, which are configurable as either hard
  (immediate switch) or smooth (gradual transition).
-Collider components are now dynamically loaded from Tiled objects,
enhancing environmental interactions.

Please note that this is an initial version and may undergo significant
refactoring.
  • Loading branch information
uggla committed May 13, 2024
1 parent b2840fb commit 2b9f4be
Show file tree
Hide file tree
Showing 11 changed files with 931 additions and 258 deletions.
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,13 @@ opt-level = 3

[dev-dependencies]
pretty_assertions = "1.4.0"

# Binary target
[[bin]]
name = "rock_run"
path = "src/main.rs"

# Library target
[lib]
name = "screen_map"
path = "src/screen_map.rs"
318 changes: 225 additions & 93 deletions assets/level01.tmx

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions assets/rockrun.tiled-session
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
"scaleInDock": 1
},
"level01.tmx": {
"scale": 0.7310546874999999,
"selectedLayer": 0,
"scale": 0.25,
"selectedLayer": 2,
"viewCenter": {
"x": 592.9788939353462,
"y": 359.07026449372165
"x": 474,
"y": 202
}
},
"level01.tmx#backgrounds": {
Expand Down
114 changes: 113 additions & 1 deletion src/camera.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::screen_map::Transition;
use bevy::prelude::*;

use crate::{
level::{CurrentLevel, Level},
player::{Player, PlayerState, PLAYER_SPEED},
state::AppState,
};
pub struct CameraPlugin;
Expand All @@ -13,7 +15,11 @@ impl Plugin for CameraPlugin {
OnEnter(AppState::GameCreate),
move_camera_to_level_start_screen,
)
.add_systems(OnEnter(AppState::StartMenu), move_camera_to_center);
.add_systems(OnEnter(AppState::StartMenu), move_camera_to_center)
.add_systems(
Update,
camera_follows_player.run_if(in_state(AppState::GameRunning)),
);
}
}

Expand Down Expand Up @@ -43,3 +49,109 @@ fn move_camera_to_level_start_screen(
}
}
}

fn camera_follows_player(
time: Res<Time>,
player_query: Query<&Transform, With<Player>>,
player_state: Res<State<PlayerState>>,
mut camera_query: Query<&mut Transform, (With<Camera2d>, Without<Player>)>,
current_level: Res<CurrentLevel>,
levels: Query<&Level, With<Level>>,
mut offset: Local<Vec2>,
) {
let mut camera = camera_query.single_mut();
let player = player_query.single();

levels
.iter()
.filter(|level| level.id == current_level.id)
.for_each(|level| {
let (screen_center, screen_is_fixed, screen_transition) =
match level.map.get_screen(player.translation.xy()) {
Some(screen) => (
screen.get_center(),
screen.is_fixed_screen(),
screen.get_transition(),
),
None => (player.translation.xy(), false, Transition::Smooth),
};

let (above_screen_is_fixed, above_screen_transition) =
match level.map.get_above_screen(player.translation.xy()) {
Some(above_screen) => (
above_screen.is_fixed_screen(),
above_screen.get_transition(),
),
None => (true, Transition::Smooth),
};

let dist = screen_center - player.translation.xy();

let new_camera_pos = match (
screen_is_fixed,
screen_transition,
above_screen_is_fixed,
above_screen_transition,
) {
(true, Transition::Hard, _, _) => {
// Hard camera transition going down
Vec2::new(player.translation.x, player.translation.y + dist.y)
}
(false, _, true, Transition::Hard) => {
// Hard camera transition going up
Vec2::new(player.translation.x, player.translation.y + dist.y)
}
(true, Transition::Smooth, _, _) => {
// Smooth camera transition going down
if dist.y > 0.0
&& !(*player_state == PlayerState::Falling
|| *player_state == PlayerState::Jumping)
{
if camera.translation.y < screen_center.y {
let offset_tmp = *offset;
*offset = Vec2::new(
offset_tmp.x,
offset_tmp.y + PLAYER_SPEED / 2.0 * time.delta_seconds(),
);
} else {
*offset = dist;
}
}

debug!("player_state: {:?}", player_state);
debug!("offset: {:?}", offset);
Vec2::new(player.translation.x, player.translation.y + offset.y)
}
(false, _, true, Transition::Smooth) => {
// Smooth camera transition going up
if dist.y < 0.0
&& !(*player_state == PlayerState::Falling
|| *player_state == PlayerState::Jumping)
{
let offset_tmp = *offset;
if offset.y > 0.0 {
*offset = Vec2::new(
offset_tmp.x,
offset_tmp.y - PLAYER_SPEED / 2.0 * time.delta_seconds(),
);
} else {
*offset = Vec2::new(offset_tmp.x, 0.0);
}
}

debug!("player_state: {:?}", player_state);
debug!("offset: {:?}", offset);
Vec2::new(player.translation.x, player.translation.y + offset.y)
}
_ => {
// The camera follows the player
Vec2::new(player.translation.x, player.translation.y)
}
};

camera.translation = level
.map
.move_camera(camera.translation.xy(), new_camera_pos)
.extend(0.0);
})
}
40 changes: 20 additions & 20 deletions src/collision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use bevy::prelude::*;
use bevy_rapier2d::control::KinematicCharacterControllerOutput;

use crate::{
ground_platforms::{Ground, Platform},
player::{Player, PlayerState},
Ground, Platform,
};

pub struct CollisionPlugin;
Expand All @@ -23,26 +23,26 @@ fn player_collision(
ground: Query<Entity, With<Ground>>,
platforms: Query<Entity, With<Platform>>,
) {
let ground_entity = ground.single();

if let Ok((_player_entity, output)) = controllers.get_single() {
// info!(
// "Entity {:?} moved by {:?} and touches the ground: {:?}",
// player_entity, output.effective_translation, output.grounded
// );
for character_collision in output.collisions.iter() {
// Player collides with ground or platforms
if (character_collision.entity == ground_entity
|| platforms.contains(character_collision.entity))
&& output.grounded
&& state.get() != &PlayerState::Jumping
{
next_state.set(PlayerState::Idling);
if let Ok(ground_entity) = ground.get_single() {
if let Ok((_player_entity, output)) = controllers.get_single() {
// info!(
// "Entity {:?} moved by {:?} and touches the ground: {:?}",
// player_entity, output.effective_translation, output.grounded
// );
for character_collision in output.collisions.iter() {
// Player collides with ground or platforms
if (character_collision.entity == ground_entity
|| platforms.contains(character_collision.entity))
&& output.grounded
&& state.get() != &PlayerState::Jumping
{
next_state.set(PlayerState::Idling);
}
}
// Player is falling
if !output.grounded && state.get() == &PlayerState::Idling {
next_state.set(PlayerState::Falling);
}
}
// Player is falling
if !output.grounded && state.get() == &PlayerState::Idling {
next_state.set(PlayerState::Falling);
}
}
}
Loading

0 comments on commit 2b9f4be

Please sign in to comment.