use happy_feet as kinematic character controller (#42)
This commit is contained in:
@@ -1,63 +1,26 @@
|
||||
use super::{ControlState, ControllerSet, Controls, controller_common::MovementSpeedFactor};
|
||||
use super::{ControlState, ControllerSet, Controls};
|
||||
use crate::control::controller_common::MovementSpeedFactor;
|
||||
use crate::control::controls::ControllerSettings;
|
||||
use crate::{GameState, abilities::TriggerStateRes, player::PlayerBodyMesh};
|
||||
use avian3d::{math::*, prelude::*};
|
||||
use bevy::{ecs::query::Has, prelude::*};
|
||||
use bevy::prelude::*;
|
||||
use happy_feet::prelude::{Grounding, KinematicVelocity, MoveInput};
|
||||
|
||||
use super::controller_common::{
|
||||
CharacterController, ControllerGravity, Grounded, JumpImpulse, MaxSlopeAngle, MovementSpeed,
|
||||
PlayerMovement, reset_upon_switch,
|
||||
};
|
||||
use super::controller_common::PlayerMovement;
|
||||
|
||||
pub struct CharacterControllerPlugin;
|
||||
|
||||
impl Plugin for CharacterControllerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
reset_upon_switch,
|
||||
set_movement_flag,
|
||||
update_grounded,
|
||||
apply_gravity,
|
||||
rotate_view,
|
||||
movement,
|
||||
)
|
||||
PreUpdate,
|
||||
(set_movement_flag, rotate_view, apply_controls)
|
||||
.chain()
|
||||
.in_set(ControllerSet::ApplyControlsRun)
|
||||
.run_if(in_state(GameState::Playing)), // todo check if we can make this less viral
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the [`Grounded`] status for character controllers.
|
||||
fn update_grounded(
|
||||
mut commands: Commands,
|
||||
mut query: Query<
|
||||
(Entity, &ShapeHits, &Rotation, Option<&MaxSlopeAngle>),
|
||||
With<CharacterController>,
|
||||
>,
|
||||
) {
|
||||
for (entity, hits, rotation, max_slope_angle) in &mut query {
|
||||
// The character is grounded if the shape caster has a hit with a normal
|
||||
// that isn't too steep.
|
||||
let is_grounded = hits.iter().any(|hit| {
|
||||
if let Some(angle) = max_slope_angle {
|
||||
(rotation * -hit.normal2).angle_between(Vector::Y).abs() <= angle.0
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
if is_grounded {
|
||||
debug!("grounded");
|
||||
commands.entity(entity).insert(Grounded);
|
||||
} else {
|
||||
debug!("not grounded");
|
||||
commands.entity(entity).remove::<Grounded>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the movement flag, which is an indicator for the rig animation and the braking system.
|
||||
fn set_movement_flag(
|
||||
mut player_movement: ResMut<PlayerMovement>,
|
||||
@@ -97,70 +60,38 @@ fn rotate_view(
|
||||
}
|
||||
}
|
||||
|
||||
/// Responds to [`MovementAction`] events and moves character controllers accordingly.
|
||||
fn movement(
|
||||
controls: Res<Controls>,
|
||||
mut controllers: Query<(
|
||||
&MovementSpeed,
|
||||
fn apply_controls(
|
||||
controls: Res<ControlState>,
|
||||
mut character: Query<(
|
||||
&mut MoveInput,
|
||||
&mut Grounding,
|
||||
&mut KinematicVelocity,
|
||||
&ControllerSettings,
|
||||
&MovementSpeedFactor,
|
||||
&JumpImpulse,
|
||||
&mut LinearVelocity,
|
||||
Has<Grounded>,
|
||||
)>,
|
||||
rig_transform_q: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
|
||||
mut jump_used: Local<bool>,
|
||||
) {
|
||||
let mut direction = controls.keyboard_state.move_dir;
|
||||
let Ok((mut move_input, mut grounding, mut velocity, settings, move_factor)) =
|
||||
character.single_mut()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut jump_requested = controls.keyboard_state.jump;
|
||||
let mut direction = -controls.move_dir.extend(0.0).xzy();
|
||||
|
||||
if let Some(gamepad) = controls.gamepad_state {
|
||||
direction += gamepad.move_dir;
|
||||
|
||||
jump_requested |= gamepad.jump;
|
||||
if let Some(ref rig_transform) = rig_transform_q {
|
||||
direction = (rig_transform.forward() * direction.z) + (rig_transform.right() * direction.x);
|
||||
}
|
||||
|
||||
let ground_normal = *grounding.normal().unwrap_or(Dir3::Y);
|
||||
|
||||
let y_projection = direction.project_onto(ground_normal);
|
||||
direction -= y_projection;
|
||||
direction = direction.normalize_or_zero();
|
||||
|
||||
for (movement_speed, factor, jump_impulse, mut linear_velocity, is_grounded) in &mut controllers
|
||||
{
|
||||
let mut direction = direction.extend(0.0).xzy();
|
||||
move_input.set(direction * move_factor.0);
|
||||
|
||||
if let Some(ref rig_transform) = rig_transform_q {
|
||||
direction =
|
||||
(rig_transform.forward() * direction.z) + (rig_transform.right() * direction.x);
|
||||
}
|
||||
|
||||
linear_velocity.x = -direction.x * movement_speed.0 * factor.0;
|
||||
linear_velocity.z = -direction.z * movement_speed.0 * factor.0;
|
||||
|
||||
if is_grounded && jump_requested && !*jump_used {
|
||||
linear_velocity.y = jump_impulse.0;
|
||||
debug!("jump");
|
||||
*jump_used = true;
|
||||
}
|
||||
|
||||
if !controls.keyboard_state.jump
|
||||
&& !controls
|
||||
.gamepad_state
|
||||
.map(|state| state.jump)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
*jump_used = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies [`ControllerGravity`] to character controllers.
|
||||
fn apply_gravity(
|
||||
time: Res<Time>,
|
||||
mut controllers: Query<(&ControllerGravity, &mut LinearVelocity, Option<&Grounded>)>,
|
||||
) {
|
||||
let delta_time = time.delta_secs();
|
||||
|
||||
for (gravity, mut linear_velocity, grounded) in &mut controllers {
|
||||
if grounded.is_none() {
|
||||
linear_velocity.0 += gravity.0 * delta_time;
|
||||
}
|
||||
if controls.jump && grounding.is_grounded() {
|
||||
happy_feet::movement::jump(settings.jump_force, &mut velocity, &mut grounding, Dir3::Y)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user