Overview
saddle-character-controller now has three layers:
- Core runtime
- simulation, state, messages, and ordering hooks
- consumes
AccumulatedInput - consumes
EnvironmentModifiers
- Input adapters
- write
AccumulatedInput - example:
adapters::enhanced_input::CharacterControllerEnhancedInputPlugin
- write
- Convenience helpers
- write
EnvironmentModifiers - provide opinionated presets or demo-oriented setup
- example:
convenience::environment::CharacterControllerEnvironmentPlugin
- write
That keeps the controller reusable in projects that do not use bevy_enhanced_input, do not want built-in swim-volume detection, or want their own tuning catalog.
Core Entity Layout
The core entity owns:
CharacterControllerCharacterControllerStateAccumulatedInputCharacterMotionStats
Optional runtime abilities remain additive:
CharacterFlyingCharacterMantleCharacterWallKickCharacterDashCharacterGravityCharacterPushExternalMotion
Swim-mode tuning now lives in convenience::environment::CharacterSwimming instead of the root API surface.
Update Pipeline
The public pipeline is exposed through CharacterControllerSystems:
ReadInputPreMovementGroundingMovementPostMovementPresentation
Within that flow, the core runtime currently does:
- Tick buffered jump / traverse / dash timers.
- Initialize newly added controllers and refresh active collider shapes.
- Depenetrate stale overlaps.
- Probe ground using the active capsule.
- Resolve support-body velocity and detach grace.
- Resolve crouch shape transitions.
- Expire stale buffered actions.
- Apply movement-mode-specific acceleration, friction, gravity, jump, flying, mantle, wall-kick, dash, and slide motion.
- Re-probe ground after motion and update support attachment.
- Publish movement messages and clear per-frame look accumulation.
- Draw optional debug gizmos in
PostUpdate.
Environment detection is no longer auto-installed by the core plugin. If you use the provided helper plugin, it schedules its volume classification in CharacterControllerSystems::Grounding.
Input Model
The core plugin never assumes a concrete input stack.
The contract is:
- some upstream system writes into
AccumulatedInput - the controller consumes that buffer on its injected simulation schedule
- buffered timers let jump / traverse / dash input survive between render-rate updates and fixed-rate simulation
The optional enhanced-input adapter keeps the old behavior, but it is explicit now:
- add
adapters::enhanced_input::CharacterControllerEnhancedInputPlugin - attach
actions!(CharacterController[..])to entities that should receive those bindings
Projects using another stack can skip that plugin and write AccumulatedInput directly.
Environment Model
EnvironmentModifiers is still the bridge between movement and world context:
depthactive_volumespeed_multiplieracceleration_multipliergravity_multiplier
The movement core reads those resolved modifiers, but it does not decide how they are produced.
The convenience environment plugin provides one implementation:
EnvironmentVolumefor generic speed / acceleration / gravity multipliersSwimVolumefor swim-mode depth classification whenCharacterSwimmingis present
Projects can replace that plugin with their own detector systems without changing the controller runtime.
Simulation vs Presentation
The controller owns logical state only:
- current movement mode
- support velocity and support entity
- environment depth and modifiers
- air jump count and dash state
- mantle target
- view orientation
The crate does not impose a camera or render-body architecture. Consumers can:
- attach a first-person camera from
CharacterLookand the configured eye height - drive a third-person follow camera from
CharacterControllerState::orientation - attach an animated mesh child that follows the logical controller
- interpolate a separate visual proxy if they want stricter fixed-step presentation
Moving Platforms
Support-body handling remains a first-class part of the runtime:
- accepted ground hits define the current support entity
- support velocity is computed from the platform's linear and angular motion at the contact point
MovementSurface::conveyor_velocitylayers on topSupportVelocityPolicyselects full, horizontal-only, or no inheritanceSupportRotationPolicyselects whether yaw from rotating supports is inheritedsupport_detach_gracepreserves momentum briefly after leaving the platform
Per-surface overrides are supported through MovementSurface::inherit_velocity_policy and MovementSurface::inherit_rotation_policy.