use bevy::prelude::*; use bevy_xpbd_2d::{ math::{Scalar, Vector, PI}, prelude::*, SubstepSchedule, SubstepSet, }; use crate::GRAVITY_VECTOR; #[derive(Component, Reflect)] #[component(storage = "SparseSet")] pub struct Grounded; #[derive(Component, Reflect)] pub struct JumpImpulse(Scalar); impl Default for JumpImpulse { fn default() -> Self { JumpImpulse(7.) } } #[derive(Component, Reflect)] pub struct MaxJumpDuration(Scalar); impl Default for MaxJumpDuration { fn default() -> Self { MaxJumpDuration(0.1) } } #[derive(Component, Reflect)] pub struct MovementAcceleration(Scalar); impl Default for MovementAcceleration { fn default() -> Self { MovementAcceleration(75.) } } #[derive(Component, Reflect)] pub struct MovementDampingFactor(Scalar); impl Default for MovementDampingFactor { fn default() -> Self { MovementDampingFactor(0.9) } } #[derive(Component, Reflect)] pub struct MaxSlopeAngle(Scalar); impl Default for MaxSlopeAngle { fn default() -> Self { MaxSlopeAngle(PI * 0.45) } } #[derive(Component, Default, Reflect)] pub struct CharacterController; #[derive(Component, Reflect)] pub struct ControllerGravity(Vector); impl Default for ControllerGravity { fn default() -> Self { ControllerGravity(GRAVITY_VECTOR) } } #[derive(Bundle, Default)] pub struct MovementBundle { acceleration: MovementAcceleration, damping: MovementDampingFactor, jump_impulse: JumpImpulse, max_slope_angle: MaxSlopeAngle, direction: MovementDirection, } #[derive(Bundle)] pub struct CharacterControllerBundle { character_controller: CharacterController, rigidbody: RigidBody, collider: Collider, ground_caster: ShapeCaster, gravity: ControllerGravity, movement: MovementBundle, locked_axes: LockedAxes, } impl Default for CharacterControllerBundle { fn default() -> Self { let collider = Collider::rectangle(12., 24.); let mut caster_shape = collider.clone(); caster_shape.set_scale(Vector::ONE * 0.5, 1); Self { locked_axes: LockedAxes::ROTATION_LOCKED, character_controller: CharacterController, rigidbody: RigidBody::Kinematic, collider, ground_caster: ShapeCaster::new(caster_shape, Vector::ZERO, 0.0, Direction2d::NEG_Y) .with_max_time_of_impact(1.0), gravity: ControllerGravity(GRAVITY_VECTOR * 5.), movement: MovementBundle { acceleration: MovementAcceleration(420.), jump_impulse: JumpImpulse(46.), ..default() }, } } } #[derive(Component, Reflect)] #[component(storage = "SparseSet")] pub struct Jumping; #[derive(Component, Default, Reflect)] pub struct MovementDirection(pub Scalar); pub struct CharacterControllerPlugin; impl Plugin for CharacterControllerPlugin { fn build(&self, app: &mut App) { app.register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .register_type::() .add_systems( Update, ( update_grounded, apply_gravity, handle_movement, handle_jump, apply_movement_damping, ) .chain(), ) .add_systems( // Run collision handling in substep schedule SubstepSchedule, kinematic_controller_collisions.in_set(SubstepSet::SolveUserConstraints), ); } } fn handle_movement( time: Res