update movement
This commit is contained in:
parent
e4be421ef9
commit
e09b7d2537
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 34 B |
1
assets/player.png
Symbolic link
1
assets/player.png
Symbolic link
@ -0,0 +1 @@
|
||||
/home/mitchell/Pictures/player.png
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 34 B |
134
src/avian.rs
134
src/avian.rs
@ -6,15 +6,7 @@ pub struct CharacterControllerPlugin;
|
||||
impl Plugin for CharacterControllerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_message::<MovementAction>()
|
||||
.add_systems(Update, ((keyboard_input, gamepad_input), movement).chain())
|
||||
.add_systems(
|
||||
// Run collision handling after collision detection.
|
||||
//
|
||||
// NOTE: The collision implementation here is very basic and a bit buggy.
|
||||
// A collide-and-slide algorithm would likely work better.
|
||||
PhysicsSchedule,
|
||||
kinematic_controller_collisions.in_set(NarrowPhaseSystems::Last),
|
||||
);
|
||||
.add_systems(Update, ((keyboard_input, gamepad_input), movement).chain());
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,8 +43,8 @@ impl CharacterControllerBundle {
|
||||
character_controller: CharacterController,
|
||||
body: RigidBody::Kinematic,
|
||||
collider,
|
||||
speed: MaxSpeed(100.),
|
||||
acceleration: MaxAcceleration(1000.),
|
||||
speed: MaxSpeed(300.),
|
||||
acceleration: MaxAcceleration(5000.),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,6 +101,7 @@ fn movement(
|
||||
|
||||
for (max_speed, max_acceleration, mut linear_velocity) in &mut controllers {
|
||||
while movement_reader.len() > 1 {
|
||||
warn!("Extra movement message. Ignoring");
|
||||
movement_reader.read();
|
||||
}
|
||||
|
||||
@ -128,122 +121,3 @@ fn movement(
|
||||
**linear_velocity += delta;
|
||||
}
|
||||
}
|
||||
|
||||
/// Kinematic bodies do not get pushed by collisions by default,
|
||||
/// so it needs to be done manually.
|
||||
///
|
||||
/// This system handles collision response for kinematic character controllers
|
||||
/// by pushing them along their contact normals by the current penetration depth,
|
||||
/// and applying velocity corrections in order to snap to slopes, slide along walls,
|
||||
/// and predict collisions using speculative contacts.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn kinematic_controller_collisions(
|
||||
collisions: Collisions,
|
||||
bodies: Query<&RigidBody>,
|
||||
collider_rbs: Query<&ColliderOf, Without<Sensor>>,
|
||||
mut character_controllers: Query<
|
||||
(&mut Position, &mut LinearVelocity),
|
||||
(With<RigidBody>, With<CharacterController>),
|
||||
>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
// Iterate through collisions and move the kinematic body to resolve penetration
|
||||
for contacts in collisions.iter() {
|
||||
// Get the rigid body entities of the colliders (colliders could be children)
|
||||
let Ok([&ColliderOf { body: rb1 }, &ColliderOf { body: rb2 }]) =
|
||||
collider_rbs.get_many([contacts.collider1, contacts.collider2])
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Get the body of the character controller and whether it is the first
|
||||
// or second entity in the collision.
|
||||
let is_first: bool;
|
||||
|
||||
let character_rb: RigidBody;
|
||||
let is_other_dynamic: bool;
|
||||
|
||||
let (mut position, mut linear_velocity) =
|
||||
if let Ok(character) = character_controllers.get_mut(rb1) {
|
||||
is_first = true;
|
||||
character_rb = *bodies.get(rb1).unwrap();
|
||||
is_other_dynamic = bodies.get(rb2).is_ok_and(|rb| rb.is_dynamic());
|
||||
character
|
||||
} else if let Ok(character) = character_controllers.get_mut(rb2) {
|
||||
is_first = false;
|
||||
character_rb = *bodies.get(rb2).unwrap();
|
||||
is_other_dynamic = bodies.get(rb1).is_ok_and(|rb| rb.is_dynamic());
|
||||
character
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// This system only handles collision response for kinematic character controllers.
|
||||
if !character_rb.is_kinematic() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate through contact manifolds and their contacts.
|
||||
// Each contact in a single manifold shares the same contact normal.
|
||||
for manifold in contacts.manifolds.iter() {
|
||||
let normal = if is_first {
|
||||
-manifold.normal
|
||||
} else {
|
||||
manifold.normal
|
||||
};
|
||||
|
||||
let mut deepest_penetration: Scalar = Scalar::MIN;
|
||||
|
||||
// Solve each penetrating contact in the manifold.
|
||||
for contact in manifold.points.iter() {
|
||||
if contact.penetration > 0.0 {
|
||||
position.0 += normal * contact.penetration;
|
||||
}
|
||||
deepest_penetration = deepest_penetration.max(contact.penetration);
|
||||
}
|
||||
|
||||
// For now, this system only handles velocity corrections for collisions against static geometry.
|
||||
if is_other_dynamic {
|
||||
continue;
|
||||
}
|
||||
|
||||
if deepest_penetration > 0.0 {
|
||||
// The character is intersecting an unclimbable object, like a wall.
|
||||
// We want the character to slide along the surface, similarly to
|
||||
// a collide-and-slide algorithm.
|
||||
|
||||
// Don't apply an impulse if the character is moving away from the surface.
|
||||
if linear_velocity.dot(normal) > 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Slide along the surface, rejecting the velocity along the contact normal.
|
||||
let impulse = linear_velocity.reject_from_normalized(normal);
|
||||
linear_velocity.0 = impulse;
|
||||
} else {
|
||||
// The character is not yet intersecting the other object,
|
||||
// but the narrow phase detected a speculative collision.
|
||||
//
|
||||
// We need to push back the part of the velocity
|
||||
// that would cause penetration within the next frame.
|
||||
|
||||
let normal_speed = linear_velocity.dot(normal);
|
||||
|
||||
// Don't apply an impulse if the character is moving away from the surface.
|
||||
if normal_speed > 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute the impulse to apply.
|
||||
let impulse_magnitude =
|
||||
normal_speed - (deepest_penetration / time.delta_secs_f64().adjust_precision());
|
||||
let mut impulse = impulse_magnitude * normal;
|
||||
|
||||
// Apply the impulse differently depending on the slope angle.
|
||||
// Avoid climbing up walls.
|
||||
impulse.y = impulse.y.max(0.0);
|
||||
linear_velocity.0 -= impulse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main.rs
19
src/main.rs
@ -23,12 +23,7 @@ fn main() {
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let mut projection = OrthographicProjection::default_2d();
|
||||
projection.scaling_mode = ScalingMode::AutoMin {
|
||||
min_width: 1920.,
|
||||
@ -38,10 +33,9 @@ fn setup(
|
||||
|
||||
// player
|
||||
commands.spawn((
|
||||
Mesh2d(meshes.add(Capsule2d::new(12.5, 20.0))),
|
||||
MeshMaterial2d(materials.add(Color::srgb(0.2, 0.7, 0.9))),
|
||||
Transform::from_xyz(0.0, -100.0, 0.0),
|
||||
CharacterControllerBundle::new(Collider::capsule(12.5, 20.0)),
|
||||
Sprite::from_image(asset_server.load("player.png")),
|
||||
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||
CharacterControllerBundle::new(Collider::capsule(15., 27.5)),
|
||||
));
|
||||
|
||||
// A cube to move around
|
||||
@ -55,11 +49,6 @@ fn setup(
|
||||
RigidBody::Dynamic,
|
||||
Collider::rectangle(30.0, 30.0),
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
Sprite::from_image(asset_server.load("player.png")),
|
||||
Transform::from_xyz(0., 0., 0.),
|
||||
));
|
||||
}
|
||||
|
||||
fn debug_border(mut gizmos: Gizmos) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user