#bevy_enhanced_input

1 messages ยท Page 6 of 1

round portal
#

Oh, this is exactly what I'm trying to do, would you mind sharing a snippet of how you do this? I currently struggle to define context that supports both Tab + Shift and gamepad L1 R1 to navigate tabs

#

what I meant is right now how I see it I got this and I do not understand why I have to spawn Action::<Modifier> in order to use modifier binding for CycleTabBack, Modifier component feels like a third leg:

        Actions::<ModalInput>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
            let modifier = context
                .spawn((Action::<Modifier>::new(), bindings![KeyCode::ShiftLeft]))
                .id();

            context.spawn((
                Action::<CycleTab>::new(),
                bindings![KeyCode::Tab, GamepadButton::RightTrigger],
            ));
            context.spawn((
                Action::<CycleTabBack>::new(),
                Chord::single(modifier),
                bindings![KeyCode::Tab],
            ));
            context.spawn((
                Action::<CycleTabBack>::new(),
                bindings![GamepadButton::LeftTrigger],
            ));
        })),
    ));


#

is there a better way to structure this?

heady mauve
#

I think in your case you do not need the Chord in order to use Shift with Tab.

#

Hold on, let me think about this...

#
        Actions::<ModalInput>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
            context.spawn((
                Action::<CycleTabBack>::new(),
                bindings![(KeyCode::Tab).with_mod_keys(ModKeys::SHIFT), GamepadButton::LeftTrigger],
            ));
            context.spawn((
                Action::<CycleTab>::new(),
                bindings![KeyCode::Tab, GamepadButton::RightTrigger],
            ));
        })),

I think this will work, but the KeyCode::Tab might need to be adjusted a bit. My BEI is a bit rusty.

sharp warren
#

Yes, that's how I'd do it.

Fan fact: in Unreal they don't provide a built-in support for keyboard modifiers, so you always need to create a chord for any key combination.

heady mauve
#

Does with_mod_keys support side unique modifiers like Left or Right Shift only? I haven't looked through the code but the documentation seems to only designate the type of modifier, not side.

sharp warren
heady mauve
sharp warren
heady mauve
#

Okay. I was thinking to use that approach for the values. But that brings up another possible issue. Currently SHIFT is defined as 0b00000010 while I'd like to define it to equal both SHIFT_LEFT and SHIFT_RIGHT so 0b00100010. This would cause a change in how things have been serialized and de-serialized where the previous combined modifier would now only be the left versions. If that is the direction we want to take, would I indicate the change in the Changelog or do we have any separate migration documentation that needs to be updated as well?

sharp warren
heady mauve
#

You know, I updated the test to include the sided modifiers and didn't even process that it was a string.

round portal
frigid holly
#

I'm trying to get a specific behavior with BEI and not sure how to go about it:

  • Grid-based move input with Ordinal::numpad()
  • Pulse condition with trigger_on_start: true
  • I'd like the input to additionally fire any time the user hits a numpad key, even if one of the keys is already down (so: fire on input changes and re-start the pulse)
#

perhaps I should just re-implement Pulse with a custom InputCondition::evaluate for my needs?

#

the problem is basically if the user hits another key within the initial_delay period of the Pulse condition, the input doesn't fire again even if the "cooldown" (i.e. interval) is already up

#

so the input feels weird and unresponsive

sharp warren
frigid holly
#

alrighty, a custom Pulse seems somewhat easier so i'll try that, thanks

sharp warren
#

I'd like to hear user opinions.

We have modifiers represented as bitflags, and right now they work similarly to how OSes handle them.

For example, you can attach SHIFT to a binding, and it will mean "any shift (left or right)".
So CTRL | SHIFT means Any Ctrl + Any Shift.

We decided to extend the bitset by adding side-specific modifiers. PR: https://github.com/simgine/bevy_enhanced_input/pull/301

It adds *_LEFT and *_RIGHT variants and makes the existing variants equal to *_LEFT | *_RIGHT. While this works, it makes the bitset behave inconsistently:

  1. OR-ing different modifiers means "all of them should be pressed". For example, CTRL_LEFT | SHIFT_LEFT means Left Ctrl + Left Shift.
  2. OR-ing two sides means logical "any of". For example, CTRL_LEFT | CTRL_RIGHT means Any Ctrl, to preserve the current functionality.

This inconsistency is why I'm unsure about the change.

For example:

  • CTRL_LEFT | CTRL_RIGHT | SHIFT_LEFT means Any Ctrl + Left Shift.
  • ModKeys::pressed returns SHIFT if the user presses both Shift keys, which is equivalent to Any Shift. I'd expect it to return something that represents Both Shifts.
GitHub

This PR adds the ability to use only the left or right modifier key instead of only allowing the use of both. This most often is an issue when creating simulation games that have a lot of keybindin...

ionic sundial
# sharp warren I'd like to hear user opinions. We have modifiers represented as bitflags, and ...

Yeah 2. Above is a bit unexpected to me.

If I have Ctrl + Shift + P, I expect I need to press all 3 keys to trigger it.

If, all of a sudden, LeftCtrl + RightCtrl + LeftShift + RightShift + P means the same thing, that seems unexpected because it reads as โ€œuser needs to press all 5 keys to triggerโ€ but I think youโ€™re saying this particular example is the same as Ctrl + Shift + P.

heady mauve
#

I think if you want to prevent (CONTROL_LEFT | CONTROL_RIGHT) == CONTROL then the solution probably needs to move away from bitflags for how they are represented. Having bitflags for either as well as sided modifiers would require checking for exclusivity between them. I think an enum that has the exclusive variants might be a possible solution:

enum ModSet {
    #[default]
    Either,
    Left,
    Right,
    Both,
}

struct ModKeys {
    shift: Option<ModSet>,
    control: Option<ModSet>,
    alt: Option<ModSet>,
    super: Option<ModSet>,
}

If you still want easy modifier creation, perhaps using a DSL based on how they are usually represented would be best: the modifier itself like "Ctrl" means either, the modifier with direction indicates the side such as "Right Alt", and if both are listed like "Left Super + Right Super" would mean both. This would also move away from possible issues with bit operations.

sharp warren
heady mauve
# sharp warren Do we want to support modifiers from different sides, like `LEFT_CTRL + RIGHT_SH...

I think since you are concerned with (CONTROL_LEFT | CONTROL_RIGHT) having confusing meaning that you already acknowledge the possibility someone will try to use it or other combinations of different sided modifiers and I agree that should be considered. My mouse has programmable buttons and can output either side of modifier keys and input devices that follow the USB HID Keyboard specification to output key presses should be able to do the same.

While I don't think it will be common I do think if any games wants to support user defined keybindings then we probably should plan to allow any valid set of bindings. The fact that third party software like AutoHotkey, Joy2Key, and Joystick Gremlin exists to make it possible to do bindings that were not valid within games or other software shows that users will try to find a way.

sharp warren
# heady mauve I think since you are concerned with `(CONTROL_LEFT | CONTROL_RIGHT)` having con...

Users aren't limited by what ModKeys support. They always can create a Chord for those rare cases.
ModKeys exists to reduce boilerplate for common cases. Originally I thought extending it to side-specific modifiers will be trivial, but I haven't considered the mentioned inconsistencies ๐Ÿค”
I'm fine with making the struct more complicated, but I want to make sure we won't introduce weird behavior to other parts of the API.

heady mauve
# sharp warren Users aren't limited by what `ModKeys` support. They always can create a `Chord`...

Is there a reason for the separation between ModKeys and Chord? If there is a benefit to using ModKeys over Chord then I think that benefit should apply to even the rare cases otherwise it might cause further confusion to developers as to why certain modifier combinations work with ModKeys while others don't seem to. I think you previously mentioned that Unreal doesn't provide built in support for keyboard modifiers so in that case it makes me wonder if perhaps ModKeys is an extra API that creates two sets of input modifiers that points to a possible underlying issue.

sharp warren
round portal
sharp warren
round portal
floral vector
#

I'm trying to add controller input, but I'm a little confused on what Swizzle does.

#

Can somebody explain how it works in this example?:

(KeyCode::KeyW, SwizzleAxis::YXZ),
ember shore
floral vector
#

Also, judging from the docs, I'm probably going to have to redo my inputs. Currently, each movement direction is its own Action, while it seems that the intended usage is a single "Movement" Action that encompasses all of them.

ember shore
sharp warren
ember shore
#

Sometimes having each direction be a discrete input is preferable. a lot of these libraries have a lot of extra bells and whistles that you may not need. I think it's important to know what tools are available but don't feel like you have to use it just because it's there. If it makes more sense for your control scheme to be able to remap up, down, left, right for example then just do that. You can also have multiple physical inputs bound to the same thing like I have games that accept WASD, arrow keys, analog sticks etc under one Action. It may or may not be preferable to do it that way but it's possible.

floral vector
#

Whereas GamePad has just x and y representing two directions per axis.

#

Basically, I'm back to thinking if I need to redesign my input system.

#

Unless there's some way to associate only the positive or negative values of an axis (possibly as an absolute value).

ember shore
sharp warren
#

Anyone interested in writing a controller example for BEI using pull-style API? ๐Ÿ™‚

round portal
sharp warren
#

On the internet there is a bunch of mario-style controllers, it would be nice to adapt one of them, for example.

#

Having a proper complex controller for Bevy as an example would be great.

#

Just listing options, I'd welcome any example ๐Ÿ™‚

#

I'm just a bit lazy It's a great opportunity for new contributors to learn

round portal
ionic sundial
#

Is there an easy answer to the question: "In what schedule, or where, should I enable/disable my actions?" (as in, add/remove Disabled component).

Tension: If you call mock_once and then immediately disable the action (after BEI's systems have run), BEI's update system will not see this action until you enable it again. So if you have that action disabled for 27s (for example), at the end of 27s the action enables again, BEI's update system now sees it and the mock you called 27s ago runs, even though it was supposed to be a "once" mock that only ran one time.

#

Right now, Disabled seems to act a bit like "pause" for BEI actions in some cases. But I wonder if it should instead be more like "stop" or "reset". So, for example, in this case, it would actually reset any pending or in progress action mock. I haven't thought through the consequences of doing this, just thinking out loud.

round portal
#

I'd expect Disabled adding ContextAvtivity::<MyContext>::INACTIVE. IIUC it prevents BEI from listening to the input altogether, right?

ionic sundial
sharp warren
sharp warren
ionic sundial
ionic sundial
sharp warren
ionic sundial
# sharp warren I think if you want this behavior, you can additionally reset the mock ๐Ÿค”

Hmmm yes maybe I can. Yeah I probably can sort it out on my end. I wrote a test case where I ran 1000 updates and that โ€œonceโ€ mock was still just sitting there and on frame 1001 after Disabled was removed, and the action was โ€œunpausedโ€ the โ€œonceโ€ mock ran, and so it stood out to me as possibly weird so I thought Iโ€™d ask here. But Iโ€™m happy to leave it to being solvable in user land for now.

sharp warren
floral vector
#

How do I translate right axis to mouse movement?

sharp warren
floral vector
#

Right stick, yes.

#

In fact. I change my question. How do I Bind MouseMotion?

#

Is it also a Vec2?

#

I'm trying to tie MouseMotion and the right stick to the same Action.

sharp warren
sharp warren
#

Check examples for how to map multiple bindings to the same action.

floral vector
#

I was able to tie them together, and while the Gamepad stick feels fine, the mouse feels weird, like dragging it trough mud.

#

Is there a way to get something like MouseMotion.delta()?

sharp warren
#

You can adjust the sensitivity by adding a modifier

#

Check the basic_action_management example

floral vector
#

Still feels weird. I decided to just separate the two inputs.

graceful tapir
#

So, this doesn't seem to be getting triggered.

(Action::<CameraScroll>::new(),bindings![Binding::mouse_wheel()]),

#[derive(InputAction)]
#[action_output(f32)]
pub struct CameraScroll;
fn camera_zoom(camera_scroll: On<Start<CameraScroll>>, mut spring_arm: Single<&mut RayCaster, With<PlayerSpringArm>>,) {
    spring_arm.max_distance += 5. * camera_scroll.value.signum()
}
#

Which is a bit confusing.

sharp warren
graceful tapir
#

Jeez triggers are slow

#

i'm never using that for camera movement

#

Makes me wonder if there might be input latency for buttons/keys

sharp warren
graceful tapir
# sharp warren What do you mean?

Anything that is a constant input like mouse motion results in bevy stuttering to activate the trigger, and results in the camera moving in disjointed sections

#

Only thing I can imagine it to be is that triggers are slowing it down, so I use Single<&Action<>> instead

#

Triggers have also been said to be slow by others as well

outer wyvern
sharp warren
#

I use only observers in my game just fine ๐Ÿค”

ionic sundial
#

Has anyone come up with a good solution for stopping typing in bevy inspector egui's text box to search for an entity triggering BEI actions?

graceful tapir
graceful tapir
sharp warren
ionic sundial
sharp warren
sharp warren
#

I would include an example for it as well, but it requires depending on egui.
Even when it's in dev-dependencies, it prevents me from following RC.
But it's simple, just instead of Query, you check its resources.

ionic sundial
#

Perhaps if not an example, adding to the quick start could be an option if that interests you.

sharp warren
#

I think when we upstream it, it would be better for egui to provide an example.

graceful tapir
# sharp warren Not sure If I get you. If you having problems with observers, you need to provid...
#[derive(InputAction)]
#[action_output(Vec2)]
pub struct MoveCamera;
#[derive(Component)]
pub struct PlayerCameraPivot;
#[derive(Resource)]
pub struct CameraValues {
    pub sensitivity: f32,
    pub rotation: Vec2,
}
//spawn this under any input context
(Action::<MoveCamera>::new(),bindings![Binding::mouse_motion()]),

fn camera(
    move_camera: On<Start<MoveCamera>>,
    mut camera_pivot: Single<&mut Transform, With<PlayerCameraPivot>>,
    mut camera_values: ResMut<CameraValues>,
    time: Res<Time>,
) {
    let sensitivity = camera_values.sensitivity;
    camera_values.rotation -= move_camera.value * sensitivity * time.delta_secs();
    camera_values.rotation.y = camera_values
        .rotation
        .y
        .clamp(-89f32.to_radians(), 89f32.to_radians());
    camera_pivot.rotation = Quat::from_rotation_y(camera_values.rotation.x)
        * Quat::from_rotation_x(camera_values.rotation.y);
}
graceful tapir
# ionic sundial Perhaps if not an example, adding to the quick start could be an option if that ...

This?

pub fn pointer_check(mut actionsources: ResMut<ActionSources>, egui: Res<EguiWantsInput>) {
    if egui_wants_any_pointer_input(egui) {
        actionsources.mouse_buttons = false
    } else {
        actionsources.mouse_buttons = true
    }
}
pub fn keyboard_check(mut actionsources: ResMut<ActionSources>, egui: Res<EguiWantsInput>) {
    if egui_wants_any_keyboard_input(egui) {
        actionsources.keyboard = false
    } else {
        actionsources.keyboard = true
    }
}
ionic sundial
# graceful tapir This? ```rs pub fn pointer_check(mut actionsources: ResMut<ActionSources>, egui:...

Awesome thanks for sharing. Here's what I did which works well enough for now (system is verbose but I didn't make any effort to shorten it):

app.add_systems(
    PostUpdate,
    toggle_action_sources
        .after(EguiPostUpdateSet::ProcessOutput)
        .run_if(resource_changed::<EguiWantsInput>),
);

fn toggle_action_sources(
    egui_wants_input: If<Res<EguiWantsInput>>,
    mut action_sources: If<ResMut<ActionSources>>,
) {
    if egui_wants_input.wants_any_pointer_input() {
        if action_sources.mouse_buttons == true {
            action_sources.mouse_buttons = false;
            debug!("Disabled mouse button input");
        }
        if action_sources.mouse_motion == true {
            action_sources.mouse_motion = false;
            debug!("Disabled mouse motion input");
        }
        if action_sources.mouse_wheel == true {
            action_sources.mouse_wheel = false;
            debug!("Disabled mouse wheel input");
        }
    } else {
        if action_sources.mouse_buttons == false {
            action_sources.mouse_buttons = true;
            debug!("Enabled mouse button input");
        }
        if action_sources.mouse_motion == false {
            action_sources.mouse_motion = true;
            debug!("Enabled mouse motion input");
        }
        if action_sources.mouse_wheel == false {
            action_sources.mouse_wheel = true;
            debug!("Enabled mouse wheel input");
        }
    }
    if egui_wants_input.wants_any_keyboard_input() {
        if action_sources.keyboard == true {
            action_sources.keyboard = false;
            debug!("Disabled keyboard input");
        }
    } else {
        if action_sources.keyboard == false {
            action_sources.keyboard = true;
            debug!("Enabled keyboard input");
        }
    }
}
graceful tapir
ionic sundial
graceful tapir
#

past that assigning just has the cost you'd expect

graceful tapir
#

Apologies, used to old observers

spice knot
#

is it somehow possible to add actions to a context from different systems, currently I have the following systems but they seem to replace the actions for the Player context instead of adding both sets of actions:

fn add_player_input_actions(
    e_player_added: On<Add, Player>,
    mut commands: Commands,
) {
    info!("Adding player input actions.");

    commands.entity(e_player_added.entity).insert(actions!(
        Player[
            (
                Action::<WalkVertical>::new(),
                Bindings::spawn((Bidirectional::new(KeyCode::KeyW, KeyCode::KeyS), Bidirectional::new(KeyCode::ArrowUp, KeyCode::ArrowDown)))
            ),
            (
                Action::<WalkHorizontal>::new(),
                Bindings::spawn((Bidirectional::new(KeyCode::KeyD, KeyCode::KeyA), Bidirectional::new(KeyCode::ArrowRight, KeyCode::ArrowLeft)))
            )
        ]
    ));
}

fn add_player_input_actions_open_inventory(
    e_general_controls_added: On<Add, Player>,
    mut commands: Commands,
) {
    commands
        .entity(e_general_controls_added.entity)
        .insert(actions!(
            Player[(Action::<OpenInventory>::new(), bindings![KeyCode::KeyI])]
        ));
}
sharp warren
ionic sundial
# sharp warren I'm not against simplifying it somehow by extending the API, but I think `Disabl...

Iโ€™m going to try adding a user land version of mock_once that does nothing if the action is Disabled and see how that design works for me. I am disabling actions at the start of every frame (as I once said), but inside a frame, itโ€™s so easy to call mock_once on AI entities but forget to check if the action youโ€™re mocking is even enabled. Even if you do remember to check if the action is enabled, it feels unergonomic to have to fetch the actionโ€™s Disabled state just before calling a method on commands. I guess I might call it mock_once_if_enabled.

No action needed here, just sharing my current thinking.

sharp warren
ionic sundial
# sharp warren But if the action is disabled, you won't see it in queries, no?

Iโ€™d use Has<Disabled> when querying.

Note: maybe to help visualise this, imagine a system that runs every 1s and for each AI entity it cycles through each of the actions it has. Each time it picks one action, it calls mock_once. But in the case the action is disabled, we get in the weird state I previously described. So instead I wrap the mock_once call in a check to only do it if the action is enabled (and that data comes off a query on this system that uses Has<Disabled>).

sharp warren
#

Has<Disabled> makes the query see the disabled actions.

#

Without Has<Disabled> it will skip disabled entities automatically.

ionic sundial
# sharp warren But why use `Has<Disabled>` if you don't want to mock disabled actions? ๐Ÿค”

I guess not every call to mock_once originates from action query data.

Imagine some logic that triggers an action after 10s. Well, I donโ€™t need to have queried for the action, so I wonโ€™t know if itโ€™s disabled or not. All I need access to in order to call mock_once is the context entity.

To make it clearer: Imagine a unit was told to charge after 10s. I can just trigger the action using mock_once: thereโ€™s no action query data in sight.

sharp warren
ionic sundial
# sharp warren Not sure if I get it, could you elaborate? I would imagine if you have `Has<Dis...
fn on_selecting_magic_item_target_started(
    start: On<Start<SelectingMagicItemTargetAction>>,
    mut commands: Commands,
    charge_magic_item_action_query: Query<
        &ActionOf<SelectingMagicItemTargetContext>,
        With<Action<ChargeMagicItemAction>>,
    >,
) {
    let magic_item_entity = start.context;

    commands
        .entity(magic_item_entity)
        .try_insert(ContextActivity::<SelectingMagicItemTargetContext>::ACTIVE);

    // If the magic item has a charge magic item action, trigger it because
    // starting a selecting magic item target action also starts charging the
    // magic item (if it can be charged).
    if charge_magic_item_action_query
        .iter()
        .any(|action_of| **action_of == magic_item_entity)
    {
        commands
            .entity(magic_item_entity)
            .try_mock_once::<SelectingMagicItemTargetContext, ChargeMagicItemAction>(
                TriggerState::Fired,
                true,
            );
    }
}
  • Not sure if the following gives enough context to you on the action/context setup, but:
  • SelectingMagicItemTargetAction is an action in the magic item's root context
  • The observer activates SelectingMagicItemTargetContext
  • But immediately also fires off a ChargeMagicItemAction action (which is an action in SelectingMagicItemTargetContext)
  • In my PreUpdate system I will disable ChargeMagicItemAction if it cannot be fired (e.g., not enough mana)
  • But in this observer, I don't currently check if ChargeMagicItemAction is actually enabled/disabled so right now, this try_mock_once is going to incorrectly sit "queued" in BEI until the action becomes enabled.
  • My two options for fixes are a) Write my own try_mock_once_if_enabled, or b) wrap the try_mock_once call in a check that first queries for the action's Disabled state.
#
  • The general pattern here is just: try_mock_once::<C, A> can be called from anywhere, and you needn't have fetched action A beforehand.
#
  • To be clear: I know this example does have a query charge_magic_item_action_query and therefore it does have a gate on it, but not every call to try_mock_once has to look like this.
sharp warren
#

Ah, I get it now!
I'd check for Disabled. But I'd also consider moving the gameplay logic (like the check for mana) outside of BEI.

ionic sundial
# sharp warren Ah, I get it now! I'd check for `Disabled`. But I'd also consider moving the gam...

I'm using BEI heavily for AI actions. The features that are good so far are:

  • Disabled: I can just make an action disabled if all the conditions are not true for it to be fire-able.
  • Start/Complete events: These are really handy.
  • Parity between AI/player unit actions: More or less, players and AI are the same it's just that players have bindings / AI has action mocks enabled.

I mention this because the mana check is done as part of working out if the BEI "use magic item" or "charge magic item" action can even be enabled, and so the BEI action having all of the above features is providing a lot of value in achieving that and not having to have two different ways of doing gameplay logic or enablement checks. By this I mean that players have many actions that are definitely BEI because they are triggered by bindings. AI have the same actions just triggered by mocks. Using/charging magic items is yet another action that a player can do and therefore AI can do, and so currently there's a strong link between gameplay logic / enablement and BEI actions. Whether that's the right approach I guess I'll find out, or maybe I will be convinced otherwise ๐Ÿ™‚

(To give you an idea on what it looks like, this WIP system runs before BEI every frame and you can see that it just amounts to working out if an action should be enabled or not: https://gist.github.com/mgi388/917653868844aaa8237b0dff9e6c183d)

sharp warren
sharp warren
round portal
sharp warren
#

@inner sky why doing character controller in Update is antipattern? ๐Ÿค”

inner sky
inner sky
#

Could you take a look at it? It's a bit hard to summarize ๐Ÿ˜„

#

otherwise I would do so

sharp warren
inner sky
#

also, "physics" is relative here. A 2D game with top-down controls will also run into problems with tunneling if you don't use deltatime

#

so that already counts as "physics" for our definition

#

Bevy in general is kinda bad at teaching people that game logic fundamentally belongs in a fixed timestep, and this example would make that situation a little bit worse by suggesting that a character controller is fine in Update and presenting code that you would have to modify in cumbersome ways to get the actual code you want

#

especially the second part is what irks me, since it makes it hard for people that want to do it right

#

IMO better to show the cumbersome API and be honest about it rather than showing the simple API that won't work on machines with low FPS

#

that, or change the example to just show the pull-based API in a vacuum outside the context of character controllers

#

Anyhoot, I'll leave it up to your call of course ๐Ÿ™‚

#

No harsh feelings if you disagree or anything!

heady mauve
#

In my opinion, since BEI is slated to be upstreamed I think it would be better to show best practices as well.

sharp warren
inner sky
#

here's how I do it with push-style

sharp warren
#

Got it! Here is what I've been thinking:

  • Rework our local_multiplayer example into a snake game with 2 snakes. In this kind of game we can mention that it's ok to use Update. It also looks fun.
  • Add pull_style_api that mirrors basic_action_management, but uses pull-style API.
  • Write a proper character controller example instead of the current one.
inner sky
#

Note that if you do pull-style, the system should be in PreUpdate after the BEI system set, as FixedUpdate happens before Update. So if you pull in Update, you will get a one-frame lag.

inner sky
sharp warren
inner sky
#

bit less hassle

#

yeah comment totally works ๐Ÿ™‚

sharp warren
inner sky
inner sky
#

@sharp warren FYI you did a little oops on 0.24: ActionState is correctly a deprecating alias to TriggerState, but it's not in the prelude, so updates don't point you in the correct direction

#

i.e. I got a compilation error, had to import the type now, compile again, and then I got the deprecation warning

heady mauve
inner sky
heady mauve
#

That makes sense.

heady mauve
#

I don't have enough experience with deprecation in libraries. If I remove the warning in the library by adding #[allow(deprecated)] above ActionState in the prelude will it also remove the warning for downstream users of the library?

inner sky
#

#[allow] is always just for your own linting, it does not get propagated

heady mauve
#

That's good to know.

sharp warren
sharp warren
#

Tsudico submitted the fix and I drafted a patch ๐Ÿ™‚

ionic sundial
#

@inner sky I am trying to understand more about how you are doing input accumulation in bevy_ahoy regarding your NPCs/AIs.

Your BEI context is registered to the PreUpdate schedule so when you call mock_once 1 this inserts ActionMock, and then BEI will process that in PreUpdate and fire the observers in PreUpdate. Now, I know that this is effectively the same as when a player presses a binding (the point of it is to be a mock!), and input accumulation will run no matter if it's a physical binding-based action or a mocked on.

But what I am having trouble wrapping my head around is the fact that you now have some code running in a non-deterministic schedule for your AI (edit: right?). But couldn't you just have your AI system update your AccumulatedInput component directly and have it update it in a FixedUpdate system?

To be fair, I can't quite put my finger on what could go wrong by relying on your AI action being driven by PreUpdate, but your AI will never be in a situation that will miss inputs (because your code essentially sets them), and you've already proved that you can bypass BEI with the AccumulatedInput, hence: Can you just set it directly for AI?

inner sky
#

there's no non-determinism here IMO since PreUpdate is the next frame. There is a one frame delay here however. That is annoying when it happens to users, but I figured it doesn't matter for AI

#

But yeah it's entirely possible that this specific design would be more elegant by setting stuff directly, or having convenience methods on AccumulatedInput ๐Ÿ™‚

ionic sundial
#

One reason I came across this is that I was really keen on using mocking because it allowed me to get the BEI machinery for free: I could just do stuff in BEI Complete observers. But once I realised those observers fire in PreUpdate I realised that they should never be used for game logic (also said in https://github.com/simgine/bevy_enhanced_input/issues/106 so this is not new information).

So then, I have all of these AI mock_once calls that ultimately make observers fire, and inside that where I currently do game logic I need to change that to instead do input accumulation.

But now we've come full circle. The AI should just set the input accumulation directly.

And also worth noting when you use an ActionMock things like conditions are disabled, so I'm now thinking maybe this round about way of AI setting their action state is not as useful as I thought.

inner sky
#

you're absolutely right

#

I should change that

#

thank for sharing your thoughts on this

ionic sundial
# inner sky But yeah it's entirely possible that this specific design would be more elegant ...

I still don't have a concrete argument to say it's right or wrong, but I'm also thinking of pausing the game. I believe you can get far with a pause implementation by pausing FixedTime. 1) In the bevy_ahoy example your NPC update system is in Update so you can't easily pause that without using other state. 2) If you rely on ActionMocks/mock_once that will go in PreUpdate and you also now cannot rely on pausing those BEI events firing.

inner sky
#

eek

#

that's wrong haha

#

should have checked that better when I reviewed it

ionic sundial
inner sky
#

I need to clean up bits there anyways

#

thanks for the heads-up

ionic sundial
#

No worries. Thanks for the input yourself. I'm going around in circles on this stuff but I think slowly retaining the context needed to get it right.

#

Thought I was on to something with using BEI observers all over the place against mocked actions, but I've got some rewriting to do I think.

#

BTW pausing FixedTime is a litmus test I've been using lately to try and work out if my design / game logic is right and that's what made me think of the AI/NPC thing leaking out into PreUpdate as unpausable.

(Assuming it's the right thinking and noting maybe it doesn't apply to every game).

graceful tapir
#

If I want to insert a ContextActivity can I just spawn it anywhere for the effect to apply?

ionic sundial
inner sky
sharp warren
graceful tapir
high compass
#

is it possible to manually trigger an action without binding? eg I want a button clickable in ui with also an optional shortcut.

#

same applies for eg picking if you want to have action that's trigerred both my shortcut and clicking. do I add additional indirection instead?

#

I guess I should just add indirection.

sharp warren
analog mist
#

Hiya, I'm fairly new to this crate and I'm wondering what the best way is to program an orbiting camera using this? As in, when holding right click, mouse motion should spin the camera?
I tried using context switching, and while it works mostly great, there is an issue where any smoothing just abruptly cuts off if I let go of right click early as it switches off the context activity. Is there a better way to do this, by chance?

analog mist
#

Okay, realised what I was actually looking for for this "right click and drag" behaviour was Chord. Still don't know how to get it to sort of carry the momentum with the smoothing thing, but that's whatever

Code below in case anyone else comes across this :]

fn setup(mut commands: Commands) {
    info!("Spawning camera.");
    commands.spawn((
        Transform::from_xyz(5., 0., 0.).looking_at(Vec3::ZERO, Vec3::Y),
        OrbitCamera::default(),
        Actions::<OrbitCamera>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
            let modifier = context
                .spawn((
                    Action::<OrbitModifier>::new(),
                    bindings![MouseButton::Right],
                ))
                .id();

            context.spawn((
                Action::<Orbit>::new(),
                DeltaScale::default(),
                SmoothNudge::new(16.),
                Scale::splat(1.5),
                Chord::single(modifier),
                bindings![(Binding::mouse_motion(), Negate::all())],
            ));

            context.spawn((
                Action::<Zoom>::new(),
                Scale::splat(0.2),
                SmoothNudge::new(12.),
                bindings![(Binding::mouse_wheel(), SwizzleAxis::YXZ)],
            ));
        })),
    ));
}
sharp warren
tardy scroll
#

Hello! Do you have any aspect of the original plugin that the crate won't be really inspired by?

sharp warren
tardy scroll
#

When you read here and work on the crate, what's "meh" for you?

sharp warren
tardy scroll
#

Do you have any existing solution to use bindings-bindings pair directly for Bidirectional? So it goes something like

let bindings_forward = bindings![KeyCode::KeyW, KeyCode::ArrowUp];
let bindings_back = bindings![KeyCode::KeyS, KeyCode::ArrowDown];
// ...
Bidirectional::new(bindings_forward, bindings_back)

(I may be sleepy, so I might have missed some in the documentation; My apologies in that case)

sharp warren
outer wyvern
sharp warren
# outer wyvern

Looks good!

Regarding BEI, I assume you're busy this release cycle? If yes, it's fine to wait. Maybe I can make some BSN-related improvements after 0.19.

outer wyvern
sharp warren
#

Particularly, I'm thinking about utilizing templates for presets

#

And curious if I can optimize how it can be defined inside BSN. Maybe we won't need actions! and bindings! ๐Ÿค”

outer wyvern
#

The fewer macros the better

#

Both for maintenance, and in terms of helping people identify the right mental model for what's going on

sharp warren
outer wyvern
#

Maybe: we'll have to play with the exact traits in practice to see if that's viable IMO

tardy scroll
# outer wyvern

Speaking of the guide, hypothetically (assuming BEI is upstreamed), if both features are enabled, would BEI provide, for example, EnhancedFreeCameraPlugin?

  • If so, and the guide doesn't mention this, should we include a note about it (encouraging crate-to-crate compatible solution)?
  • Who should provide it by the way?

#1459437342409359504 message

outer wyvern
#

There's no need for anything more complex

#

Probably worth adding a bit of a note about how integration work should be done once upstreaming is complete

tardy scroll
#

It could be bizarre especially more than two crates

tardy scroll
# outer wyvern How so?

i meant agreeing that, this would lead to for example discussion over XY XZ XYZ plugins for X, Y, and Z plugins

outer wyvern
sharp warren
visual hornet
#

I migrated a project from leafwing-input-manager to BEI this week. I'd hoped to find the repository that hosted #1297361733886677036 message to compare notes on my total blundering approach, NiseVoid, I think?

sharp warren
visual hornet
tardy scroll
#

This crate made me want to have add_observers so that i can:

impl Plugin for FooPlugin {
  fn build(&self, app: &mut App) {
    app.add_observers((a,b,c,d,e,f,g,h,i,j,k));
  }
}
sharp warren
tardy scroll
#

It'd be just my lazy ass. Hmm... If that's the case, I can't assert clearly. Aren't we given any compile time threshold (neither a formal methodology) for this particular section?
It'd be more efficient to check past relevant issues first.

sharp warren
fast sedge
#

What would be the best way to get the current highest ContextPriority value?

sharp warren
royal steeple
#

Hi

#

I want to increment key binding by 1 instead of smooth movement forward

#

like snaped 1 step

#

forward

#

each key binding

#

instead of this behavior I want to step one square ahead

sharp warren
royal steeple
#

snap in the center of each square on move

#

is this an option in the crate or should I make it ?

sharp warren
royal steeple
#

I want to try

#

thanks

#

I working first on limit my move to blue squares

royal steeple
#

how does DeadZone works?

royal steeple
#

can i speed up the complete event Observer?

#

now it is working by use a Boolean and Complete event by it take time as you can see :

royal steeple
#

I am using a Res<Time> instead of complete event to tweak release move

tardy scroll
#

Have you thought about exposing ContextInstances when a dev/debug feature is on?

tardy scroll
#

Oh sorry! It's to the maintainers.

royal steeple
sharp warren
sharp warren
tardy scroll
#

Well, In that logic, i don't need to work on development tooling since i don't have such scale of game to dealt with tooling

#

lol

sharp warren
#

It has a lot of stuff that doesn't reflect nicely, so I'm not sure

tardy scroll
#

it's ok. i can bottle it down

sharp warren
#

We can add reflect ignore for most fields ๐Ÿค”
If you open a PR, I'll accept it

tardy scroll
#

Cool! I'll get back to it in a few days.

tardy scroll
#

Do you mean like this

royal steeple
# sharp warren I'm not sure what you mean

I mean when you push a button and release it it take too much time to get to complete event change the boolean and make the use push again. can we minimize this time?

sharp warren
sharp warren
royal steeple
#

ok

royal steeple
#

does Release::default() works with #[derive(InputAction)] #[action_output(Vec2)] pub(crate) struct Movement;

#

with boolean is fine

#

but with Vec2 not

sharp warren
#

You will need to configure the actuation.
It computed as length of the value (Vec2 in your case).

royal steeple
#

I am using Pulse

#

and it works thanks ๐Ÿ˜„

thick kiln
#

Is ActionMock now added by default to all Action entities?

oak monolith
#

Hi! I wanted to implement state-context integration in my project but I soon realized that it won't work properly with SubStates. I get a panic, probably because the observer that syncs the ContextActivity expects the state resource to exist, and this is not always true with SubStates. Is this something that will be supported in the future? I don't know how difficult to implement this could be, but I can always take a stab at it provided I get some help and it's something people would find useful. Thanks, and have a great day!

sharp warren
oak monolith
#

Sure. I'll take a look at the code later and if I can't figure it out I'll open an issue. Thanks for your reply ๐Ÿ˜

zinc cliff
#

Does it make sense to re-use contexts with different actions?

#

Like I want to be able to swap cameras (which also means swapping ContextActivity::<_>::INACTIVE/ACTIVE for inputs)

#

but right now its a bit of a pain with different context types

#

I guess actually I could just throw an ActiveCamera component on and observer change ContextActivity...

#

sorry accidentally rubber ducked here

tardy scroll
tardy scroll
oak monolith
# sharp warren Looks like we forgot to make the state access optional in `state.rs`. Could you ...

I gave this a bit of thought after glancing at the code and I think there might be a case (maybe more than one) where the optional States resource can be None when sync_on_insert observer triggers. I'm thinking a sub-state whose source State was not yet set, but ActiveInStates was just inserted.

Let's say we have an entity with some bindings that gets spawned in a system running On(State::First)with an ActiveInStates<Context, SubState::SomeVariant> component. If said SubState source is State::Second it could trigger this issue, right?

Would this be concerning? We can always add some warning log if this happens. I haven't tested this though, as soon as I have some time I'll implement the change and verify if my hypotetical case even triggers. Wanted to think out loud as I might be overthinking. ๐Ÿ˜…

sharp warren
sharp warren
#

Anyway, I reviewed the PR ๐Ÿ™‚

tardy scroll
#

it wasn't. haha. It should help though.

sharp warren
tough harness
#

When running the following line on a barebones project with DefaultPlugins and no changes to window, no errors show, the process starts and a tab is created on the taskbar, but the window itself is invisible, unless I remove the .finish(); Running on linux with Wayland
app.add_plugins(EnhancedInputPlugin).add_input_context::<Character>().finish();

sharp warren
heady mauve
tough harness
oak monolith
tardy scroll
sharp warren
tardy scroll
#

i thought it was convenient. Let's iterate with ignore attribute

sharp warren
#

I don't think we need it ๐Ÿค”

#

Missed your message on GitHub somehow.

tardy scroll
#

That's why we have this Discord server!

tardy scroll
#

hmm how would you like to have this lifetime shortname for reflect?

sharp warren
tardy scroll
#

lol i thought short_name.rs was under context/

tardy scroll
#

Bueno? (2nd iteration)

#[derive(Debug)]
#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Default))]
pub struct ContextInstance {
    pub(super) entity: Entity,
    #[cfg(not(feature = "reflect"))]
    pub(super) name: ShortName<'static>,    
    #[cfg(feature = "reflect")]
    pub(super) name: String,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    type_id: TypeId,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    priority: usize,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    is_active: fn(&Self, &FilteredEntityRef) -> bool,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    actions: for<'a> fn(&Self, &'a FilteredEntityRef) -> Option<&'a [Entity]>,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    actions_mut: for<'a> fn(&Self, &'a mut FilteredEntityMut) -> Option<Mut<'a, [Entity]>>,
}
sharp warren
#

And I'd remove Debug. It won't print nicely due to function pointers.

tardy scroll
#

sรญ o no (3rd iteration)

#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Default))]
pub struct ContextInstance {
    pub entity: Entity,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    pub name: ShortName<'static>,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    type_id: TypeId,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    priority: usize,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    is_active: fn(&Self, &FilteredEntityRef) -> bool,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    actions: for<'a> fn(&Self, &'a FilteredEntityRef) -> Option<&'a [Entity]>,
    #[cfg_attr(feature = "reflect", reflect(ignore))]
    actions_mut: for<'a> fn(&Self, &'a mut FilteredEntityMut) -> Option<Mut<'a, [Entity]>>,
}
sharp warren
tardy scroll
#
#[cfg(feature = "reflect")]
impl Default for ContextInstance {
    fn default() -> Self {
        Self {
            entity: Entity::PLACEHOLDER,
            name: ShortName::of::<()>(),
            type_id: TypeId::of::<()>(),
            priority: 0,
            is_active: |_, _| false,
            actions: |_, _| None,
            actions_mut: |_, _| None,
        }
    }
}
#

lol

sharp warren
tardy scroll
#

That's why we work this out together. Enlighten me please.

tardy scroll
# sharp warren But why add it? I feel like you don't really understand what're doing ๐Ÿค”

Oh did you ask why i add it? Sorry it was right before I slept I couldn't see it. I must push you little bit because I do care about your crate:

  • I don't know exactly what you want (what have you said so far?).
  • Compilation error leads to the approach.
  • Of course, there are more ways to resolve but (if this is the case you imply) I cannot keep writing code and see if you like or not. I'm not your LLM, buddy! What have you specified so far? Scroll up please. Based on what, should I understand how deep what these are doing?
sharp warren
tardy scroll
#

So can i still add the default?

sharp warren
tardy scroll
#

Of course ShortName is not required by reflect (required by you lol)

error[E0277]: the trait bound `bevy::prelude::ShortName<'_>: core::default::Default` is not satisfied
  --> src/context/instance.rs:42:40
   |
42 | #[cfg_attr(feature = "reflect", derive(Reflect))]
   |                                        ^^^^^^^ the trait `core::default::Default` is not implemented for `bevy::prelude::ShortName<'_>`
   |
   = note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info)
sharp warren
tardy scroll
#

hmm... can you go to the disqualified and beg ask for the reflective ShortName?

sharp warren
tardy scroll
#

And you don't like this: #1297361733886677036 message

sharp warren
tardy scroll
#

Ah so you want me to go deeper than that

sharp warren
sharp warren
tardy scroll
#

I can leave some comment (for the second) if you like?

sharp warren
tardy scroll
#

I haven't thought so much

sharp warren
#

If you find a use case, I'm open exporting it. But right not it looks like something useful for BEI internals (you saw it stores pointers, ID, etc.).

tardy scroll
#

we talked about its being public when a (new?) dev/debug feature is on

sharp warren
tardy scroll
#

I'm lazy to write a query just for a few takes in debugging Personally it's a debugging workflow. There's no use case with a tangible application.

sharp warren
tardy scroll
#

Well this makes me hard to argue haha.

tardy scroll
#

Were you talking about it versus having a separate component independent from ContextInstance in debugging (so to query)?

sharp warren
# tardy scroll Were you talking about it versus having a separate component independent from `C...

Ah, I forgot all context instances are generic and you probably want to iterate over all of them, right?
Maybe you could do register_required_component<C, YourDebugMarker> for each C registered as context. To do this, you'll need to create your own register_context_dev. But I don't think it worth upstreaming since in BEI entities could have mutliple contexts.

ContextInstances is also generic over a schedule, so it wouldn't help you even if we expose it.

tardy scroll
#

I'm lazy this would be another bulk in my debugging, proportional to the number of context while ContextInstances can always be iterated over finite in dev somewhat fixed number of schedule label, which still helps.

#

well in that case you can still suggest writing a macro for this or adding up many other things (observer, event, ... or you can suggest writing more test maybe). There are so many ways to achieve it on the user-side, versus having nothing but the mundane iteration over the exposed resource.

tardy scroll
#

By the way, out of topic, can we imagine if we must do this for all third-party Bevy crates (which hasn't happened; maybe in the future ecosystem? ). If they phenomenize artificially encouraging everyone to wait for the beautiful full-reflection ecosystem, I won't wait for it (I like reflection though). I have to proceed.

sharp warren
tardy scroll
tardy scroll
#

(nudges Shatur)

sharp warren
sharp warren
#

@tardy scroll drafted a new release with the change.

tardy scroll
#

Awesome!

inner sky
#

@sharp warren I got a design question for how to best model the following commands:
I have some PlayerInputContext that's working great, but I now need to add some debugging controls. I'm thinking that holding Alt enters debug mode, which allows things like middle-clicking the thing you're looking at to reset its state

#

off the top of my head, this sounds like a DebugInputContext that should be added while Alt is held and removed after

#

is that the right approach?

#

or should it instead be actions on the regular context that are gated on holding the alt key?

#

note that the debug mode should stay in the final game, so modders have access to it

#

so I'm not worried about compiling it out or anything

#

followup question: even if I go the route of adding and removing a dedicated context, I'm still curious, what's the best way to say "fire this action when middleclicking, but only if Alt is also held"? A chord?

sharp warren
sharp warren
inner sky
sharp warren
inner sky
sharp warren
inner sky
sharp warren
#

You you can even put it inside your existing context

sharp warren
#

I think not removing is easier

inner sky
sharp warren
#

You just create Alt+whatever for every debug actions

inner sky
#

ok, I'll do that then

sharp warren
#

I probably wouldn't remove and insert.

inner sky
#

I'm just confused where in here I'm supposed to add it

sharp warren
inner sky
#

oh it's with_mod_keys now I think?

sharp warren
#

Ah, yes

inner sky
sharp warren
inner sky
#

for that one I would need SpawnWith, right?

sharp warren
#

No, why?

inner sky
#

hmm I'm misunderstanding

sharp warren
#

It's just a component on the context entity. Just insert alongside your actions!

inner sky
#

Oh okay. Hmm, so right now I'm inserting both contexts on the Player

#

since they don't make sense outside the context of the player

#

that wouldn't fly with ContextPriority, right?

#

since then the context priority would be set equally for PlayerInputContext and DebugInputContext

sharp warren
inner sky
#

OOOOO

#

Okay

#

sorry

#

that makes sense

sharp warren
#

No problem ๐Ÿ™‚

inner sky
#

I'm too dumb to read today haha

sharp warren
#

It's ok ๐Ÿ™‚

inner sky
#

I got confused by this sentence:

Determines the evaluation order of the input context C on the entity.

#

which makes me feel like it's an order, i.e. 0 comes before 1

#

ah wait

#

1 overrides 0 then

#

okay yeah that makes sense

#

I've been doing a lot of 3D art lately, and there layers are always evaluated like layers of paint

#

i.e. the ones that come first in the list are painted on top of everything else

#

(since the layers are usually drawn top-to-bottom)

#

so I need to switch my mental model here

#

Maybe something to consider if one day BEI contexts are shown in-editor @eager aspen

sharp warren
#

Higher priority wins. It's already in the docs, but probably worth mentioning on the priority component as well

oak monolith
inner sky
#

@sharp warren I'm trying to bump BEI to bevy main for the Bavy jamthread 'main' (590280) panicked at /home/hhh/.cargo/git/checkouts/bevy-50d7e162b728c6c6/d06aa47/crates/bevy_ecs/src/query/state.rs:216:13:
error[B0001]: Query<', ', EntityMut<'>, With<ActionFns>> in system (bevy_ecs::system::commands::Commands<', '>, bevy_ecs::change_detection::params::Res<', bevy_enhanced_input::context::instance::ContextInstances<bevy_app::main_schedule::PreUpdate>>, bevy_ecs::system::query::Query<', ', bevy_ecs::world::entity_access::filtered::FilteredEntityRef<', '>, bevy_ecs::query::filter::Without<bevy_enhanced_input::action::fns::ActionFns>>, bevy_ecs::system::query::Query<', ', bevy_ecs::world::entity_access::entity_mut::EntityMut<'_>, bevy_ecs::query::filter::With<bevy_enhanced_input::action::fns::ActionFns>>) accesses component(s) ContextInstances<PreUpdate> in a way that conflicts with a previous system parameter. Consider using Without<T> to create disjoint Queries or merging conflicting Queries into a ParamSet.

#

@sharp warren I'm trying to bump BEI to bevy main for the Bavy jam and get this

thread 'main' (590280) panicked at /home/hhh/.cargo/git/checkouts/bevy-50d7e162b728c6c6/d06aa47/crates/bevy_ecs/src/query/state.rs:216:13:
error[B0001]: Query<'_, '_, EntityMut<'_>, With<ActionFns>> in system (bevy_ecs::system::commands::Commands<'_, '_>, bevy_ecs::change_detection::params::Res<'_, bevy_enhanced_input::context::instance::ContextInstances<bevy_app::main_schedule::PreUpdate>>, bevy_ecs::system::query::Query<'_, '_, bevy_ecs::world::entity_access::filtered::FilteredEntityRef<'_, '_>, bevy_ecs::query::filter::Without<bevy_enhanced_input::action::fns::ActionFns>>, bevy_ecs::system::query::Query<'_, '_, bevy_ecs::world::entity_access::entity_mut::EntityMut<'_>, bevy_ecs::query::filter::With<bevy_enhanced_input::action::fns::ActionFns>>) accesses component(s) ContextInstances<PreUpdate> in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`.
#

any idea where to look?

#

it appears when adding add_input_context

#

okay it's context::apply

#

so uuuh

#

what to do hmm

sharp warren
#

I don't know, depends on what changed on main.

inner sky
#

oh it's a resource

#

I bet that's resources-as-entities

#

yep, slapping a Without<ContextInstances<S> on the actions works: mut actions: Query<EntityMut, (With<ActionFns>, Without<ContextInstances<S>>)>

sharp warren
#

Ah, makes sense

sharp warren
eager bloom
#

Hi all . I am using version 0.24.3. I would like the output value to go to (0.,0.) immediately when all keys are released. Current code below uses SmoothNudge which works OK. But I do not want nudging because a quick key tap moves the player a little too far for my liking. When I remove SmoothNudge there is no event coming in; and my player continues to move.
How do I implement an immediate stop for my character on key release?

            Action::<MoveAction>::new(),
            DeadZone::default(),
            // Able to handle multiple keys down
            bevy_enhanced_input::prelude::Down::new(0.01),
            // This is needed to bring movement to a stop
            SmoothNudge::new(16.0),
            Bindings::spawn((Cardinal::arrows(), Axial::left_stick(),)),
sharp warren
eager bloom
#

I fixed my problem by changing the system that processes the output value. Which is fine - and sensible in a way. My new solution now depends on nudge to bring down the speed gradually.

sharp warren
#

Yeah, this also makes sense.

tame umbra
#

Hi is it possible to have Key instead of KeyCode in the keyboard bindings? Having trouble with implementing hotkeys compatable with different language kbs

sharp warren
tame umbra
#

KeyCode::KeyZ is Z in english, but Y on german keyboards

sharp warren
tame umbra
#

but germans also use ctrl Z to undo

#

as well

#

so keycode would cause problems

sharp warren
#

In that case we might need to allow Key ๐Ÿค”

tame umbra
#

Yup, one of my testers is swiss and encountered this issue. they expected ctrl z to undo but instead it does redo

sharp warren
tame umbra
#

yup

sharp warren
#

Thank you!
If you have some free time, a PR would be welcome ๐Ÿ™‚

tame umbra
#

yup working on it

#

oh key also has undo redo

#

undo keycode is cursed

#

will need to test if I can just use that

sharp warren
#

I think it's dedicated undo / redo buttons.

sharp warren
# tame umbra yup working on it

Maybe instead of having a whole separate Binding variant we could have a special enum for Binding::key field with a From impl

proud coral
#

xddd

tame umbra
#

xdd

proud coral
#

man software is weird

tame umbra
#

we would need to store and check Key separately either way I think

#

they're tracked in diff bevy resources too

#

having issues w this impl because

#

Binding and KeyCode is Copy but Key is sadly not, so would need wrapping struct

tame umbra
sharp warren
tame umbra
#

yup working on that rn

#

but

#

key does not impl copy :(

#

prolly cuz it does some string stuff?

sharp warren
#

Hm... Probably. It would be unfortunately to not have Copy ๐Ÿค”

#

Not sure what to suggest at the moment.

#

You can try to remove the Copy for now.

tame umbra
#

yup

#

is ignored() used only for AnyKey?

#

also removing copy is an absolute nightmare xd

sharp warren
sharp warren
tame umbra
sharp warren
#

@tame umbra have you considered converting Key to KeyCode somehow?

tame umbra
#

but they seem very separate

#

otherwise I wouldn't need any changes

sharp warren
tame umbra
#

hmm

#

key seems to have a lot of stuff that wouldn't map to keycode at all

sharp warren
#

I mean this could even be possible to do outside of BEI

tame umbra
#

maybe

sharp warren
#

But on second thought, I think something like this should be baked in BEI ๐Ÿค”

#

Because I think it's quite common

tame umbra
#

for my personal game, I could just map ,/. to undo redo

#

but

tame umbra
#

not sure why they would do that if they could just map it

#

up to you I guess, I'll hold off on the copy stuff for now

#

well

#

it doesnt work but its what im working on

sharp warren
#

And store BindingKey (the enum you added) inside ignored cache as well.

tame umbra
#

hmm alright, are we de-copying Binding?

sharp warren
tame umbra
#

oh yeah i still have compile issues

#

mostly just cloned my way thru it but copy should be removed now

sharp warren
tame umbra
#

it's what happened last time too xd

sharp warren
#

Looks like a Windows configuration issue ๐Ÿค”

#

Looks like tests fail

tame umbra
#

yup i saw

#

also worried about if i handled ignored_keys correctly, especially in the context of AnyKey

sharp warren
#

A bit sad that we need to remove Copy, but in most cases it will be trivially clonable, so it's fine

sharp warren
#

Both key and key codes should to be checked for any.

tame umbra
#

yup

sharp warren
#

Once CI passed, I'll do full review. Also would be nice to hear @outer wyvern opinion

#

Ah, looks like she upvoted the issue

#

Should be good to go

tame umbra
sharp warren
#

Just saw the message, but looks like you already replied ๐Ÿ™‚

outer wyvern
#

Mhmm!

wicked moat
#

Hi folks ! BEI has been amazing so far, however I am confused about context layering , I thought I had made a mistake, but running the example in the BEI repo has the same "issue":
ie

  • when pressing Enter, the context switches from Driving to Player and immediatly back again to Driving, which is not what I would have expected. (I thought require_reset was supposed to prevent exactly this)
  • however If I I add consume_input: true, to the ExitCar action's settings , then it behaves like I would expect : ie
(
      Action::<ExitCar>::new(),
      ActionSettings {
          // We set `require_reset` to `true` because `EnterCar` action uses the same input,
          // and we want it to be triggerable only after the button is released.
          require_reset: true,
          consume_input: true,

  ),

So am I missing something obvious ? it this the intended way to deal with context layering ? or is this a bug ?
Thanks ! ๐Ÿ™‚

sharp warren
wicked moat
wicked moat
sharp warren
#

Working with bevy_picking for my game and I feel like we'll need to heavily change it after BEI upstreaming.
Either use a BEI context for it or entirely split casting logic from events ๐Ÿค”

heady mauve
#

Doesn't it have the ability to be disabled? Seems like having it enabled within a BEI context probably would require the least modification.

sharp warren
wicked moat
sharp warren
# wicked moat Ouch, I was investigating "re-binding" the picking logic using BEI, but other th...

For UI I use this glue to create button shortcuts:
https://github.com/simgine/simgine/blob/master/ui/src/widget/button/action.rs
And in the code I react on Activate, which triggered on both - clicks and hotkey presses.

For picking with physics I think I'll write a tiny abstraction over SpatialQuery to cast things from cursor.
In my game a lot of things are context-dependent. So I use picking only for UI clicks.

GitHub

A work-in-progress life simulation game. Contribute to simgine/simgine development by creating an account on GitHub.

#

@outer wyvern I need your call on https://github.com/simgine/bevy_enhanced_input/pull/320
To put it short:

  • We wanted to support Key to have shortcuts like Ctrl + Z to be location dependent.
  • But Key doesn't implement Copy, so this introduces a clone hell.

My suggestion is to support only char instead of the whole Key. This would solve the problem without giving up Copy on Binding.
I don't think users will ever need the full Key support in actions. What do you think?

outer wyvern
sharp warren
outer wyvern
#

Hmm ๐Ÿค”

outer wyvern
#

Are there applications for this beyond the modifier key problem?

#

Because we already have a solution to that IIRC

sharp warren
outer wyvern
#

Why is Ctrl+Z special? Or is it?

sharp warren
#

I think because it's mnemonic. People used to Ctrl + Z. So on QWERTZ it's still Ctrl + Z, but the location is different.

#

Maybe for other shortcuts like this it could also make sense

outer wyvern
#

Do other shortcuts work this way?

#

Ah, the problem in Ctrl + Z that you're talking about here is that the Z moves around, not that you should be able to move either left or right control

sharp warren
#

Yes!

outer wyvern
#

Okay, I think I understand the problem more clearly ๐Ÿ™‚

#

(This is going to need a ton of docs about best practices)

#

Yes, using the scancode values for UI-flavored hotkeys makes sense to me

#

We should support this

#

Using a single char alone doesn't seem sufficient though

#

Things like Home are important to be keybindable

#

And don't map to this

#

We don't need to support complex Chinese characters or emoji as action-bindable variants

#

As funny as that would be

#

Key itself is only useful for text input

#

(what a mess...)

sharp warren
outer wyvern
#

And all the F keys ๐Ÿค”

#

Okay, I think I'm down!

sharp warren
#

Yeah, I think KeyCode includes everything needed, just for some cases we want only characters to be keyboard related.

outer wyvern
#

Alright, I'm on board

sharp warren
#

Wait, Character says:

A key string that corresponds to the character typed by the user, taking into account the userโ€™s current locale setting, and any system-level keyboard mapping overrides that are in effect.
Which means Z won't work with different locales, right? Like with somethin cyrilic.

outer wyvern
#

If they can type "Z" it should work

#

I still think this is the right choice to support, and should be the default for things like editor hotkeys

sharp warren
#

Cyrilic doesn't have Z. I need to switch to English for it.

#

If this works how I think it works - it won't be good.
Vim has the same problem: keyboard shortcuts based on logical keys, which might not work when you switch layout.

@tame umbra could you try adding any cyrilic layout to the system and see if Ctrl + Z works with your solution?
Like add Ukranian or Russian layout.

sharp warren
#

Well, the button is "ั". But it should work with Z

tame umbra
#

oh ok

sharp warren
# tame umbra

Yep. So you want to bind Ctrl + Z, but it should work as Ctrl + ั on Ukranian layout and as Ctrl + Z on QWERTZ keyboard.

#

If the cyrilic option doesn't work - it's definitely not what you want.
You just want consistent behavior based on keyboard type. The keyboard layout (i.e. language) shouldn't matter.

tame umbra
#

I wonder if logical undo would fix this

#

I'll test it in a bit

sharp warren
tame umbra
#

hmm my pr seems to break things pretty badly

#

im going to just check w the existing bevy resource

#

same "ctrl z" position in 3 different languages

#

so because the ukraine one is in the same position, it has the same Keycode and Keycode would work, but Key would not, however, in the german keyboard Keycode would not work, but Key would.

#

as it KeyZ is Character Y and germans use Character Z to undo

#

for my personal game i might just bind < and > tbh xd

#

but not sure how you would approach it here

sharp warren
tame umbra
#

yeah im building a game so i can just rebind it nbd

#

but for productivity apps i could see this being a headache

sharp warren
#

@tame umbra On second thought, users could technically bind to both Key::Character('Z') and KeyCode::Z. Would you mind trying this?

#

I mean Ctrl + those keys

tame umbra
#

that would make german ctrl z undo and redo at the same time maybe

#

which would be weird?

sharp warren
#

Right, not a good solution ๐Ÿ˜ข

heady mauve
#

The more I hear about this the more I think this sounds more like it might be a localization issue than an issue with the binding API.

sharp warren
heady mauve
#

The issue seems to stem from German keyboards switching the placement of Y and Z while still using Ctrl-Z for undo. It is unique to a locale based situation. That is why it seems to me that the solution is to alter the bindings based on the locale would be the proper choice for this case and isn't necessarily a good use case for altering the API.

As for altering the API to support Key as a binding, I'm not necessarily opposed. I honestly don't know enough about how winit works to determine the pros and cons so would have to look into it more. I think I'd need to see a use case that doesn't have such a specific scenario surrounding it.

sharp warren
#

This is why I suggested to try to map both

heady mauve
# sharp warren The problem with `Key` is that it breaks for other layouts. So we something like...

I think this is where my confusion lies. Key breaks for different layouts so the preference, at least for most games, is to use KeyCode which has consistent placement across layouts. Is there a reason other than localization where using Key provides benefits?

If there is a way for the user to bind inputs then it seems better to take the inputs as KeyCodes directly. So that seems to leave possible developer scenarios. I'm trying to determine a non-localization use case for Key but having trouble with that.

If there isn't a use case that lies outside of localization then it seems to me that it is a localization issue and not an API issue.

tame umbra
#

possibly typing?

sharp warren
heady mauve
#

Yeah, I understand it is still an issue. But if we know it is only for localization we could see if there is a solution that lies in that space. Unfortunately, what that might be eludes me.

A brief thought I had was just using separate bindings and swapping based on locale but I'm not sure if Bevy, winit, or any other underlying library provides the OS locale. I'm not sure how easily we can do partial or complete binding swaps but that would be useful for other cases as well.

inner sky
#

@sharp warren is BEI (or even bevy_input) aware of MIDI controllers?

sharp warren
inner sky
#

So I'm thinking of reading MIDI messages in PreUpdate and translating them to BEI actions

sharp warren
toxic peak
#

that's how you can make e.g. virtual pointers driven by software instead of any sort of hardware

#

Because pointer positions and presses are driven by these events, you can use them to mock inputs for testing.

inner sky
#

@sharp warren we are discussing using BEI for keybindings of addons for Jackdaw

#

I just noticed there is add_input_context, but not remove_input_context

#

if every addon uses its own input context, that would mean a memory leak when an addon is unloaded at runtime (e.g. when it was hot-reloaded or disabled by the user)

#

Is that an API hole, or the wrong approach?

#

should we use a single input context for all of jackdaw instead, and have user-written addons add their actions to that global context?

outer wyvern
sharp warren
toxic peak
#

Yup totally, was driving by just to let you know the input part of the pipeline is designed to be pluggable. We should def change the default to use BEI instead of HW directly though.

sharp warren
inner sky
#

Cc @eager aspen

eager aspen
#

fyi, itโ€™s not โ€œhot reloadingโ€ itโ€™s just registering/unregistering the plugin - effectively a on/off switch for each plugin

#

Itโ€™s not dynamic lib loading

#

But we obviously donโ€™t want that extension to continue listening for inputs if itโ€™s been disabled

#

Ie, the extension API supports custom keybind overrides within BEI

#

for context - i have a custom extension here in jackdaw i added, which prints to the log when a user presses โ€œF9โ€

sharp warren
inner sky
inner sky
#

Now that we have system removal, at least

#

Should probably also add a remove_message upstream for convenience

sharp warren
inner sky
#

It's not like we want to remove all of BEI at runtime

#

just unload a specific action context and load a new one

sharp warren
sharp warren
#

If there is no hot library reloading yet, I'd just despawn the context and don't bother unregistering it.

inner sky
#

makes sense

sharp warren
coarse knoll
#

Hi, I'm struggling a bit with understanding how to modals with BEI ๐Ÿ™ I use the same key to spawn and dismiss a modal and thought I could use On<Start<input>> to do both. But, it seems that if the key is already pressed when the entity is spawned, Start fires then. So I get in an infinite loop.I switched to Complete but, then I needed an up/down axis to move between options and reorder them, and On<Complete<axis>> doesn't give me a direction - always returns Vec2(0.0,0.0) because (I guess) that is the actual current value of the axis. And anyway, I really would prefer Start and be consistent between different inputs.

#

No problem I thought! I'll just have a permanent ModalController entity that already exists when the key is pressed, and can deal with the input. But then I have to add the context components to the ModalController and not to the modal, which needs more queries and ties code together that I'd rather be separated.

#

I feel like I'm just not using this system the way it's intended. Can anyone give me some advice?

#

Maybe consume_input which i see referenced above? But I guess that might have the same problem on not being consumed when the entity with the action doesn't exist yet.

heady mauve
#

You may need to enable require_reset in your ActionSettings so that it only can reactivate after the key is released.

coarse knoll
#

Ok, that looks promising

coarse knoll
#

aha i finally found the context switch example...

#

I think the trouble was I was for some reason stuck to the idea this would be something that contexts were useful for, but it isn't

#

I guess require_reset works ok with sticks and dead zones ? I can't test that atm

#

anyway much better, thank you

outer wyvern
coarse knoll
#

Well I'm not sure what the best approach is tbh. My game has an unholy mixture of entities and resources because I have been slowly learning and updating since 0.8, so I am not a trustworthy Bevy developer

#

I would say though my expectation of Start<input> was that it would detect the input as having actually just started (i.e. with require_reset built in) Something like First<input> would fit the behaviour better (although this is perhaps bikeshedding), or maybe a note in the docs of Start would be enough

#

I'm happier putting in docs PRs than code ones ๐Ÿ™‚

#

Similarly Complete providing a zero value confused me for a while though I guess it is logical.

sharp warren
coarse knoll
#

Yes and that seems like a good architectural choice. I'm coming at it from the top down though and would find that kind of note useful.

sharp warren
coarse knoll
#

It was good! I got started quickly. It was only the behaviour on spawn tripped me up and I didn't think of looking in ActionSettings.

#

90% of the work was done by then ๐Ÿ˜€

sharp warren
#

Maybe worth metioning in the guide ๐Ÿค”

sharp warren
#

@inner sky what do you think about upstreaming DidFixedUpdateRunThisFrame (maybe under a different name) into Bevy?

inner sky
#

in fact, it's even in one of the examples

#

I had to use it there as well

#

one of the review comments even was "this could be upstream"

#

I just didn't bother haha

sharp warren
inner sky
inner sky
#

I thought that's what you were referencing

inner sky
#

well I see why you pinged me about it haha

sharp warren
ionic sundial
round portal
#

is it the best way to unpack Vec2 input from Query<&Action<MyVec2Input>>? its the one that works, but I doubt its the best
let movement: Vec2 = (*(*movement)).into();

round portal
#

One more thing - I want to query Action<MyVec2Input> for one of the entities that have the context with this action, how do I do that? for the sake of example - I want to get movement vec2 on custon event observer and use observer entity to query Action. I verified that target entity I get to observer is actually the one with the context

sharp warren
inner sky
#

since avian runs in FixedPostUpdate, I would not run any input application there, otherwise you will at random be either on time or one frame behind, or change the physics stuff while Avian is processing it

#

sure you can do .after(PhysicsSystems::Last), but IMO it's safer to apply the inputs to FixedPreUpdate

#

Since you will also run your own game's logic in FixedUpdate, applying the input in FixedPostUpdate will guarantee that your own logic sees them one frame delayed

#

which doesn't matter at high FPS, but is definitely noticeable on low-end machines

#

want me to do a PR for that?

sharp warren
soft root
#

Hi everyone, I am having issues with consume_input setting. I have 2 different input contexts. One for navigation and one for widget control. They have Actions with same bind. Widget action has consume_input set to true and it is evaluated before navigation. For some reason if Actions have Press condition than both contexts fire events. But if Actions have Pulse or Down condition than input consumed as expected. Is it intended behaviour for Press condition?

sharp warren
oak monolith
#

Hi there! I'm trying to implement a cooldown for a complete 3-action Combo (basically I want to trigger the cooldown on the last action, preventing the first one from being triggered), but I'm not sure how to properly do it. This is a (probably incorrect) code snippet of roughly what I'd like to have:

let combo_timeout = 0.5;
let hack = context
    .spawn((
        Action::<Hack>::new(),
        BlockBy::single(other_action),
        Press::default(),
        bindings![...],
    ))
    .id();
let combo_step = ComboStep::new(hack).with_timeout(combo_timeout);

context.spawn((
    Action::<Slash>::new(),
    Combo::default().with_step(combo_step).with_step(combo_step),
));

context.spawn((
    Action::<Slam>::new(),
    Combo::default()
        .with_step(combo_step)
        .with_step(combo_step)
        .with_step(combo_step),
));

Should I go for some custom logic? I was wondering if there's a solution using Cooldown but I tried some combinations of it and it doesn't work (the combo just keeps getting blocked at one point when using it).

sharp warren
oak monolith
sharp warren
#

Another option is to create a custom condition.

oak monolith
#

Thanks, I'll try some stuff and see what works!

sharp warren
#

@inner sky could you take a look at https://github.com/simgine/bevy_enhanced_input/pull/327 ? The explanation makes sense, but I want to hear your opinion.
I remember you were mentioning that it would be better to put input handling in FixedPreUpdate ๐Ÿค”

GitHub

Turns out I was wrong on this comment on my last PR.
It's true that both this example and bevy_ahoy use a similar did_fixed_timestep_run_this_frame mechanism, but both these cases are meant...

inner sky
#

while only the gameplay logic should be

inner sky
#

if multiple fixed updates happen in one frame, but you clear the inputs in the fixed updates themselves, the second fixed update that frame won't have any input

#

so e.g. if you have a game where you need to hold space to jump higher, you will randomly jump short since the input was interrupted

#

or you will slow down for one tick, since only the first tick that frame has the W key held

#

PR should be closed imo

#

I can take some time this weekend to fix the input handling in the examples

#

and add some comments why the things are done a certain way

#

as you can see, input handling in Bevy still has quite a few footguns :/

sharp warren
#

Thanks for the input!

inner sky
#

wish all of this happened in the background without you noticing

inner sky
#

"input"

sharp warren
inner sky
#

as in it's tied to ahoy itself

sharp warren
#

I just wanted to say that it's great to have ahoy ๐Ÿ˜…
And yeah, I wish it was simpler

sharp warren
inner sky
#

the scheduling is not entirely realistic: applying the input would usually be done in FixedUpdate, and the physics in FixedPostUpdate

#

but that can be a followup

#

I'd merge it as-is

sharp warren
inner sky
#

sure, sec

sharp warren
#

Thank you!

inner sky
#

done

distant oasis
#

can i get a sanity check on this pr for jackdaw?
is there a better way to disable all input contexts (such as when typing, where you don't want actions to do anything) when there could be any arbitrary number of contexts from extensions than what i did?
tldr: i made a wrapper for registering input contexts which also sets up systems to disable them via ContextActivity::INACTIVE when a global resource is changed. i couldn't just add it without a wrapper because ContextActivity is generic over the context type

heady mauve
distant oasis
#

oh interesting

#

i'll check it out

distant oasis
sharp warren
sharp warren
languid thorn
#

Does BEI make sense for "global" actions that aren't really associated with a specific entity? Should I just spawn an empty entity with actions!?

sharp warren
#

Resources will be entities in 0.19

languid thorn
languid thorn
sharp warren
languid thorn
#

Sounds promising!

outer wyvern
sharp warren
outer wyvern
sharp warren
#

Sorry!

outer tusk
#

Random question- As part of upstreaming, are there any plans to make the "pull" workflow more ergonomic? Having to have a separate systemparam for each input and an iter_many(children).next() unless you're using Single feels pretty far from the ergonomics I've seen in other engines.

sharp warren
outer tusk
#

I thought each Action exists on a separate Entity? How would you go about using a single Query for multiple actions? Option?

cinder grail
#

@sharp warren Something I have been wanting to discuss is a bridge between BEI and bevy_settings - so that keyboard shortcuts can be saved as user preferences.

  • This will need to be strictly opt-in, since many games won't want this feature (or may not have settings at all)
  • bevy_settings currently requires that settings be stored as a resource, while BEI stores its key bindings in components, so it's a good bet that we would need to copy / sync between the two - saving directly from components is not possible
  • Copy / sync is probably a good idea anyway, because the way that shortcuts are grouped and organized in the settings file (and in the settings ui) might not be the same way as they are grouped and organized in the entities that they are attached to.
  • To avoid writing a lot of complex code, we need some way to declaratively map between these different contexts.
  • For games with large numbers of shortcuts, we'll probably want to group them into different sections in the settings TOML file, which can either be done via multiple resources, or by a single resource with multiple submaps
sharp warren
sharp warren
undone abyss
#

Just set this up in my game, was easy to get going! (on bevy main)

candid totem
#

Hey man, this crate is an absolute godsend. It's very easy to use and I had a blast adapting my project to it the moment I discovered it, made everything so much better (and ready for multiplayer too!).

I actually have a question, suppose I have some Player entity with a very simple action child:

actions!(GameInput[
            (Action::<PrimaryFire>::new(), bindings![MouseButton::Left]),
            ...
        ])

Is there any way that I can trigger the Start<PrimaryFire> event manually within a system? Like a commands.trigger but for the start of an action without pressing MouseButton::Left?

kindred epoch
candid totem
#

Thanks!

#

Looks like it suffices to insert it into the action entity and it fires on its own, so that's going to make it simpler

sharp warren
visual hornet
#

Hi all. I recently discovered what seems like an oversight. I am binding an action to Binding::mouse_wheel and it's much more sensitive -- i.e. yields larger deltas -- on macOS than on Linux/X11.

I tracked this down to the unhandled MouseWheel.unit: MouseScrollUnit field. macOS uses MouseScrollUnit::Pixels and Linux uses MouseScrollUnit::Lines.

I found that "mouse wheel" events generated via trackpad gesture on macOS generates the Pixels variant while using an actual mouse yields the Lines one. Windows generates Lines with a mouse too. I see the projection_zoom Bevy example has the same issue with much larger scrolls using a trackpad.

Should this disparity be addressed in BEI, i.e. with a conversion via MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR (for now)?

sharp warren
visual hornet
sharp warren
visual hornet
languid thorn
#

Is there a way to delay the rapid fire of an action, similar to text input boxes where you'd need to hold down your key for a bit before you can spam a letter?

visual hornet
languid thorn
#

I tried that, but it's not quite what I wanted. The behavior you have with text input boxes is something like this:

the key gets held down -> behavior immediately triggered -> nothing happens for 1-2 seconds -> rapid fire starts

visual hornet
visual hornet
languid thorn
#

Is there a way to do this with a single observer?

sharp warren
#

There is also Pulse

visual hornet
languid thorn
# sharp warren There is also `Pulse`

Isn't Pulse basically just for rate-limiting the rapid firing?
What I need is the same behavior in native text inputs.
When you press down the key you immediately get a single one-time response. But you need to continue holding it for a few seconds before the action starts rapidly firing.

sharp warren
#

You'll need to set the initial delay

languid thorn
sharp warren
#

There is also an option to write your own custom condition ๐Ÿ™‚ See existing conditions for examples. You can write them directly inside your own crate. Just don't forget to register them in the app.

languid thorn
vernal glade
#

Hey, I'm doing a bit of a weird stuff with inputs, but I want to ask a question. I have the following input stream:

ButtonInput { pressed: {}, just_pressed: {}, just_released: {} }
ButtonInput { pressed: {KeyA}, just_pressed: {KeyA, ControlLeft}, just_released: {ControlLeft} }
ButtonInput { pressed: {}, just_pressed: {}, just_released: {KeyA} }

I intended this to trigger KeyCode::KeyA.with_mod_keys(ModKeys::Control), but it's currently not working.
I'm guessing that the funky way that ControlLeft is in both just_pressed and just_released is messing with the detection.