diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-09-02 00:05:29 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-09-02 00:05:29 -0700 |
| commit | 51ecf9530dae94341e2ba96453a4bf7e376ad33b (patch) | |
| tree | 24b3ec0eee4729802cddbb6af4556999e3e42d9b /src/content | |
| parent | 5ebc92d5a6bbbe14e7c4c25b25a4ff8578ddcd5a (diff) | |
character controller
Diffstat (limited to 'src/content')
| -rw-r--r-- | src/content/entities/entity_wizard.rs | 158 | ||||
| -rw-r--r-- | src/content/entities/mod.rs | 1 |
2 files changed, 159 insertions, 0 deletions
diff --git a/src/content/entities/entity_wizard.rs b/src/content/entities/entity_wizard.rs new file mode 100644 index 0000000..eea2a48 --- /dev/null +++ b/src/content/entities/entity_wizard.rs @@ -0,0 +1,158 @@ +use glam::Vec2; +use rapier2d::{ + control::{CharacterAutostep, CharacterLength, KinematicCharacterController}, + pipeline::QueryFilter, +}; + +use crate::{ + config::GRAVITY, + input::Input, + sim::{ + entity::{EntityBehaviour, EntityDef, EntityUpdateCtx}, + sim_manager::SimCtx, + }, +}; + +struct WizardEntityBehaviour { + movement_input_x: f32, + jump: f32, + + grounded: bool, + acc_vel: Vec2, + kinematic_controller: KinematicCharacterController, +} + +const JUMP_VELOCITY: f32 = 100.0; +const ACCEL: f32 = 1000.0; +const AIR_ACCEL: f32 = 350.0; +const MAX_SPEED: f32 = 75.0; + +impl EntityBehaviour for WizardEntityBehaviour { + fn update(&mut self, _: &mut EntityUpdateCtx, ctx: &mut SimCtx, _: f32) { + if ctx.input_manager.held(Input::Left) { + self.movement_input_x = -1.0; + } else if ctx.input_manager.held(Input::Right) { + self.movement_input_x = 1.0; + } else { + self.movement_input_x = 0.0; + } + + if ctx.input_manager.pressed(Input::Up) { + self.jump = 0.06; + } + } + + fn physics_update( + &mut self, + update_ctx: &mut EntityUpdateCtx, + ctx: &mut SimCtx, + delta_time: f32, + ) { + if self.grounded { + // TODO delta_time + if self.movement_input_x.abs() < 0.2 { + self.acc_vel.x = self.acc_vel.x * 0.7; + } + // if we're moving up, keep moving up + self.acc_vel.y = self.acc_vel.y.min(0.0); + if self.jump > 0.0 { + self.acc_vel.y -= JUMP_VELOCITY; + self.jump = 0.0; + } + } else { + self.acc_vel.x = self.acc_vel.x * 0.95; + self.acc_vel.y += GRAVITY * delta_time; + + if self.jump > 0.0 { + self.jump -= delta_time; + } + } + + if self.acc_vel.x.abs() < 1.0 { + self.acc_vel.x = 0.0; + } + + let flip_adj = if self.acc_vel.x.signum() != self.movement_input_x.signum() { + 3.0 + } else { + 1.0 + }; + self.acc_vel.x += self.movement_input_x + * if self.grounded { ACCEL } else { AIR_ACCEL } + * delta_time + * flip_adj; + self.acc_vel.x = self.acc_vel.x.clamp(-MAX_SPEED, MAX_SPEED); + + let query_pipeline = ctx + .rb_manager + .physics_manager + .world + .query_pipeline_with_filter( + QueryFilter::default().exclude_rigid_body(update_ctx.entity_data.rb_h.unwrap()), + ); + + let rb = ctx + .rb_manager + .physics_manager + .world + .bodies + .get(update_ctx.entity_data.rb_h.unwrap()) + .unwrap(); + + let collider = ctx + .rb_manager + .physics_manager + .world + .colliders + .get(update_ctx.entity_data.collider_h.unwrap()) + .unwrap(); + + // move_shape works in translations, not velocities: feed it the distance we + // want to cover this step, and convert the allowed distance back to a velocity. + let movement = self.kinematic_controller.move_shape( + delta_time, + &query_pipeline, + collider.shape(), + rb.position(), + self.acc_vel * delta_time, + |_| {}, + ); + + let rb = ctx + .rb_manager + .physics_manager + .world + .bodies + .get_mut(update_ctx.entity_data.rb_h.unwrap()) + .unwrap(); + + rb.set_linvel(movement.translation / delta_time, true); + + self.grounded = movement.grounded; + } +} + +pub fn entity_wizard_def(position: Vec2) -> EntityDef { + let mut kinematic_controller = KinematicCharacterController::default(); + kinematic_controller.up = -Vec2::Y; + kinematic_controller.autostep = Some(CharacterAutostep { + max_height: CharacterLength::Absolute(4.0), + min_width: CharacterLength::Absolute(8.0), + include_dynamic_bodies: false, + }); + kinematic_controller.offset = CharacterLength::Absolute(0.1); + kinematic_controller.normal_nudge_factor = 1.0e-3; + kinematic_controller.max_slope_climb_angle = 45f32.to_radians(); + + EntityDef::kinematic_from_sprite( + position, + "assets/sprites/wizard", + Some(Box::new(WizardEntityBehaviour { + movement_input_x: 0.0, + jump: 0.0, + grounded: false, + acc_vel: Vec2::ZERO, + kinematic_controller, + })), + ) +} diff --git a/src/content/entities/mod.rs b/src/content/entities/mod.rs index 0ec45ce..11c1706 100644 --- a/src/content/entities/mod.rs +++ b/src/content/entities/mod.rs @@ -2,3 +2,4 @@ pub mod entity_bullet_emitter; pub mod entity_cube; pub mod entity_grenade; pub mod entity_tnt; +pub mod entity_wizard; |
