feat: vector-based movement for responsive control and air strafing
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
This commit is contained in:
@@ -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) {
|
pub fn apply_fire_damage(&mut self) {
|
||||||
if !self.on_fire {
|
if !self.on_fire {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+17
-6
@@ -4,6 +4,7 @@ pub struct Player {
|
|||||||
pub entity_id: EntityId,
|
pub entity_id: EntityId,
|
||||||
pub move_speed: f32,
|
pub move_speed: f32,
|
||||||
pub jump_force: f32,
|
pub jump_force: f32,
|
||||||
|
pub move_dir: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
@@ -11,8 +12,9 @@ impl Player {
|
|||||||
let id = manager.spawn(EntityKind::Player);
|
let id = manager.spawn(EntityKind::Player);
|
||||||
Self {
|
Self {
|
||||||
entity_id: id,
|
entity_id: id,
|
||||||
move_speed: 0.3,
|
move_speed: 0.5,
|
||||||
jump_force: 1.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) {
|
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) {
|
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) {
|
pub fn jump(&self, manager: &mut EntityManager, on_ground: bool) {
|
||||||
if on_ground {
|
if on_ground {
|
||||||
if let Some(e) = manager.get_mut(self.entity_id) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-7
@@ -245,11 +245,21 @@ impl Game {
|
|||||||
self.player.jump(&mut self.entities, on_ground);
|
self.player.jump(&mut self.entities, on_ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Movement: applied every tick while held
|
// Movement: applied every tick while held (vector-style, direct velocity)
|
||||||
for action in self.input.held_actions() {
|
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 {
|
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::MoveCameraLeft => self.cam_x -= 2,
|
||||||
Action::MoveCameraRight => self.cam_x += 2,
|
Action::MoveCameraRight => self.cam_x += 2,
|
||||||
Action::MoveCameraUp => self.cam_y -= 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 (cx, cy, cvx, cvy, half_w, half_h) = {
|
||||||
let e = &self.entities.all()[idx];
|
let e = &self.entities.all()[idx];
|
||||||
(e.cx, e.cy, e.cvx, e.cvy, e.half_w, e.half_h)
|
(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 nx = cx;
|
||||||
let mut ny = cy;
|
let mut ny = cy;
|
||||||
let mut nvx = cvx * damping;
|
// Horizontal: direct velocity, no damping (vector movement)
|
||||||
let mut nvy = cvy * damping;
|
let mut nvx = cvx;
|
||||||
|
// Vertical: gravity + light damping for natural fall
|
||||||
|
let mut nvy = cvy * 0.99;
|
||||||
nvy += gravity;
|
nvy += gravity;
|
||||||
|
|
||||||
let v_mag = (nvx * nvx + nvy * nvy).sqrt();
|
let v_mag = (nvx * nvx + nvy * nvy).sqrt();
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl VerletSolver {
|
|||||||
gravity: 0.04,
|
gravity: 0.04,
|
||||||
damping: 0.97,
|
damping: 0.97,
|
||||||
dt: 1.0,
|
dt: 1.0,
|
||||||
max_vel: 1.0,
|
max_vel: 2.0,
|
||||||
substeps: 8,
|
substeps: 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user