The mouse wheel's per-frame delta is a Vec2, so it maps directly onto
axis actions alongside gamepad sticks. hardware_to_actions now reads
AccumulatedMouseScroll in the Axis branch (max-magnitude combine); the
digital branch ignores it, and Scalar is unchanged.
Since an axis is refreshed every frame and the scroll delta is zero when
idle, on_action fires exactly while the wheel turns and the piped value
is the delta — no persistence or edge-guarding needed. Enables e.g.
`.bind(Action::Zoom, [Binding::Scroll])`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- The #[action_kind(..)] attribute is now optional and defaults to instant
(the most common kind), so bare enum variants are instant actions.
- Document the multiple-plugin pattern: add one ActionsPlugin::<T> per action
enum (gameplay/menu/audio/debug) for separation of concerns, each with its
own independent ActionState/InputMap/systems, plus the run_if(in_state(..))
approach for mutually-exclusive sets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Derive Default on ActionSource so it satisfies bevy's template system
(Template/FromTemplate are blanket-impl'd only for Clone + Default + Unpin),
letting it be spawned as a component in a bsn! scene. The bound sits on the
derived Default impl only, so the Component impl stays generic over every
GameAction — the fan-in systems keep working for non-Default actions.
- Improve #[derive(GameAction)] diagnostics: each now names the offending
variant and shows the exact expected syntax (not-an-enum, non-unit variant,
missing/duplicate/unknown action_kind, malformed action_handler).
- Add a #[cfg(test)] module exercising all four kinds + an #[action_handler]
each + ActionsPlugin + App::on_action, proving the macro codegen compiles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both remove the add_systems(.., run_if(on_action(..))) boilerplate:
- `#[action_handler(path)]` derive attribute colocates a handler with its
action variant and wires it for its kind — a gated plain system for
instant/toggle, a typed pipe (axis_value/scalar_value) plus gate for
axis/scalar. The derive generates an `impl GameActionHandlers` (empty when
no variant declares one), which ActionsPlugin calls on build.
- `App::on_action(schedule, action, system)` (AppActionExt) — the explicit,
no-macro form that keeps registration visible in main.
ActionsPlugin's Plugin impl now requires T: GameActionHandlers (satisfied
automatically by the derive).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a fourth ActionKind, Scalar, carrying an f32 from a slider's
ValueChange<f32>. It is edge-triggered like Instant but value-carrying
like Axis, completing the edge/level split (Instant/Scalar vs Toggle/Axis).
- ActionValue::Scalar(f32) + as_scalar(); ActionState scalar()/set_scalar().
- active() is now kind-dispatched: edge kinds report "this frame", level
kinds report current state.
- slider_to_action observer stores live value on every change but only
triggers on is_final, so gated consumers run once per settled change.
- scalar_value(a) typed provider pipes a bare f32 into In<f32>.
- #[action_kind(scalar)] added to the derive macro.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce engine::actions: an input-source-agnostic action layer that
fans keyboard, gamepad, and Feathers UI (Button/Checkbox) input into a
single ActionState<T> resource, so games gate systems on abstract actions
rather than raw inputs.
- GameAction trait + #[derive(GameAction)] proc macro (new engine_macros
crate) to declare each enum variant's ActionKind via #[action_kind(...)].
- Three kinds: Instant (momentary), Toggle (persistent on/off), Axis (Vec2).
- Remappable InputMap<T> with additive bind(action, [Binding, ...]).
- Symmetrical entry points over a unified ActionValue: run_if(on_action(a))
to gate any kind, action_value(a).pipe(sys) to deliver its payload;
typed axis_value/toggle_value helpers for bare Vec2/bool.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>