mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
85 lines
2.8 KiB
Rust
85 lines
2.8 KiB
Rust
//! Handle player input and translate it into movement through a character
|
|
//! controller. A character controller is the collection of systems that govern
|
|
//! the movement of characters.
|
|
//!
|
|
//! In our case, the character controller has the following logic:
|
|
//! - Set [`MovementController`] intent based on directional keyboard input.
|
|
//! This is done in the `player` module, as it is specific to the player
|
|
//! character.
|
|
//! - Apply movement based on [`MovementController`] intent and maximum speed.
|
|
//! - Wrap the character within the window.
|
|
//!
|
|
//! Note that the implementation used here is limited for demonstration
|
|
//! purposes. If you want to move the player in a smoother way,
|
|
//! consider using a [fixed timestep](https://github.com/bevyengine/bevy/blob/main/examples/movement/physics_in_fixed_timestep.rs).
|
|
|
|
use bevy::{prelude::*, window::PrimaryWindow};
|
|
|
|
use crate::AppSet;
|
|
|
|
pub(super) fn plugin(app: &mut App) {
|
|
app.register_type::<(MovementController, ScreenWrap)>();
|
|
|
|
app.add_systems(
|
|
Update,
|
|
(apply_movement, apply_screen_wrap)
|
|
.chain()
|
|
.in_set(AppSet::Update),
|
|
);
|
|
}
|
|
|
|
/// These are the movement parameters for our character controller.
|
|
/// For now, this is only used for a single player, but it could power NPCs or
|
|
/// other players as well.
|
|
#[derive(Component, Reflect)]
|
|
#[reflect(Component)]
|
|
pub struct MovementController {
|
|
/// The direction the character wants to move in.
|
|
pub intent: Vec2,
|
|
|
|
/// Maximum speed in world units per second.
|
|
/// 1 world unit = 1 pixel when using the default 2D camera and no physics
|
|
/// engine.
|
|
pub max_speed: f32,
|
|
}
|
|
|
|
impl Default for MovementController {
|
|
fn default() -> Self {
|
|
Self {
|
|
intent: Vec2::ZERO,
|
|
// 400 pixels per second is a nice default, but we can still vary this per character.
|
|
max_speed: 400.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn apply_movement(
|
|
time: Res<Time>,
|
|
mut movement_query: Query<(&MovementController, &mut Transform)>,
|
|
) {
|
|
for (controller, mut transform) in &mut movement_query {
|
|
let velocity = controller.max_speed * controller.intent;
|
|
transform.translation += velocity.extend(0.0) * time.delta_seconds();
|
|
}
|
|
}
|
|
|
|
#[derive(Component, Reflect)]
|
|
#[reflect(Component)]
|
|
pub struct ScreenWrap;
|
|
|
|
fn apply_screen_wrap(
|
|
window_query: Query<&Window, With<PrimaryWindow>>,
|
|
mut wrap_query: Query<&mut Transform, With<ScreenWrap>>,
|
|
) {
|
|
let Ok(window) = window_query.get_single() else {
|
|
return;
|
|
};
|
|
let size = window.size() + 256.0;
|
|
let half_size = size / 2.0;
|
|
for mut transform in &mut wrap_query {
|
|
let position = transform.translation.xy();
|
|
let wrapped = (position + half_size).rem_euclid(size) - half_size;
|
|
transform.translation = wrapped.extend(transform.translation.z);
|
|
}
|
|
}
|