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 {
|
impl Plugin for CharacterControllerPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_message::<MovementAction>()
|
app.add_message::<MovementAction>()
|
||||||
.add_systems(Update, ((keyboard_input, gamepad_input), movement).chain())
|
.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),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +43,8 @@ impl CharacterControllerBundle {
|
|||||||
character_controller: CharacterController,
|
character_controller: CharacterController,
|
||||||
body: RigidBody::Kinematic,
|
body: RigidBody::Kinematic,
|
||||||
collider,
|
collider,
|
||||||
speed: MaxSpeed(100.),
|
speed: MaxSpeed(300.),
|
||||||
acceleration: MaxAcceleration(1000.),
|
acceleration: MaxAcceleration(5000.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +101,7 @@ fn movement(
|
|||||||
|
|
||||||
for (max_speed, max_acceleration, mut linear_velocity) in &mut controllers {
|
for (max_speed, max_acceleration, mut linear_velocity) in &mut controllers {
|
||||||
while movement_reader.len() > 1 {
|
while movement_reader.len() > 1 {
|
||||||
|
warn!("Extra movement message. Ignoring");
|
||||||
movement_reader.read();
|
movement_reader.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,122 +121,3 @@ fn movement(
|
|||||||
**linear_velocity += delta;
|
**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();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
mut commands: Commands,
|
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
) {
|
|
||||||
let mut projection = OrthographicProjection::default_2d();
|
let mut projection = OrthographicProjection::default_2d();
|
||||||
projection.scaling_mode = ScalingMode::AutoMin {
|
projection.scaling_mode = ScalingMode::AutoMin {
|
||||||
min_width: 1920.,
|
min_width: 1920.,
|
||||||
@ -38,10 +33,9 @@ fn setup(
|
|||||||
|
|
||||||
// player
|
// player
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh2d(meshes.add(Capsule2d::new(12.5, 20.0))),
|
Sprite::from_image(asset_server.load("player.png")),
|
||||||
MeshMaterial2d(materials.add(Color::srgb(0.2, 0.7, 0.9))),
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
Transform::from_xyz(0.0, -100.0, 0.0),
|
CharacterControllerBundle::new(Collider::capsule(15., 27.5)),
|
||||||
CharacterControllerBundle::new(Collider::capsule(12.5, 20.0)),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
// A cube to move around
|
// A cube to move around
|
||||||
@ -55,11 +49,6 @@ fn setup(
|
|||||||
RigidBody::Dynamic,
|
RigidBody::Dynamic,
|
||||||
Collider::rectangle(30.0, 30.0),
|
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) {
|
fn debug_border(mut gizmos: Gizmos) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user