bevy-ichun 0.6.0

A simple kinematic character controller for avian3d
Documentation
//! # Character Actions System
//!
//! This module provides a component-based action system for handling character movement,
//! rotation, jumping, and gravity modifications. It processes actions in a specific order
//! and automatically clears them after each frame to ensure consistent behavior.
//!
//! ## Components
//!
//! * [`KccActions`]: Container component that holds all possible actions for an entity
//!
//! ## Usage
//!
//! ```rust
//! use bevy::prelude::*;
//! use your_crate::actions::{KccActions, MoveAction, JumpAction};
//!
//! fn setup_character(mut commands: Commands) {
//!     commands.spawn((
//!         Kcc,
//!         KccActions::new(),
//!     ));
//! }
//! ```

use bevy::prelude::*;
use gravity_action::GravityAction;
use jump_action::JumpAction;
use move_action::MoveAction;
use rotate_action::RotateAction;

use crate::{
    kcc::{Kcc, KccVelocity},
    system_sets::IchunSystemSet,
};

pub mod gravity_action;
pub mod jump_action;
pub mod move_action;
pub mod rotate_action;

/// Plugin that manages the character actions system.
///
/// This plugin adds systems for processing and clearing character actions.
/// Actions are processed during the Update schedule and cleared during PostUpdate
/// to ensure they only affect the character for one frame.
///
/// ## Systems Added
///
/// * `process_kcc_actions_sys` - Processes all active actions on entities (Update)
/// * `clear_kcc_actions_sys` - Clears all actions after processing (PostUpdate)
pub struct IchunActionsPlugin;

impl Plugin for IchunActionsPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            process_kcc_actions_sys.in_set(IchunSystemSet::ActionsSet),
        )
        .add_systems(PostUpdate, clear_kcc_actions_sys);
    }
}

/// Component that holds all possible actions for a kinematic character controller.
///
/// This component acts as a container for different types of actions that can be
/// applied to a character. Actions are processed in a specific order each frame:
/// 1. Rotation actions (camera/character rotation)
/// 2. Movement actions (velocity changes)
/// 3. Jump actions (vertical impulses)
/// 4. Gravity actions (gravity modifications)
///
/// Actions are automatically cleared after each frame to prevent unintended
/// carry-over effects.
///
/// ## Example
///
/// ```rust
/// use bevy::prelude::*;
/// use your_crate::actions::{KccActions, JumpAction};
///
/// // System that handles jump input and creates jump actions
/// fn handle_jump_input(
///     keys: Res<ButtonInput<KeyCode>>,
///     mut query: Query<&mut KccActions, With<Player>>,
/// ) {
///     if keys.just_pressed(KeyCode::Space) {
///         for mut actions in query.iter_mut() {
///             // Create a jump action when space is pressed
///             actions.jump_action = Some(JumpAction::new(500.0)
///                 .with_allow_in_air(false));
///         }
///     }
/// }
/// ```
#[derive(Component, Default, Debug)]
pub struct KccActions {
    /// Optional rotation action for camera/character rotation.
    pub rotation_action: Option<RotateAction>,

    /// Optional movement action for velocity changes.
    pub move_action: Option<MoveAction>,

    /// Optional jump action for vertical impulses.
    pub jump_action: Option<JumpAction>,

    /// Optional gravity action for gravity modifications.
    pub gravity_action: Option<GravityAction>,
}

impl KccActions {
    /// Creates a new, empty `KccActions` component.
    ///
    /// All action fields will be `None` initially.
    pub fn new() -> Self {
        Self::default()
    }

    /// Adds a rotation action to this component.
    ///
    /// # Arguments
    ///
    /// * `rotation` - The rotation action to add
    ///
    /// # Returns
    ///
    /// Self, for method chaining
    pub fn with_rotation(mut self, rotation: RotateAction) -> Self {
        self.rotation_action = Some(rotation);
        self
    }

    /// Adds a movement action to this component.
    ///
    /// # Arguments
    ///
    /// * `movement` - The movement action to add
    ///
    /// # Returns
    ///
    /// Self, for method chaining
    pub fn with_movement(mut self, movement: MoveAction) -> Self {
        self.move_action = Some(movement);
        self
    }

    /// Adds a jump action to this component.
    ///
    /// # Arguments
    ///
    /// * `jump` - The jump action to add
    ///
    /// # Returns
    ///
    /// Self, for method chaining
    pub fn with_jump(mut self, jump: JumpAction) -> Self {
        self.jump_action = Some(jump);
        self
    }

    /// Adds a gravity action to this component.
    ///
    /// # Arguments
    ///
    /// * `gravity` - The gravity action to add
    ///
    /// # Returns
    ///
    /// Self, for method chaining
    pub fn with_gravity(mut self, gravity: GravityAction) -> Self {
        self.gravity_action = Some(gravity);
        self
    }
}

/// System that processes all active actions on entities with `KccActions` components.
///
/// This system runs during the Update schedule and processes actions in a specific order:
/// 1. **Rotation Actions**: Applied to the entity's Transform for camera/character rotation
/// 2. **Movement Actions**: Applied to KccVelocity for movement and acceleration
/// 3. **Jump Actions**: Applied to KccVelocity for vertical impulses and jumping
/// 4. **Gravity Actions**: Applied to KccVelocity for gravity modifications
///
/// The processing order ensures that rotations are applied before movement calculations,
/// and that gravity modifications are applied last to override any conflicting effects.
///
/// ## Required Components
///
/// Entities processed by this system must have:
/// * `KccActions` - The actions to process
/// * `KccVelocity` - For velocity modifications
/// * `Transform` - For rotation modifications  
/// * `Kcc` - For character controller state
fn process_kcc_actions_sys(
    mut commands: Commands,
    time: Res<Time>,
    mut query: Query<(
        Entity,
        &mut KccActions,
        &mut KccVelocity,
        &mut Transform,
        &mut Kcc,
    )>,
) {
    for (entity, mut actions, mut kcc_vel, mut transform, mut kcc) in query.iter_mut() {
        if let Some(ref mut rotation) = actions.rotation_action {
            rotation.apply(&mut transform);
        }

        if let Some(ref mut movement) = actions.move_action {
            movement.apply(&time, &mut kcc_vel, &transform, &kcc);
        }

        if let Some(ref mut jump) = actions.jump_action {
            jump.apply(&mut commands, entity, &mut kcc_vel, &mut kcc);
        }

        if let Some(ref mut gravity) = actions.gravity_action {
            gravity.apply(&mut kcc_vel);
        }
    }
}

/// System that clears all actions after they have been processed.
///
/// This system runs during the PostUpdate schedule to ensure that all actions
/// are reset to `None` after being processed. This prevents actions from
/// carrying over to the next frame and ensures consistent, predictable behavior.
///
/// Without this clearing step, actions would continue to be applied every frame
/// until manually removed, leading to uncontrolled movement or rotation.
fn clear_kcc_actions_sys(mut query: Query<&mut KccActions>) {
    for mut actions in query.iter_mut() {
        // Clear all action fields to prevent carry-over to next frame
        actions.rotation_action = None;
        actions.move_action = None;
        actions.jump_action = None;
        actions.gravity_action = None;
    }
}