From 3aa67140c85fea11c0224cec713a8f8a6fd557cb Mon Sep 17 00:00:00 2001 From: Emil Date: Sat, 20 Jun 2026 23:35:41 +0300 Subject: [PATCH] feat: vector-based movement for responsive control and air strafing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed from physics-based (accumulate + damping) to vector-based: - Horizontal velocity is SET directly, not accumulated - Press A → cvx = -0.5 (instant, no ramp-up) - Release → cvx = 0 (instant stop, no sliding) - No horizontal damping = precise control - Air strafing: same horizontal speed in air as on ground - Can change direction mid-jump freely - Vertical: gravity + minimal damping (0.99) for natural fall - Jump sets cvy directly, gravity pulls back - move_speed 0.5 (was 0.3), max_vel 2.0 (was 1.0) - Player: move_dir tracks current direction, stop_horizontal() clears velocity - handle_input: calls stop_horizontal when no movement key held 109 tests, 0 warnings, 0 failures --- src/entity/entity.rs | 12 ++++++++++++ src/entity/player.rs | 23 +++++++++++++++++------ src/game.rs | 26 +++++++++++++++++++------- src/physics/verlet.rs | 2 +- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/entity/entity.rs b/src/entity/entity.rs index 0fa3652..ab8d537 100644 --- a/src/entity/entity.rs +++ b/src/entity/entity.rs @@ -181,6 +181,18 @@ impl Entity { } } + pub fn set_horizontal_vel(&mut self, vx: f32) { + if self.rigid { + self.cvx = vx; + } + } + + pub fn set_vertical_vel(&mut self, vy: f32) { + if self.rigid { + self.cvy = vy; + } + } + pub fn apply_fire_damage(&mut self) { if !self.on_fire { return; diff --git a/src/entity/player.rs b/src/entity/player.rs index 243b7d6..c76f491 100644 --- a/src/entity/player.rs +++ b/src/entity/player.rs @@ -4,6 +4,7 @@ pub struct Player { pub entity_id: EntityId, pub move_speed: f32, pub jump_force: f32, + pub move_dir: i32, } impl Player { @@ -11,8 +12,9 @@ impl Player { let id = manager.spawn(EntityKind::Player); Self { entity_id: id, - move_speed: 0.3, + move_speed: 0.5, jump_force: 1.5, + move_dir: 0, } } @@ -22,22 +24,31 @@ impl Player { } } - pub fn move_left(&self, manager: &mut EntityManager) { + pub fn move_left(&mut self, manager: &mut EntityManager) { + self.move_dir = -1; if let Some(e) = manager.get_mut(self.entity_id) { - e.move_center(-self.move_speed, 0.0); + e.set_horizontal_vel(-self.move_speed); } } - pub fn move_right(&self, manager: &mut EntityManager) { + pub fn move_right(&mut self, manager: &mut EntityManager) { + self.move_dir = 1; if let Some(e) = manager.get_mut(self.entity_id) { - e.move_center(self.move_speed, 0.0); + e.set_horizontal_vel(self.move_speed); + } + } + + pub fn stop_horizontal(&mut self, manager: &mut EntityManager) { + self.move_dir = 0; + if let Some(e) = manager.get_mut(self.entity_id) { + e.set_horizontal_vel(0.0); } } pub fn jump(&self, manager: &mut EntityManager, on_ground: bool) { if on_ground { if let Some(e) = manager.get_mut(self.entity_id) { - e.move_center(0.0, -self.jump_force); + e.set_vertical_vel(-self.jump_force); } } } diff --git a/src/game.rs b/src/game.rs index 853768a..81f9a42 100644 --- a/src/game.rs +++ b/src/game.rs @@ -245,11 +245,21 @@ impl Game { self.player.jump(&mut self.entities, on_ground); } - // Movement: applied every tick while held - for action in self.input.held_actions() { + // Movement: applied every tick while held (vector-style, direct velocity) + let held = self.input.held_actions(); + let moving_left = held.iter().any(|a| *a == Action::MoveLeft); + let moving_right = held.iter().any(|a| *a == Action::MoveRight); + + if moving_left && !moving_right { + self.player.move_left(&mut self.entities); + } else if moving_right && !moving_left { + self.player.move_right(&mut self.entities); + } else { + self.player.stop_horizontal(&mut self.entities); + } + + for action in &held { match action { - Action::MoveLeft => self.player.move_left(&mut self.entities), - Action::MoveRight => self.player.move_right(&mut self.entities), Action::MoveCameraLeft => self.cam_x -= 2, Action::MoveCameraRight => self.cam_x += 2, Action::MoveCameraUp => self.cam_y -= 2, @@ -335,7 +345,7 @@ impl Game { } } - fn update_rigid_entity(&mut self, idx: usize, gravity: f32, damping: f32, max_vel: f32) { + fn update_rigid_entity(&mut self, idx: usize, gravity: f32, _damping: f32, max_vel: f32) { let (cx, cy, cvx, cvy, half_w, half_h) = { let e = &self.entities.all()[idx]; (e.cx, e.cy, e.cvx, e.cvy, e.half_w, e.half_h) @@ -343,8 +353,10 @@ impl Game { let mut nx = cx; let mut ny = cy; - let mut nvx = cvx * damping; - let mut nvy = cvy * damping; + // Horizontal: direct velocity, no damping (vector movement) + let mut nvx = cvx; + // Vertical: gravity + light damping for natural fall + let mut nvy = cvy * 0.99; nvy += gravity; let v_mag = (nvx * nvx + nvy * nvy).sqrt(); diff --git a/src/physics/verlet.rs b/src/physics/verlet.rs index aeed351..be4e2bf 100644 --- a/src/physics/verlet.rs +++ b/src/physics/verlet.rs @@ -97,7 +97,7 @@ impl VerletSolver { gravity: 0.04, damping: 0.97, dt: 1.0, - max_vel: 1.0, + max_vel: 2.0, substeps: 8, } }