#leafwing-input-manager

1 messages Β· Page 5 of 1

candid vigil
#

πŸ€” But I feel that this approach still retains the complexity of reflection and serde

#

The main reason for using enums for input processors is that their performance can be 4-5 times faster than dynamic dispatch, especially when there are many processors and multiple pointer references, which can be quite slow

#

some people do need a more open-ended input design, and it's not entirely necessary to include some specific designs in the LWIM

bold lodge
bold lodge
candid vigil
#

πŸ€” I think your issue #620 could be fixed this week

#

Unfortunately, the default serde tokens generated by InputMap are not aesthetically pleasing

bold lodge
#

Yeah... Having them very nice is not very important, but nice to have.

candid vigil
#

The broken deserializations of separate traits have been fixed

#

I will push the changes after checking that the other parts are error-free

bold lodge
#

Great!

#

Having a fix for the current version will allow me to migrate to 0.15.

But I still think that it may be worth exploring non-dynamic approaches...

candid vigil
#

πŸ€” I think it could be addressed by another approach that implements Into<UserInputWrapper> for concrete inputs and boxed input trait objects

#

then in favor of fn insert(&mut self, action: A, input: impl Into<UserInputWrapper>) for InputMap

bold lodge
#

I saying that traits are PITA for ser/de and can break just like in this release without anyone noticing.

candid vigil
bold lodge
#

Ah, got it.

candid vigil
#

I've added a regression test for the ser/de of InputMaps and inputs, so that if anything goes wrong, we'll catch it right away

candid vigil
#

πŸ˜… I'm running into an issue with serde_flexitos where it only accepts deserializers that have been registered ONLY ONCE

#

This means our current approach using an App extension method won't work if we add different input plugins for different actions

bold lodge
#

Alternatively you could try to use reflection

karmic monolith
candid vigil
karmic monolith
#

It doesn't need to be, but it's messy

bold lodge
#

So it doesn't affect users in any way

candid vigil
#

opened an issue on their repo

candid vigil
#

added a custom implementation and works

#

@karmic monolith What do you think about this?

karmic monolith
#

Generally I don't expect users to be implementing this often enough to be worth it

candid vigil
#

The main reason I thought of this is that if we can implement Into<UserInputWrapper> for T: Buttonlike and Box<dyn Buttonlike> and other input types, creating and inserting into InputMap can be as simple as in 0.14

#

Then implementing a derive can reduce a lot of redundant code, but I'm not sure if it's really worth it

karmic monolith
#

Yeah, I generally find macros relatively hard to maintain πŸ˜…

candid vigil
#

How should we version the merged VirtualAxis and friends?

#

Should it be in 0.16, given that it's a breaking change? Or should we consider a minor version bump to 0.15.1?

#

Could someone explain the reason why the compiler reports: the trait user_input::Buttonlike is not implemented for bevy::prelude::KeyCode and bevy::prelude::GamepadButtonType

#

their impls are already in user_input/keyboard.rs and user_input/gamepad.rs

karmic monolith
candid vigil
#

πŸ˜‚ an unstable feature?

karmic monolith
candid vigil
#

Ahh, you're right

karmic monolith
next sierra
#

Hello, I'm trying to migrate leafwing_input_manager from version 0.14.0 to 0.15.0. With version 0.14.0, I use this to create my input_map:

input_map.insert(
    MenuAction::Up,
    SingleAxis::positive_only(GamepadAxisType::LeftStickY, 0.4),
);

In version 0.15.0, I wrote this:

input_map.insert(
    MenuAction::Up,
    GamepadControlDirection::LEFT_UP,
);

But if I'm not mistaken, there's no concept of a threshold, which makes the stick overly sensitive. Should I rather use a GamepadControlAxis with a deadzone? Could you point me in the right direction?

bold lodge
#

Does the plugin provides a way to listen for input events in observers?

karmic monolith
candid vigil
#

πŸ€” Perhaps we can add that method back

midnight bramble
next sierra
# candid vigil GamepadControlAxis::LEFT_Y.with_processor(AxisBounds::at_least(0.4))

I agree that using GamepadControlAxis is an option, but it functions more like an analog stick. In other words, you can't use the pressed() method with it. I believe adding a threshold to GameControlDirection would better align with the way things worked in version 0.14. But I'd be fine using GamepadControlAxis if that's the recommended approach.

midnight bramble
#

using the item wheel as example:
you use buttons to activate slots 1, 2, 3~8...
you can reconfigure those buttons for keyboard easily to e.g. q/e
on controller you'd want to use something like LB + wheel in one of the 8 directions, and might want to reconfigure a couple slots to something that's easier to access, like just RB

#

and in that scenario left_y wouldnt work for diagonal, is there anything else for that?

candid vigil
next sierra
next sierra
#

btw I could also have a look at the code and if it is not too complex, send a PR.

candid vigil
old spoke
#

camera.rs
controls.rs
it looks like when i connect a controller my sensitivity is extremely slow,
and the y-axis is inverted whenever i look around, help would be appreciated.

old spoke
karmic monolith
#

I don't have time right now, sorry

#

But I do recommend testing this out using raw bevy_input

#

And exposing settings to control sensitivity and axis inversion to end users

restive knoll
#

There is also a missing feature where you can't tell LWIM to multiply the controller input by the delta time, which means your controller input will be FPS bound (or if you multiply it yourself, mouse input will become FPS bound because the number of pixels moved already relates to the frame delta)

#

As an example, my camera bindings look like this:

.insert_dual_axis(
    ControlAction::Camera,
    MouseMove::default().sensitivity_x(0.1).sensitivity_y(0.0625),
)
.insert_dual_axis(
    ControlAction::Camera,
    GamepadStick::RIGHT
        .with_deadzone_symmetric(0.2)
        .sensitivity_x(0.8)
        .sensitivity_y(0.3)
        .inverted_y(),
)
old spoke
karmic monolith
restive knoll
midnight bramble
#

aren't processors supposed to be only the things that players should be able to modify?

karmic monolith
#

We also need to unify processors with GamepadSettings at some point, but that's a post-upstreaming problem

midnight bramble
#

then is there any place to put delta/clamping/normalization for the action itself?

karmic monolith
#

I think the latter pattern is better

midnight bramble
#

system logic works well so far. it would make sense if input manager would handle it, but i don't think it's actually important at all

restive knoll
#

End user system logic can't handle this in any reasonable way. If LWIM fully achieves the goal of abstracting the inputs away that trigger actions, then you cannot know that you need to multiply by delta

#

I think more generally however this might be an issue with the abstraction (axislike) not fitting an input that is already frame rate independant (mouse movement)

#

In many other context, throwing mouse motion in an axis makes very little sense

#

Like assigning mouse motion to movement

midnight bramble
restive knoll
#

Mouse motion, or relative cursor position? Because those are very different types of input

midnight bramble
#

there was a split between using mouse for movement, and using arrow keys... it took a while until WASD became the default

#

there's also hammerfall that uses mouse for movement and it's actually awesome

#

and some top-down games where precision movement is required

restive knoll
#

You don't normally use a mouse the way you do a gamepad stick, since you would have to constantly move it to the side you want to move by exactly one pixel per frame

midnight bramble
#

ehh, it depends

restive knoll
#

I've played a lot of games but I have never played a game that treated mouse motion the same as a gamepad stick for more than a few frames (those few frames being however long you have a item/weapon wheel open)

midnight bramble
#

i'm not even sure how it is on controller, but try hammerfall?

#

mouse movement is directly translated into velocity every frame from what i remember about it

#

oxyd was similar too

#

and i can't imagine either game working with keyboard instead of mouse for movement

#

tbh it's a bit strange that 3d/top-down 2d precision platformers using mouse aren't more common, it's really not as bad as you're imagining it

restive knoll
midnight bramble
#

i've also seen people one guy play FPS on a driving wheel

restive knoll
#

Keyboard camera controls have the same issue as a gamepad stick

#

If you do not multiply it by delta, it's only going to work if you're constantly at the perfect FPS you set your sensitivity for, which is an insane system

midnight bramble
#

oh, i finally get what you mean

#

yeah, just lowering sensitivity wouldn't make it framerate-independent...

restive knoll
#

Mouse motion has no delta (well unless camera movement is handled in FixedUpdate, which is a mistake)

#

Well it kind of does, it is in itself the delta of some virtual mouse position since the last frame

#

Guess we should call it a Deltalike which is definitely not confusing at all thonk

midnight bramble
#

if you multiply mouse motion for camera by time delta then you get the opposite effect, making it framerate-dependent...

karmic monolith
#

I'd like to get it out in a timely fashion, but I'm also frustrated with it and really want to prioritize #editor-dev work right now

#

Nothing too hard: just needs debugging and an example

midnight bramble
#

if triggerlike for keycode returns just 1.0/0.0, doesn't that mean analog keyboards will only work as normal ones?

karmic monolith
#

But if they do I'll add it

candid vigil
#

πŸ€” And do we need Triggerlike versions for VirtualAxis and its friends?

karmic monolith
candid vigil
karmic monolith
candid vigil
#

But if every Buttonlike is also going to be a Triggerlike anyway, why don't we just make trigger_value() a method of Buttonlike?

karmic monolith
#

Okay yeah fine

#

That's soo much simpler

candid vigil
#

ohh, Triggerlike is only implemented for GamepadButtons, KeyCodes and MouseButtons, not all Buttonlikes

karmic monolith
#

Yeah, but we could easily have a reasonable value for any of them

#

It's just that GamepadButton needs special logic

candid vigil
#

Would it be beneficial to allow directional inputs to get value ranging from 0 to 1 or higher?

candid vigil
#

For example, MouseScrollAxis gives you how much the wheel has moved in an axis. So, should MouseScrollDirection::UP give you a similar value?

#

These are the implementors of Buttonlike, but only the first four implement Triggerlike in the PR

#

I was just looking at this section and it made me think of that

karmic monolith
candid vigil
#

πŸ€” If we had a product manager or detailed user requirements, life would be a breeze

#

No overthinking needed, we could just toss it to ChatGPT and call it a day

karmic monolith
#

Truly πŸ˜‰

#

Unfortunately user input is sooo much more complex to get good abstractions for than I ever expected

midnight bramble
candid vigil
#

I might’ve missed something. Could you give me a bit more context?

midnight bramble
candid vigil
#

Wow, these are really great examples. However, I'm not an experienced game developer or player, so I believe I couldn't create something as good as this

midnight bramble
#

it would be much longer than that, and i don't think i've seen a perfect input manager anywhere yet.
i still see games with some of the input being hard-coded, so i think as a first challenge i'd want to see just making sure there's no need to hard-code anything that players might want to change

#

like in games where you have a skillbar, usually on controller...
N/E/S/W are for first 4 skills,
hold left trigger to swap to next 4 skills,
hold right trigger to swap to next 4 skills,
hold both to swap to last 4 skills...

usually either the triggers or skill slots are hard-coded.
so e.g. you can't assign trigger + dpad down to anything, or use dpad down instead of both triggers + N/E/S/W, or reassign trigger to shoulder

bold lodge
#

@candid vigil I see that you fixed ser/de, but methods on InputMap still accept impl Trait.

candid vigil
#

yeah, i know that, but since we don't have trait object upcasting, we;ll need insert_boxed_button, insert_boxed_axis, and so on πŸ˜‚

karmic monolith
candid vigil
#

Could we just use this one, and implement Into<UserInputWrapper> for all concrete input types as well as boxed ones?

#

then change the methods of InputMap to accept an impl Into<UserInputWrapper>

#

but it might not be clear to users what a UserInputWrapper is compared to the old UserInput enum

bold lodge
bold lodge
#

Probably better to keep a single insert with this enum

severe arch
#

I'm assuming MouseMove is true mouse delta is that right?

#

like its not multiplied by anything its just a forwarding from the mouse motion event

severe arch
#

Also I'm a bit confused, with 0.15 can I have varying input control kinds? I have this enum:

#[derive(Actionlike, Component, Reflect, Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum PlayerInput {
    // Movement
    Move,
    Jump,
    Rotate,
    // Cast controlling
    Cast, // doubles as starting/confirming
    Cancel,
    // Ability selectors
    Ability1,
    Ability2,
    Ability3,
    Ability4,
}

But when I try to actually initialize the input manager it freaks out about control kinds.

#

or do I need to hint it somewhere?

#

I see in the latest main there is #[actionlike(DualAxis)] but I don't think that was in 0.15?

restive knoll
#

On the last release you need to manually impl the trait, it's fairly simple tho, just a match on your enum returning a kind iirc

severe arch
#

gotcha, makes sense

tawdry olive
#
/// Actions can be disabled in four different ways, with increasing granularity:
///
/// 1. By disabling updates to all actions using a run condition on [`InputManagerSystem::Update`](crate::plugin::InputManagerSystem::Update).
/// 2. By disabling updates to all actions of type `A` using a run condition on [`tick_action_state::<A>`](crate::systems::tick_action_state).

I was reading these comments in src/action_state/mod.rs.. Is it possible to customise run conditions for already added systems in Bevy? O_o

#

I'm looking for a way to disable inputs for an action type globally without having to do it for each entity. It seems to me like there was the ToggleAction resource for that before. Now I'm not quite sure how to achieve that

karmic monolith
#

You can add but not remove run conditions

tawdry olive
#

oh cool! I didn't know that. What's the API for that? Do I just re-add a system or?..

karmic monolith
tawdry olive
# karmic monolith app.configure_sets(PreUpdate, MySystemSet)

Apparently, configure_sets accepts only system set configs (which I guess is expected), not sure how to make it work with indiviudal systems.. πŸ€”

.configure_sets(
    PreUpdate,
    tick_action_state::<PlayerMovementAction>
        .run_if(in_state(GameRunningState::Running))
)

that was my first attempt

karmic monolith
#

Want to make a PR to add a type parameterized system set to the tick system?

#

I'll make a minor version release tomorrow if you do

tawdry olive
tawdry olive
karmic monolith
#

@storm anchor yes please to a 0.15 update PR!

candid vigil
#

@karmic monolith I've been looking into the Triggerlike PR

#

but I'm still thinking that maybe we could merge Triggerlike into Buttonlike

karmic monolith
candid vigil
#

And perhaps we need a new Res here

#

because we can't read the GamepadButton value from ButtonInput

#

Or is there a SystemParam in Bevy that can read two or more Res at the same time?

karmic monolith
#

Although I think we probably want to make SourceData require a SystemParam, not a Resource

candid vigil
karmic monolith
candid vigil
#

☹️ invalid system configuration

karmic monolith
candid vigil
#

πŸ€” perhaps we could pass a function instead of I::compute to update CentralInputStore?

#

this way might free us from the limitations of the UpdatableInput trait

candid vigil
#

what are the potential risks of doing this?

#

I know it's a quick and dirty solution

#

but I think it could reduce the restrictions on how we update inputs

#

just write them like normal systems

storm anchor
#

@karmic monolith how do you want to identify gamepads across runs? I see that there is a GamepadInfo::name but that doesn't seem unique to the gamepad (the docs say it's stuff like HID-compliant game controller). Entity IDs obviously won't work across runs.

In general it'd be nice if there were the joystick number in GamepadInfo. This is needed for consoles.

karmic monolith
#

But I'm not sure how / if we can get that order in a stable fashion on PC

storm anchor
#

Apparently XInput assigns numbers "somehow"

#

probably best to defer to XInput

karmic monolith
#

We can use those then

storm anchor
#

Finally got it building after ~6 hours of work

karmic monolith
storm anchor
#

@karmic monolith I'm not going to submit the leafwing PR tonight, I have no motivation to implement stable gamepad IDs at this point

#

it really needs to be implemented in bevy

#

but I can't spend my whole weekend on a bevy upgrade 😦

#

leafwing is broken until we have stable gamepad IDs, not worth upgrading

storm anchor
#

maybe I can try to do it tomorrow, just frustrated at the number of regressions that gamepads-as-entities landed with 😦

errant coral
#

I'd image we want something similar to gamepads, where the gamepad ID is the number we get from XInput, so you can identify them regardless of the Entity spawned.

karmic monolith
karmic monolith
foggy osprey
#

At long last, three-axis inputs!! Time to go refactor every space game prototype I ever wrote

karmic monolith
#

And merging the backlog of breaking changes

severe arch
#

What's the goal with stable id's for gamepads?

#

I bet there is some way to get a usb ID or something from x11

#

but I'll take a look later

#

Oh nevermind should've read the whole convo about xinput

storm anchor
#

I have LWIM compiling and some of it is working, but some stuff isn't working yet

#

also the tests are broken

#

and there aren't new docs for the new stuff I had to add yet

#

yeah, just_pressed is broken

storm anchor
#

@karmic monolith oh, ok, I found the problem I think -- LWIM moved to the fixed schedule so sometimes systems that aren't on the fixed schedule can miss inputs (the just_pressed flag clears before the system has a chance to process it). This seems like a footgun. Do you have any thoughts?

karmic monolith
storm anchor
#

well, it's definitely busted right now

#

I don't know what the best way to fix it is. I was planning to work around it by adding my own AcknowledgedActions set and tracking it manually

#

but virtually every game will have to reinvent this logic so it seems very user-unfriendly

karmic monolith
#

Okay. Can you open a draft PR so we can take a look?

storm anchor
#

oh, I was just going to do it in my own app

#

working around the leafwing bug

#

because I don't know what the best way to fix it is

#

I mean, it seems like it's by design, right? If fixed can run faster than non-fixed then this will always be a problem

karmic monolith
storm anchor
#

is that documented anywhere?

storm anchor
#

yeah, adding logging, it's very messed up

#

wish I could find a regression range but that's hopeless due to how many other incompatibilities there are with 0.15

karmic monolith
#

Open a draft PR for the upgrade and I'll take a look

storm anchor
#

bevy seems to be ignoring the ordering leafwing wants for InputManagerSystem::Tick and InputManagerSystem::Update, did 0.15 change anything about system ordering?

karmic monolith
#

Is the order between those two systems wrong?

storm anchor
#

it seems to be, based on my logging

#

even adding more before and after constraints isn't fixing it

#

this is extremely confusing

karmic monolith
#

Okay, that's really quite bad πŸ€”

#

I can't imagine what else might have touched that

storm anchor
#

ok, commenting out all the fixed main loop systems seemed to fix it

karmic monolith
#

Okay, so maybe something changed with how fixed time is handled

#

I can't wait for this crate to get upstreamed

#

It really stress tests a lot of the unloved areas of the engine

storm anchor
#

gimme a bit, I have to comment out all uses of Hanabi in my project

#

something happened in the last 2 weeks on bevy that made Hanabi now fill up all GPU memory when you spawn a particle effect

karmic monolith
storm anchor
#

yeah, that has to be it

#

I wish retained render world had landed early in the 0.15 cycle, or maybe delayed until 0.16

#

are we sure that fixed main loop and main loop don't run concurrently?

#

I'm suspicious at this point

karmic monolith
storm anchor
#

oh, I think I found the problem

#

yeah, that was my fault

storm anchor
karmic monolith
foggy osprey
#

Is there a default asset loader for input maps?

#

Oh wait, InputMap is an Asset but we store them directly on things instead of Handles. Why are they assets then?

abstract meteor
storm anchor
#

I'm redoing the leafwing bevyup from scratch because I messed up the API

merry kraken
storm anchor
#

status: it builds and almost works, but serde-ing inputmaps is giving me an extremely confusing error

#

I'm diving into the guts of serde_flexitos and it's abject misery

#

live look at the bevy ecosystem right now with all the last minute breakage

karmic monolith
candid vigil
#

I'm a bit lost here

#

Can someone fill me in?

karmic monolith
#

I'm not sure what the exact struggles he's running into with serialization right now

storm anchor
#

fixed the problem. I still have no idea why it went wrong

#

I had to change the Identifier to Cow<'static, str> from &'static str

candid vigil
#

might this work?

#

Or this one?

#

I'm stuck on the triggerlike PR

#

Bevy seems to won't let me use associated types or generics as parameters in systems

#

So how do I pass a SystemParam as the second argument to the compute function in the UpdatableInput trait? πŸ€”

storm anchor
storm anchor
# candid vigil Or this one?

AFAICT the problem is that serde_flexitos wants to deserialize the Identifier type before looking it up in the trait map. But you can't deserialize an &'static str (without leaking, anyway). You can only deserialize a String or a Cow<str>. Because we set the identifier to &'static str, we couldn't deserialize it.

#

Changing to Cow<'static, str> fixed the problem by allowing deserialization, while still avoiding allocation when inserting the strings into the deserialization map.

candid vigil
#

ohh i see

storm anchor
#

Leafwing upgrade is mostly done, finally

#

After that, it's on to Hanabi breakage and then investigating why my normals are now all messed up

foggy osprey
#

I've got bevy_mod_picking shooting triggering actions, and a system in Update that's never seeing them. I'm assuming leafwing resets inputs during PreUpdate? Would that be TickActionStateSystem?

#

I assume the way to fix this is going to be to explicitly have mod_picking do its thing before lwim clobbers the action states

karmic monolith
#

Use that

merry kraken
midnight bramble
storm anchor
#

it's almost working I guess, but the unit tests are still busted because gamepads-as-entities broke mocking somehow

foggy osprey
#

We need a :bevy_on_fire:

karmic monolith
foggy osprey
#

When I add diagnostics it works 😭

#

Guess it's time to move this out of this channel

midnight bramble
foggy osprey
midnight bramble
#

and it works every time?

foggy osprey
#

It's always consistent when the diagnostic systems are in there

foggy osprey
midnight bramble
#

πŸ€” πŸ€” πŸ€”

storm anchor
#

hate the way essential APIs can just get removed and then adding them back leads to bikeshedding in the code reviews instead of just adding them back

foggy osprey
storm anchor
#

of course the review claims that gamepad connection events are sufficient but they are not

#

wonder if this is yet another piece of gamepads-as-entities brokenness

#

ah, ok, I think you have to send RawGamepadEvent::Connection now

#

this isn't well documented at all

#

oh no, that doesn't work either

#

now you have to send to Events<GamepadConnectionEvent>

storm anchor
#

noooooo, I had it all ready but leafwing updated and now it has merge conflicts

#

oh no, the VirtualDPad stuff wrecked my upgrade

#

I hate my life

storm anchor
#

the irony of a reviewer making me take out Gamepad::new just as code lands in leafwing that makes use of it

storm anchor
candid vigil
#

Ohh I see what you mean

#

but is this one a new thing added in bevy 0.15?

#

I found it

#

I just woke up πŸ˜‚

#

I'm not sure, but should the Triggerlike add to LWIM before or after bevy 0.15?

#

πŸ€” Is it worth to allow creating VirtualDPad3D from three VirtualAxes? as well as creating VirtualDPad from two axes

storm anchor
candid vigil
#

yeah I found these two in the current doc

merry kraken
#

I integrated that PR into my branch and it builds and appears to work. Thanks @storm anchor

storm anchor
#

leafwing tests seems to have broken again with the latest bevy changes and I have no more motivation to fix it, sorry

karmic monolith
storm anchor
storm anchor
#

it seems to be just the tests that are failing -- leafwing itself works fine

karmic monolith
#

Yeah, there's a chance that the tests were wrong, or too fragile

#

Or the accumulated input impl upstream was wrong

#

They didn't just copy-paste my code, despite my urging...

storm anchor
#

@karmic monolith pretty sure it's a system ordering issue

#

it's intermittent, sometimes tests succeed, sometimes they fail

karmic monolith
#

Ooh I can add ambiguity denial to our CI now that Bevy itself is clean πŸ˜„

storm anchor
#

I think it just means that sometimes stuff is 1 frame behind

#

which should be fixed, but isn't blocking me right now

karmic monolith
#

Once the release candidate is out I'll adopt and finish this PR πŸ™‚

karmic monolith
#

Nice to have it up though for others living on main

storm anchor
#

yeah, I also don't want you to have to redo my work

#

it was a lot

#

had to redo it 3 times πŸ˜…

merry kraken
# storm anchor it was a lot

I strongly appreciate that you did this. I was dismayed looking at the scope of work yesterday morning. πŸ˜„

karmic monolith
candid vigil
#

serialisation for trait objects is easy, the problem is when deserialisation, it is hard to determine the concrete type of the boxed trait object

candid vigil
#

How should we name the new struct that combines the button state (pressed or released) and the value?

#

ButtonState is used by both Bevy and LWIM for representing pressed and released

#

and ButtonData is a kind of ActionData in LWIM

candid vigil
#

☹️ Deriving SystemParam requires lifetime parameters, but the trait and the implementor don't have such parameters, causing "unconstrained lifetime parameter"

candid vigil
#

Perhaps need a small PR to add GAT to SourceData

candid vigil
#

yeah it works

#

πŸ€” Should I add button's value to ActionDiff::Pressed and then when value changed, generating a new pressed diff

#

or create a new variant called ActionDiff::ButtonValueChanged?

karmic monolith
candid vigil
#

So, when the button is from released to pressed and from pressed to released

#

should we trigger a ButtonValueChanged with the value?

karmic monolith
#

I think so, but @umbral linden will have more informed opinions

candid vigil
#

I'm not sure that there're cases like ButtonInput<Button> is pressed but Axis<Button> is 0.0

karmic monolith
candid vigil
#

Are there cases where the ButtonInput reports that it is pressed, but its axis value is zero?

#

and the ButtonInput reports that it is released, but it has a non-zero value?

candid vigil
karmic monolith
#

I believe that those cases are possible with thresholds, especially the latter

candid vigil
#

πŸ€” don't gilrs and bevy filter out those values after physical deadzones?

spiral dirge
#

A question about using leafwing (asking for Spooky Jam entry)
Say that we have a keycode map for keyboard keys but we also want to add a mouse one
Is there a better way other than making a new resource and spawning another entity with just that? (As the player already has a component of type InputManagerBundle

spiral dirge
candid vigil
#

insert?

woeful totem
#

how would i have smth like a "optional" toggle hotkey? my usecase would be allocating singular attribute points by pressing keys 1 to 0 but when the player holds some key "x" i want to preallocate them instead or with "y" i want to allocate the max points or smth like that

bold lodge
woeful totem
restive knoll
#

Can't you just check if B is pressed when executing X, and if it is run X1 instead? πŸ€”

woeful totem
#

Ah i guess im looking for clash strategy

#

or maybe not

woeful totem
restive knoll
#

I think there's two approaches:

  1. Make two actions, one for X, one for X1, and make X1 a chord of the modifier + the key
  2. Mke two actions, one for X, one for the modifier, then check the modifier in the code that executes X and branch to X1 instead
woeful totem
#

hmm actually yea ive been thinking too complicated lol

fallen verge
#

hi what is the best practices to add custom ActionData
I'm doing sth like this, but not sure if I am running the system in the correct schedule or if I am missing anything?

impl Plugin for AimPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, mouse_motion);
    }
}

fn mouse_motion(
    mut q_action: Query<&mut ActionState<PlayerAction>, With<InputTarget>>,
    q_player: Query<&Transform, With<LocalPlayer>>,
    q_camera: Query<(&Camera, &GlobalTransform), With<GameCamera>>,
    mut cursor_evr: EventReader<CursorMoved>,
) {
    let Ok(mut action) = q_action.get_single_mut() else {
        return;
    };

    let Ok(player_transform) = q_player.get_single() else {
        return;
    };

    let Ok((camera, camera_transform)) = q_camera.get_single() else {
        return;
    };

    for cursor in cursor_evr.read() {
        let cursor_world_position = camera
            .viewport_to_world_2d(camera_transform, cursor.position)
            .unwrap_or_default();

        let direction =
            (cursor_world_position - player_transform.translation.xy()).normalize_or_zero();

        let action_data = action.action_data_mut_or_default(&PlayerAction::Aim);
        action_data.axis_pair = Some(DualAxisData::from_xy(direction));
    }
}
barren fractal
#

Hi all πŸ™‚ Anyone know if there is some neat way to do both Axis navigation, along with using the numbers 1-2-3.. on the keyboard to map to the same axis?

This is for a toolbar that you can both scroll with mouse wheel, and use buttons to navigate to directly.

rain furnace
#

I also had a question: is there a way to get the associated bindings for a given action from either the action state or input maps? I would like to provide rebinding features in a game and I need to get this info during gameplay so I can show the player the proper keys that should be used during tutorials and stuff.

crimson iron
#

hello everyone, I'm using leafwing_input_manager, I'm looking through the code and documentation but it seems to me there is no way to record a user input and assign it to an action dynamically, am I wrong?

basically I want to do a settings menu where the player can change his key bindings, when clicking on a button he should be able to create a new key combination, I don't think that is possible wiht leafwing_input_manager atm

barren fractal
#

@crimson iron @rain furnace you are asking the same lol. Have you tried updating the InputMap or just overwriting the old input map by adding a new InputMap component?

#

To read the map i think you just get the InputMap comonent?

crimson iron
#

I'm doing a poc rn yeah haha

#

I'll keep you updated

barren fractal
#

The InputManagerBundle consists of two components, ActionState and InputMap. It has hasmaps with all the mappings. Maybe not the easiest to directly do input changes from this, so you could do your own UiInputMap or somthing, that updates the InputMap when changed.

rain furnace
#

I was asking something different, I need to get which key is bound to a certain action and display that to users. Should I just do input_map.get(Action) on the input map or something? I didn't see something like this in the docs.

barren fractal
#
ub struct InputMap<A: Actionlike> {
    /// The underlying map that stores action-input mappings for [`Buttonlike`] actions.
    buttonlike_map: HashMap<A, Vec<Box<dyn Buttonlike>>>,

    /// The underlying map that stores action-input mappings for [`Axislike`] actions.
    axislike_map: HashMap<A, Vec<Box<dyn Axislike>>>,

    /// The underlying map that stores action-input mappings for [`DualAxislike`] actions.
    dual_axislike_map: HashMap<A, Vec<Box<dyn DualAxislike>>>,

    /// The underlying map that stores action-input mappings for [`TripleAxislike`] actions.
    triple_axislike_map: HashMap<A, Vec<Box<dyn TripleAxislike>>>,

    /// The specified [`Gamepad`] from which this map exclusively accepts input.
    associated_gamepad: Option<Gamepad>,
}

@rain furnace This is the InputMap struct, so you can see it's a hasmap. You can iterate over it to find the, buttons, axis etc.

HashMap<A, Vec<Box<dyn Buttonlike>>> maps ActionLike to a Vec of buttons

#

That's why I said, maybe it's best to have a separate struct that stores the mappings so it's easy to display in the UI, so that you can just reconstruct the InputMap when you edit something in the UI

rain furnace
#

Alright I'll take a look into that. Thanks for the suggestion also, could be helpful to make it easier to display yeah.

leaden breach
#

Question can I use my Input states for my animations I am newbee and dont know if that would be a problem?

half heart
#

I have a question, so basically, I'm following along the examples and trying to update my input to 0.15, and the example for a dpad mentions using VirtualDPad however in my code here I get an error saying that VirtualDPad doesn't exist

    .with_dual_axis
    (
        player_resources::Action::Move,
        (
        GamepadStick::LEFT,
        VirtualDPad::new(
            (KeyCode::KeyW, KeyCode::ArrowUp, GamepadButtonType::DPadUp),
            (KeyCode::KeyS, KeyCode::ArrowDown, GamepadButtonType::DPadDown),
            (KeyCode::KeyA, KeyCode::ArrowLeft, GamepadButtonType::DPadLeft),
            (KeyCode::KeyD, KeyCode::ArrowRight, GamepadButtonType::DPadRight),
        ), 
        )
    );```
#

the exact error message is
error[E0433]: failed to resolve: use of undeclared type `VirtualDPad` --> src\player\mod.rs:38:9 | 38 | VirtualDPad::new( | ^^^^^^^^^^^ use of undeclared type `VirtualDPad

foggy osprey
#

Can check tomorrow if you still haven't found it and ping me

foggy osprey
storm anchor
#

FYI I'm having problems with the fact that processors isn't public. (I need it in the controls UI.)

I'm going in through the bevy_reflect backdoor but there should probably be a better API for it upstream?

karmic monolith
#

@candid vigil can we just make this pub?

candid vigil
#

sure

storm anchor
#

I also had to go in through the Reflect back door with GamepadStick::x

storm anchor
#

Same with VirtualDPad directional fields

#

Reflect is such a nice feature of Bevy to work around dependencies that forget to make things public πŸ™‚

#

Actually, Reflect doesn't work for that one so I have to just nuke the VirtualDPad and recreate it every time the user rebinds one button of it 😦

full iris
#

Any ideas for supporting Picking pointers and buttons in LWIM?

leaden breach
karmic monolith
karmic monolith
full iris
#

LWIM has stuff for combo keys, like shift + click

#

so just asking whether there are plans to integrate one with the other

karmic monolith
#

Loosely!

#

It's a good idea

foggy osprey
leaden breach
#

Question is this how we define two different binding, to dual axis?>

impl CharacterAction {
    fn default_input_map() -> InputMap<Self> {
        let input_map = InputMap::new([(Self::Jump, KeyCode::Space)])
            .with_dual_axis(Self::Move, KeyboardVirtualDPad::WASD)
            .with_dual_axis(Self::Move, KeyboardVirtualDPad::ARROW_KEYS);
        return input_map;
    }
}```
half heart
#

I'm trying to get my input updated to LWIM 0.15 and I'm getting the errorthe trait bound `(GamepadStick, KeyboardVirtualDPad, GamepadVirtualDPad): DualAxislike` is not satisfied the following other types implement trait `DualAxislike`: DualAxislikeChord GamepadStick GamepadVirtualDPad KeyboardVirtualDPad MouseMove MouseScrollrustcClick for full compiler diagnostic

my code is

    .with_dual_axis
    (
        player_resources::Action::Move,
        (
        GamepadStick::LEFT,
        KeyboardVirtualDPad::WASD,
        GamepadVirtualDPad::DPAD
        )
    );```
candid vigil
#

you can't put them in a tuple, you need to use the method to bind them separately

latent nacelle
#

I'm using main branch on LWIM , now that VirtualDPad::new takes impl Buttonlike I thought I would be able to use either keyboard or mouse inputs but if I understand correctly the type has to be known at compile time to use this method? The inner part of the struct uses Boxed values but since they are pub(crate) I can't use the regular struct constructor.

midnight bramble
karmic monolith
latent nacelle
#

Just noticed there was an issue to make VirtualDPad fields public anyway, I'll take a look at it

latent nacelle
violet dock
#

I'm struggling a little bit with toggling between multiple different action states. I want to press tab to disable the current action state and enable another, and then also allow tab to be used to disable the new action state and go back to the old one.

This inevitably seems to lead to an instant transition back and forth between the states, no matter what I try with action disabling, resetting, and system ordering. Is there a good pattern for doing this type of stuff?

bold lodge
violet dock
bold lodge
violet dock
#

yes, i've solved it through other means now. it's not super pretty but i gotta ship the game at some point πŸ˜… thanks for experimenting though!

versed pier
#

Anyone found a solution to this? https://github.com/Leafwing-Studios/leafwing-input-manager/issues/592 Trying to understand what @karmic monolith means here but Can't find a similar example to that. My game controls is through the mouse not the keyboard, at least mostly.

GitHub

I'm switching from natively serialized inputs to leafwing, and I realized that the ActionLike macro removes the ability to push data with your inputs. Before I had something like this: enum Inp...

restive knoll
violet dock
swift summit
violet dock
#

yes, in two separate action states

restive knoll
#

If you disable an entire action state you can't get just_presseds from them anymore ... But you need to track a bit of state anyway since your code should be setting what context it is in, not messing with states directly all over the place

swift summit
#

hm i feel like action_just_pressed should only be triggered if the button is pressed while the action state is enabled, but i might be contradicting past pyrious idr

restive knoll
#

I think the disabled code just makes everything return as not being pressed

swift summit
#

yeah, so at least just_pressed doesn't happen on every disabled -> enabled edge while the input is held down

#

but it seems here it's happening if the input is just pressed on the same frame as the disabled -> enabled edge

#

so not contradicting past pyrious necessarily, i just wanted the former issue to be fixed but didn't consider the latter

violet dock
#

either way, i think it would make sense to have some sort of abstraction for moving between different action states contextually

restive knoll
#

Yea that does seem reasonable. LWIM is currently still very low level in that regard, which is kind of a bad thing because not having features like this built in is how games get those weird bindings for 7 actions instead of having each of them be rebindable separately πŸ˜‚

swift summit
#

i use States for this so the state change doesn't happen until the next frame :p

restive knoll
#

Depends on where you process your input, disabling has an immediate effect but enabling happens later. State transitions happen between PreUpdate (where LWIM runs) and Update, so if you use the inputs in Update only enabling has a 1 frame delay at picking up inputs (which is definitely not ideal, but at least there should be no weird bugs)

#

In previous versions disabling wasn't immediate either tho πŸ€”

swift summit
#

you're saying if it goes LWIM updates, state transitions which enables an action state, and then input is handled during Update, that will cause a 1-frame delay?

#

i thought enabling was immediate

#

it's just toggling a bool that hides the actual button state iiuc

restive knoll
#

I think we avoid updating inputs for disabled state, if we don't there is another issue where action states could clash on inputs with disabled states πŸ€”

swift summit
#

inputs are updated for disabled action states now, that was a bug fix

#

what's the clashing issue? maybe i'm misunderstanding something

restive knoll
#

If a disabled state has an input mapping for ctrl + A, and the active one has A, then the A one should trigger, but if they are both enabled only ctrl + A should trigger

swift summit
#

interesting.. i would have expected it to be an LWIM-user-configurable thing, whether you want to accept an optional modifier + A or only accept no modifier + A

#

as opposed to being dependent on the other (enabled) mappings

#

idk how other software handles that

karmic monolith
restive knoll
#

If you weren't going to have potential clashes between them you could've just run conditioned all your systems that handle the inputs after all

swift summit
leaden breach
#

How do I set my own values for dual_axis, like change the vector being sent by the action?

violet dock
#

the amount of disabling and resetting and state trickery I have to do to get three action states to play nicely together is going to make me cry. I might just have to do something more thought out to deal with this.

foggy osprey
#

@violet dock idk if this will help, but I have an RTS that uses contextual bindings by moving the relevant components off the entity rather than adding a run condition to the handling system. Something like this:

fn enable_context_1(e: Entity, world: &mut World) {
    let e = world.entity_mut(e);
    let (context1, Disabled(context2)) = e.take::<(Context1, Disabled<Context2>)>().unwrap();
    e.insert((context2, Disabled(context1)));
}

This function is an entity command, so you can just commands.entity(e).add(enable_context_1) when you need to switch contexts.
Disabled is literally just:

struct Disabled<C>(C);

Then you can do:

#[derive(Bundle)]
struct Context1 {
    input_map: InputMap<MyAction>,
    // optionally the action state, but not necessary I don't think? Might have to play around and see
    action_state: ActionState<MyAction>,
}
leaden breach
#

Let say I want to make it so move, vect is directed to cam.forward, cam.back and such, so in summary make the character move based on camera direction. How can I make that using leafwing?

#
fn move_toward_camera(
    mut query: Query<&mut ActionState<CharacterAction>>,
    cam: Query<&Transform, With<MarkerMainCamera>>,
) {
    for mut action_state in &mut query.iter_mut() {
        if let Ok(cam_transform) = cam.get_single() {
            let cam_forward = Vec2::new(cam_transform.forward().x, cam_transform.forward().z);
            let cam_backward = -cam_forward; // Opposite of forward
            let cam_right = Vec2::new(cam_transform.right().x, cam_transform.right().z);
            let cam_left = -cam_right; // Opposite of right

            action_state.set_axis_pair(&CharacterAction::Move, cam_forward);
        }
    }
}
#

Like for example in this function, how can I set up the axis pair according to the subtype of button that was pressed for example w, = cam.forward

midnight bramble
#

all it's supposed to give you is a vec2 from any kind of input device

karmic monolith
#

Yep, this should be handled in your game's code πŸ™‚

storm anchor
#

Is there a recommended way to temporarily disable a device class of input (keyboard, mouse)?

XY: I want to disable keyboard or mouse inputs for a frame if the egui context is handling the events. egui provides a pair of methods wants_keyboard_input and wants_pointer_input that you can use to check to see whether egui has the focus that frame. I was thinking I could have a system that runs before leafwing, checks the egui status, and disables the devices in leafwing, but I don't know what the best way to disable the devices is.

#

I figure this should be handled at the leafwing level because it is the device class, not action type, that needs to be suppressed, and leafwing is supposed to be an abstraction that maps device classes to action types.

leaden breach
foggy osprey
#

Tbh sounds like a job for bubbling (at least eventually)

storm anchor
karmic monolith
#

Or clear it

#

Also there's an egui compat feature flag to solve exactly this

#

The ultimate fix here is to expose real focus management in Bevy

storm anchor
storm anchor
#

argh, now in dependency hell because leafwing's egui is a different egui from my egui 😦

karmic monolith
#

But you can probably cargo patch LWIM's version

storm anchor
#

I downgraded leafwing's version locally

#

not what I wanted to do but I just wanted to get unstuck quickly

karmic monolith
#

Valid. I really hate the egui -> leafwing dependency, and am eager to get a solution in place that doesn't require them to talk to each other

storm anchor
#

@karmic monolith actually the integration is just broken

#

I'll submit a PR

#

actually, never mind, I don't think it's broken, it's just 0.15 fallout

errant coral
karmic monolith
#

Oh right, that's a good idea

acoustic locust
#

I understand that it's not 0.15 rc if nobody puts in the effort (saw the open PR), but I expected it to be 0.14 and not an rc... Sorry for the dumb question. I am sick and it's 4:55 in the morning here.

restive knoll
acoustic locust
#

Yes, but why 0.14 rc (!)

#

bevy = { version = "0.14.0-rc.3", default-features = false, features = [
"serialize",
] }

#

I would understand 0.15.0-rc.3 or 0.14 but 0.14.0-rc.3 seems weird.

#

Oh sorry, my problem came from thinking that resolving something with a suffix "rc.3" would not update to the released version... Totally my bad, paired with not reading correctly. Sorry!

restive knoll
#

When 0.14 exist, 0.14.0-rc.3 will pick up 0.14

acoustic locust
#

Yes, for "x.y.z" and "0.14.0-rc.3", z will be "0-rc.3"... I thought it would just match 0 and then not update to released

#

The PR for 0.15 is not finished. From the dev version to the current rc it seems there were some changes. Couldn't figure them out yet. Now I'll fall asleep. Thanks again and sorry! I should not ask questions right now

leaden breach
#

Question how do I make a dash, like check if key + other key pressed?

#

I only know how to do map input to one key

#

:<\

karmic monolith
#

Those are called "chords" in LWIM πŸ™‚

cold peak
#

@karmic monolith can I not reflect actionlike structs anymore?

#[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)]
pub enum CameraMovement {
    // Abilities
    #[actionlike(Axis)]
    Zoom,
    #[actionlike(DualAxis)]
    Pan,
    SwitchInputType,
}```
karmic monolith
#

I haven't published the new LWIM yet because I'm moving across the country

cold peak
karmic monolith
tame current
#

The main branch is still 0.14 tho

merry kraken
#

You might be able to get away with using my fork for now:

leafwing-input-manager = { git = "https://github.com/dead-money/leafwing-input-manager", branch = "bevy-main", features = [
    "egui",
] }

Gamepads and Reflection stuff probably won't work cos its super hacked, but it works for mouse and keyboard and got me moving forward on my migration.

placid furnace
#

It seems 0.15.1 has a breaking change by adding a new entry to ActionDiff TripleAxisChanged. This caused a build failure for me because lightyear targets 0.15, but when I added leafwing it went with 0.15.1, and then a match in lightyear broke because it didn't mention TripleAxisChanged.
I think it's only a backwards compatible change to add a new enum variant to a non_exhaustive enum.
For now I'll just force it to be 0.15, but thought I'd let you know

umbral linden
#

I'm getting an error that ActionState is not a component; that's probably due to mismatched bevy versions right?

placid furnace
#

No worries! Breaking change detection still isn't trivial, which is a bit of a shame

karmic monolith
#

Ha, I knew better for this one

violet dock
trim wigeon
#

what is the intended way to use inputmaps and actions? i just have an GameInputMaps resource that could contain different input maps (so far it just has a main: InputMap<MainAction> member, where MainAction is my own Action-derived enum).
for example, the player does this when setting up its input stuff:

input: InputManagerBundle::with_map(input_maps.main.clone()),
(where input_maps is gotten through input_maps: Res<GameInputMaps>, as a system argument).

#

is it that big of a sin to have everything in a big, monolithic Action struct, rather than having PlayerAction, UiAction, etc?

#

AND, if i do switch to having it more organised, should the action maps be in the same file as the things intended to use them? (i.e., PlayerAction located in src/player.rs, and UiAction located in src/ui.rs)

karmic monolith
#

But if you split them into different Actionlikes, it's way easier to toggle them on and off as you need to

trim wigeon
#

thanks alice!

cold peak
#

@karmic monolith how goes the move?

karmic monolith
#

No furniture or WiFi yet though so uhh may be a few days until I'm properly productive

open trout
#

time to 3d print all your furniture

karmic monolith
#

Extremely efficient and eco friendly

#

There's a used furniture charity shop nearby that we're probably going to "rent" some from

open trout
#

just 3d print solar panels first, then power the printer off those

#

and when you move again, print a crucible and melt down all your furniture back into filament to pack up

karmic monolith
#

And a hydroponics setup

open trout
#

hmmm true

merry kraken
karmic monolith
merry kraken
#

Haha

#

Some people just want to watch the world burn.

karmic monolith
#

No I'm going to buy some cheap furniture and then donate it back in a month lol

merry kraken
#

That's a good plan.

karmic monolith
#

Yes πŸ˜„

rugged mica
#

can't wait for bevy 0.15 support πŸ₯Ί

cold peak
trim wigeon
#

so, i got this crash when trying to get the value of a button-like control:

thread '<unnamed>' panicked at /home/colleen/.cargo/registry/src/index.crates.io-6f17d22bba15001f/leafwing-input-manager-0.15.1/src/action_state/mod.rs:594:14:
assertion `left == right` failed
  left: Button
 right: Axis

the mentioned file does indeed have this assert for ActionState::value():

debug_assert_eq!(action.input_control_kind(), InputControlKind::Axis);

however, the documentation states the following:

Binary buttons will have a value of 0.0 when the button is not pressed, and a value of 1.0 when the button is pressed.

karmic monolith
trim wigeon
#

i'd prefer it over as i32 as f32

karmic monolith
trim wigeon
#

ah i see

karmic monolith
#

It led to weirdness like trying to define what it meant for an axis to be pressed

#

Or problems where you'd refactor and things would kinda work in silent ways

trim wigeon
#

oh yeah, and now that i think about it, that is kind of in disregard to semantics

karmic monolith
#

It's okay to pun on it like that, but it should probably be explicit in your code

trim wigeon
#

yeah exactly

#

if to a developer, an axis being presses means being > 0.125 with a velocity >= 0.1/s, then they should manually define that

trim wigeon
karmic monolith
#

That would be super appreciated ❀️

trim wigeon
#

p.s. - i hope you get settled into (your new home?) :D

karmic monolith
#

The refactor was a huge pain

karmic monolith
#

There's wifi, one chair each and a cardboard box to use as a table

trim wigeon
#

those are the highest stakes for spilling coffee i've ever heard

karmic monolith
#

Rest of the stuff is in a truck hurtling across the prairies. Or sitting in a shipping yard in Montreal still, who knows πŸ™ƒ

karmic monolith
trim wigeon
karmic monolith
#

Lots of input mocking

#

Since we're a layer or two above the hardware it isn't too bad

trim wigeon
#

oh awesome. and also my condolences haha

karmic monolith
#

Yeah. It's great, but definitely makes the big refactors both more and less painful

#

Sometimes it's like "am I glad this found a bug??"

trim wigeon
#

is there any way i can currently make a new device / input action type? like, what if i have a way from the steamworks API to poll the state of the deck's R4 button, or the position on the right trackpad? does implementing Buttonlike/Axislike "just work"?

#

it would be awesome to make like an "extension" that includes steamdeck stuff all set up for leafwing input manager - like all your buttonlikes and axislikes that you need for steamdeck. something i'd be willing to work on if this is possible, since i plan to target the steamdeck.

#

(i dont expect help with this rn, just wondering if this is possible πŸ˜„)

karmic monolith
#

If you run into problems, please let me know / fix it

#

The trait based design is explicitly designed to support this style of extension

trim wigeon
#

the latest release version of leafwing-input-manager (can we start calling it LIM?) is just behind

#

bad news: this beautiful Issue will never see the light of day πŸ˜”

rugged mica
trim wigeon
#

ohh yeah ive seen lwim before

midnight bramble
#

it's supposed to be eventually upstreamed but i feel like everyone will still keep calling it LWIM D;

karmic monolith
#

There's a bit of cleanup to do and it would be nice to push to their branch

rain furnace
#

Oh that'd be me πŸ˜… there's some tests we skipped to merge this that we should try to figure out since it's tied to wrong values/statuses in Gamepads

#

If I have some time this week I'll keep digging but help will be appreciated as I'm not really familiar with the internals of the library

karmic monolith
rain furnace
karmic monolith
#

I love y'all ❀️

#

Hugely appreciate the help maintaining this crate

leaden breach
#

Wait is leafwing 0.15 released?

umbral linden
#

The leafwing version for bevy 0.15 isn't released yet

karmic monolith
#

Not yet, but taking a look today πŸ™‚

karmic monolith
leaden breach
#

GLORY TO ALICE oop oop

karmic monolith
#

Ha, I did like 20% of the work this cycle

untold thorn
#

what happened to consuming actions between the version for Bevy's 0.14 and LWIM main? i was depending on consume_all, not sure yet what took its place (might have missed it)

violet dock
untold thorn
# violet dock Action disabling/enabling is the replacement, I believe

Actually the fix for me was a weird one: just do nothing. The problem was that once I upgraded i was pressing on a button to go to the next state, the UI would disappear (so it couldn't block any more) and it was immediately clicking the tilemap underneath, where the button used to be. So using disable / reset would let the continued press "leak" to the next state because it would release in between. By doing nothing (I stumbled on it totally randomly) it worked because it wouldn't release at all and wouldn't "leak". Anyway, thanks for the answer!

violet dock
untold thorn
violet dock
#

Yeah I use multiple ActionLikes for this purpose

crimson oasis
#

Are there examples or idioms for assigning multiple Actionlike enums to one entity?

karmic monolith
#

And more copies of the plugin

#

It'll work totally fine

#

You might want a controlling state machine enum or something on it

#

Which then enables / disables the actions

crimson oasis
#

Oh, can you add the same generic component for different types?

#

(to an entity)

karmic monolith
#

Yep!

#

Foo<A> and Foo<B> have different types so you're all good

gray cedar
#

I have two questions.

First, is there a way to get LWIM to go into a mode where it gives me any input received whatsoever? I'm implementing a key remapping feature and while I could make a system with Query<&Gamepad> and EventReader<KeyboardInput> and all the others, there's no need for me to reinvent the wheel if LWIM already has utilities for this.

Second, is there a way, once you have an input, to quickly check that it's not already bound to something else? Again, I could grab the relevant InputMap<T> and then iter_x() through all the various input types and compare the input given with all of them, but again, it'd be nice to be able to avoid it, especially if the feature already exists.

#

For the first one, it looks like I would've been able to do it if the CentralInputStore let me look into its updated_values values, but you can't directly do so, only indirectly through queries like cis.pressed(&KeyCode::KeyA), but I don't want to have to do one of those for every possible button.

gray cedar
#

If there's any interest; I suppose in particular by Alice, I wrote some code that lets me do this:

for key in cis.all_pressed::<KeyCode>() {
    println!("{key:?}");
}

(where cis is a Res<CentralInputStore>) which produces out put like this when run in a system:

KeyT
KeyT
[5 more times]
KeyE
KeyE
[6 more times]
KeyS
[8 more times]
KeyT
[6 more times]
Digit1
Digit1
[9 more times]
Digit2
Digit2
[7 more times]
Digit3
Digit3
[8 more times]
Enter
[11 more times]
ShiftRight
ShiftRight
ShiftRight
ShiftRight
ControlRight
ControlRight
[5 more times]
LaunchApp2
[7 more times]

(There's some need for debouncing if the consumer needs that, of course, but since I only really care about the very first time somebody pushes a button, I don't really care about this for my use case.)

karmic monolith
#

I think that we should do that

#

For the second, no current solution, but I'd rather like it

crimson oasis
#

Has anyone tried sending LWIM components over the network with associated metadata? Like what window an input would belong to, given the network maintains a mapping of window entitys between clients ofc

Curious if anyone had done it before

umbral linden
#

I'm mapping LWIM components over the network using lightyear

#

I'm not mapping the metadata directly, but i'm keeping track of the entity mapping between source world and client world

gray cedar
#

I'm running into another issue. I have this ActionLike for which I produce some default bindings, something like

fn default_game_input_map() -> InputMap<Game> {
    InputMap::new([
        (Game::Up, KeyCode::KeyW),
        (Game::Up, KeyCode::ArrowUp),
        (Game::Left, KeyCode::KeyA),
        (Game::Left, KeyCode::ArrowLeft),
        // etc.
    ])
}

which I use to "seed" a player's input settings if they haven't played the game before. Since I've implemented key re-binding, their list might differ from this one after they initially run the game.

Now let's say I add a new action to Game. Obviously I'll add some new default bindings for it, but I want to make sure any existing players get them too. I can't use InputMap::merge, because if they've rebound KeyW to KeyY, say, the merge will add back the KeyW.

Next, I tried doing this:

let mut default_input_map = Self::default_options_input_map();
for (action, bindings) in default_input_map.iter_buttonlike() {
    if self.options_input_map.get_buttonlike(action).is_none() {
        for binding in bindings {
            self.options_input_map.insert(*action, binding.clone());
        }
    }
}

but that's no good either;

error[E0277]: the trait bound `std::boxed::Box<dyn leafwing_input_manager::user_input::Buttonlike>: leafwing_input_manager::user_input::Buttonlike` is not satisfied
   --> src\settings.rs:148:60
    |
148 |                     self.options_input_map.insert(*action, binding.clone());
    |                                            ------          ^^^^^^^^^^^^^^^ unsatisfied trait bound
    |                                            |
    |                                            required by a bound introduced by this call
    |
    = help: the trait `leafwing_input_manager::user_input::Buttonlike` is not implemented for `std::boxed::Box<dyn leafwing_input_manager::user_input::Buttonlike>`

Since I already am running a custom version of LWIM from before,

#

(my CentralInputStore:all_pressed mentioned in my previous Q), I just added a insert_raw to it as well that lets me pass a Box<dyn Buttonlike> directly, but it sucks that I need to resort to custom hacks like that. Is my use-case unsupported?

calm gate
#

I'm porting over some code to bevy 0.15 and new lwim and bevy_picking. I have ui buttons that when clicked mutate the ActionState flipping some action to pressed. This used to work (perhaps by dumb luck) just fine but now I found I had to do some system ordering invocations configure_sets(PreUpdate, PickSet::Focus.after(InputManagerSystem::Update)) to make it work

#

I love that bevy lets me muck around with the plumbing like that but I'm also bit worried this will come back and bite me

#

is there a better way of doing this?

karmic monolith
#

(although probably flip it for clarity)

calm gate
#

sure thing πŸ™‚

trim wigeon
#

is there any way i can treat mouse scroll up/down as virtual button inputs?

umbral linden
#

Let's say I want to network an input like 'Click On Another Entity'.
How would that work?
Would my ActionLike look like:

enum Actions {
   ClickOnEntity(Entity)
}
```?
leaden breach
half viper
#

I have split my game up into smaller substates, and in the main game loop I want to have some common controls (i.e., move camera), but depending on the state of the game, I want to reuse the same buttons for different actions. Is this possible? I think in my refactoring, I've broken everything πŸ™ƒ.
I now have multiple enums that implement ActionLike - a 'common' one that's in theory always present and has no conflicts with any of the others, and ones that conflict with each other but are only present during their respective 'mode' (I think - would need to triple check this).

I mostly just wanted to sound check this implementation, is it reasonable? Over engineered? etc

bold lodge
restive knoll
karmic monolith
#

Yeah, this is pretty reasonable

half viper
#

Sweet, thank you all! I'll assume for now that I've somehow registered conflicting ActionLike things and take a look with fresh eyes πŸ™‚

half viper
#

So they definitely currently conflict (skill issue on my part). The issue I have is they're all resources. So, do ActionLike things have to be resources?
I expect the user to be cycling through the substates often. Should I be removing/readding the resource frequently? It feels wrong, maybe this is more of a resource management question, but I've looked at the docs and can't see anything around disabling the resource

#

Or is nested enums a simpler solution?

gray cedar
#

For instance, my input handler for the main menu is registered as

.add_systems(
    Update,
    (handle_input, update_cursor_position)
        .in_set(MainMenuSet)
        .run_if(in_state(MainMenuState::Root)),
)

and my MainMenuSet is configured as MainMenuSet.run_if(in_state(AppState::MainMenu)), so this input handler only runs as long as we're in the main menu.

half viper
#

I need to read up on sets, but that looks good for me, ty! The nested enums was getting nasty

gray cedar
#

I might've missed something, but as far as I know, they're just a grouping mechanism for systems and not really pertinent to my example apart from being how I've structured my code -- could've put the .run_if directly on the system, too. πŸ˜„

half viper
#

True, it's not, but it's something else I'm taking away from this πŸ™‚

gray cedar
#

There's also the alternative of putting the ActionState as a component on an entity, which lets you disable and enable them through spawning and despawning an entity.

half viper
#

That was one option I was exploring, but I don't think I've seen many examples of that

gray cedar
#

Oh, and I just found this in the docs. πŸ˜…

Disabling actions
Actions can be disabled in four different ways, with increasing granularity:

By disabling updates to all actions using a run condition on InputManagerSystem::Update.
By disabling updates to all actions of type A using a run condition on TickActionStateSystem::<A>.
By setting a specific action state to disabled using ActionState::disable.
By disabling a specific action using ActionState::disable_action.

half viper
#

Looks like I need to read docs better πŸ˜„

karmic monolith
#

I was proud of that explanation

half viper
#

Where abouts in the docs? πŸ˜…

gray cedar
half viper
#

Maybe I need to sleep - that was pretty obvious πŸ˜„ (thank you again!)

half viper
#

Found my issue. I have no idea if they really were 'conflicting' and breaking that way. It had simply been lost on me that I needed .add_plugins(InputManagerPlugin::<MyAction>::default()) πŸ€¦β€β™‚οΈ

shut briar
#

Hmm, I seem to have a problem using any UI framework, for example Bevy HUI but also Cobweb UI and leafwing input manager.

Simply, my leafwing actions all seem to stop working once I initiate any UI nodes? Even just my "wasd" controls.
Mouse clicking handling using bevy's internal mesh picking API works fine, but leafwing actions somehow get stolen by the UI I guess?

Any ideas?

shut briar
#

I was able to fix this issue by deleting a camera2d I was spawning manually in addition to my camera3d. So, all good now!

edgy crest
#

Y'all, is leafwing input upstreamed in bevy or should I add the crate when starting a new game?

half viper
#

It's normally one of the first third party plugins I install with bevy. Some template repos will have it installed for the same reason

edgy crest
#

Y'all, I heard somewhere that bundles are getting deprecated in future Bevy. If that's the case, how would you implement this example without bundles? https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/examples/arpg_indirection.rs

#[derive(Bundle)]
struct PlayerBundle {
    player: Player,
    slot_input_map: InputMap<Slot>,
    slot_action_state: ActionState<Slot>,
    // We do not need an InputMap<Ability> component,
    // as abilities are never triggered directly from inputs.
    ability_action_state: ActionState<Ability>,
    ability_slot_map: AbilitySlotMap,
}

When I try doing this (enum names slightly different in my version than example)

fn setup(mut commands: Commands) {
  commands.spawn((
    Pawn,
    ActionBindings::default(),
    InputManagerBundle::with_map(ActionBindings::default().into()),
    ActionState::<Action>::default(),
    ActionState::<Ability>::default(),
  ));
}

I get an error:
Bundle ... has duplicate components: leafwing_input_manager::action_state::ActionState<dig::input::Action>

midnight bramble
edgy crest
#

Excellent, I went with this little bundle and it works great:

#[derive(Bundle)]
pub struct InputBundle {
  input_map: InputMap<Action>,
  action_state: ActionState<Action>,
  ability_action_state: ActionState<Ability>,
  bindings: ActionBindings,
}
quartz wedge
#

I have my inputs mapped so left/right arrows rotate at normal speed and shift+arrow rotates at a slower speed. however, when i am holding left, then start holding shift to slow my rotation, it doesn't change. i have to be holding shift before pressing the arrow. is there a way to change that behaviour?

    let mut input_map = InputMap::new([(ShipAction::Left, KeyCode::ArrowLeft),...]);
    input_map.insert(
        ShipAction::TinyLeft,
        ButtonlikeChord::new([KeyCode::ShiftLeft, KeyCode::ArrowLeft]),
    );
umbral linden
#

I think i asked this already but I forgot the answer; is it possible to associate some additional information to an ActionLike?
For example an action would be Stab(Entity) in the ActionLike enum instead of just Stab

peak aspen
#

Hi, I'm migrating to the new "trait based" leafwing input manager, and I'm quite stuck as I have no way of knowing what is inside an InputMap. It used to be a concrete enum and I could easily know what concrete input was stored and display the matching icon.
Now it's no longer possible, as everything is a dyn InputLike/ButtonLike/etc

Whats is the intended way of doing this now ? Maintaining a separate map ?

karmic monolith
#

I'm second-guessing the fully trait-based design πŸ˜…

peak aspen
#

I didn't dive into the refactoring PR yet but I guess trait based design brings more flexibility and custom inputs.
Maybe a hybrid system with concrete enums and custom boxed dun variants could bring similar flexibility?

karmic monolith
#

The nested enums approach was not really viable

#

But this approach causes too much pain for serialization IMO

#

I also really like the current split between the different flavors of input (button like, axislike etc). That's way clearer!

peak aspen
#

Having the input map generic over the action, the buttons, and the axis could work. We could have multiple input maps per type of input (mouse, keyboard, gamepad, custom) and still have them trigger the actions as now.
The traits would remain but the concrete types could still be accessible

karmic monolith
#

Which sounds fine, until you remember that CTRL+Click exists

peak aspen
#

Having separate enums for buttonlike/axislike/dualaxis with a Custom(Box<dyn>) variant is not possible?

karmic monolith
#

I think I might have been confused there

restive knoll
karmic monolith
karmic monolith
#

Okay, so let's talk through the design here

#

The problems we want to solve are:

a) serializing and deserializing trait objects is ass, but we need to do it for saving settings
b) it's too hard to figure out what type of input the user has bound, and then display a prompt
c) the axislike input processors are super powerful, but it's not clear that serializing them is a good use of complexity
d) it's hard for users to quickly understand the supported types

#

That said, I do really like the Buttonlike/Axislike etc split

#

And the corresponding traits

#

And I would like to make sure this is still extensible

bold lodge
#

@karmic monolith have you tried my approach with bevy_enhaced_input?
I think it's incredibly powerful and very easy to use.

The idea is very simple - I don't split inputs to traits at all and instead rely on actions to modify the inputs however they like.

karmic monolith
#

I'll take a closer look

#

Actions modifying inputs feels backwards to me though: why shouldn't they just be consumers of inputs?

bold lodge
#

Just run simple example and see comments. It's very easy to pick up.

karmic monolith
#

Proposal 1: enums with a Custom variant

Define a set of nice enums for each flavor of input.

/// An enum representing an [`Axislike`](crate::user_input::Axislike) input.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
pub enum AxisInput {
    /// Gamepad stick input,
    /// created by moving a stick on a gamepad along a single X or Y axis.
    GamepadControlAxis(GamepadControlAxis),
    /// Mouse wheel input,
    /// created by scrolling the mouse wheel along a single axis.
    MouseWheel(MouseWheelAxis),
    /// Mouse move input,
    /// created by moving the mouse along a single axis.
    MouseMoveAxis(MouseMoveAxis),
    /// Other custom axis-like input.
    Custom(Box<dyn Axislike>),
}

InputMap stores these enums, and all of the APIs to define input maps accept them. Internals stay mostly unchanged.

bold lodge
#

I wish I could create a thread here 😒

karmic monolith
#

So you're not writing back to the raw Events<KeyCode> or whatever, you're defining an intermediate input for the action and then mutating that?

bold lodge
#

Correct!

#

It's just such a cool approach, but it's a bit different from what game engines usually implement.
Except Unreal Engine, who about 2 years ago adopted it. And I stole the idea πŸ˜…

karmic monolith
#

Reading the example, I really like having a blessed way to say "this set of actions should be active"

#

That's the next problem to tackle though πŸ˜…

#

I'm going to strongly prefer using states or per-player enums or bubbling or something though, rather than introducing a new tool

#

But it's a real need

#

I really want to kill LWIM egui flag

#

Ah, and it looks like you went with a "finite arity chord" design?

bold lodge
bold lodge
bold lodge
karmic monolith
#

(just skimming still)

karmic monolith
bold lodge
#

Chord is a modifier, you can attach arbitrary number of modifiers

karmic monolith
bold lodge
#

Sorry, not modifier, but condition

bold lodge
karmic monolith
#

WRT observers, I agree that the if blocks suck

karmic monolith
#

For observers, we could emit triggered events whenever actions are pressed / released, but that doesn't really help, because each enum variant isn't a distinct type πŸ€”

#

It looks like you're doing one type per action?

#

Which is nice for observers

bold lodge
bold lodge
karmic monolith
#

Yeah πŸ™‚ These didn't exist when I made LWIM (understatement), so I need to think about the design again πŸ€”

#

Are you just comparing TypeId?

#

And how do you serialize them?

#

(the discussion about "how do we represent input" is basically fully orthogonal to this IMO)

bold lodge
bold lodge
bold lodge
karmic monolith
#

You can't just save a list of "KeyA, MouseButtonLeft"

bold lodge
#

Keybindings rarely correspond to actions.

#

For example, you don't bind the same key for enter and exit car

#

It's the same key

karmic monolith
#

Right, you're having a degree of separation and then the game devs will write their own plumbing layer?

#

Rather than making input maps directly serializable or displayed in a menu

bold lodge
#

Yep!

karmic monolith
#

I agree that that's how it's traditionally done, but I'm optimistic that offering more automation can be better for devs and more fine-grained control can be better for users πŸ™‚

bold lodge
#

In theory, they can serialize actions using reflection. I.e. they serialize struct names and inside context creation user can spawn actions.

karmic monolith
#

Is the backend computation of everything done using observers too?

bold lodge
#

But I currently don't.

#

Happy to reconsider.

karmic monolith
#

Clashes are so annoying, because it forces you to take a somewhat global view of things

bold lodge
bold lodge
karmic monolith
#

"in their order" = "order added?"

bold lodge
#

It may sound simple, but switching between context / layering wasn't easy to implement πŸ˜…

bold lodge
karmic monolith
#

Do you think that observers should be the single blessed way for users to react to inputs being pressed?

#

(or whatever)

bold lodge
karmic monolith
#

They feel like they're probably the right tool these days: low-performance needs, extremely flexible, low boilerplate

#

The current inability to order them does suck though

bold lodge
#

I considered the idea of providing both ways, but decided to go full observers for simplicity. I just couldn't find a real use case for regular events for inputs.

karmic monolith
#

(that's fixable upstream though IMO)

karmic monolith
#

I think that state-checking could be useful for fancy fighting-game-level input shenanigans, and is generally reasonable to provide

#

But teaching observers as the Blessed Path feels correct

bold lodge
#

Another cool thing I discovered is the removal of "Pressed", "Just presed", etc.

karmic monolith
#

Yeah?

bold lodge
#

Instead we have conditions.

karmic monolith
#

The "hold" etc variants are super useful, but felt very wasteful under the existing design

bold lodge
#

Users decide when the action is active based on them. For example, "Press for 3 seconds". Or "Tap twice".

bold lodge
karmic monolith
#

Yeah. Not impossible to do in LWIM's design either, but a bit messy to implement

bold lodge
#

Imagine having a strong attack that activated by holding left mouse button for 2 seconds. And regular attack if pressed for less then 2 seconds.
In my crate it's done like this:

  1. You just slap hold condition.
  2. Observe for "Completed" for strong attack.
  3. Observer for "Cancelled" for regular one.
karmic monolith
#

Which is similar to how we're doing things over with picking events

#

(man observers are so slick)

bold lodge
#

I got very inspired by them πŸ™‚

karmic monolith
#

So, something else that @crimson stream and I have been talking about is bubbling input events up from the pointed at / focused UI element, and only reading them in your gameplay input logic if they reach the top of the hierarchy intact

restive knoll
#

I still need to take a crack at that tick problem actually ferris_sob

karmic monolith
bold lodge
#

So, this is why I wanted to discuss it. Maybe you could use my crate for inspiration.

Or if you really like the design, maybe it will be easier to make changes to my crate. I'm open giving you full access to the repo or even transfer it somewhere.
Probably far-fetched, but I wanted to let you know that I'm fully open to collaboration.

karmic monolith
#

I think that there are easy changes to LWIM to make it more useful without completely breaking users

#

But we do need to decide what we're doing for upstreaming, and the best way to get there

bold lodge
#

Makes sense!

karmic monolith
#

Let me make up a design doc for what my "dream crate" would look like, then we can think about the easiest way to get to there

restive knoll
karmic monolith
#

If it's too big of a divergence from the current LWIM, I think the play is to not break our poor users too much, and experiment elsewhere (or in bevy_enhanced_input)

restive knoll
#

I am honestly a bit confused about why a lot of other engines seem to have such "simple" input abstractions most of the time ... Usually it's basically what LWIM does if you have exactly 1 enum, and at least half of the time no disabling either

tardy verge
#

@viscid swallow i think you would want in on this

karmic monolith
restive knoll
bold lodge
restive knoll
#

Might be worth experimenting more for sure .... I'm also not convinced the "networking" uses of LWIM make any sense at all, in any way ... They seem to be used quite a lot tho πŸ˜‚

karmic monolith
#

I think that networking at the action level makes enough sense!

#

Man, that would be so much easier if at was all event streams though

bold lodge
#

Yeah, I looking forward to see your input queue crate. Wondering if we can provide a nice way of hooking one to another without compromising the bandwidth. I remember that you mentioned that you can't directly hook input managers

restive knoll
#

Like ... I have seen at least 4 people ask if they should send input mappings to the server and that's a scary question πŸ˜‚

#

The main thing you'd really be missing from LWIM's inputs is turning it into a really compact and efficient set of data ... And removing any action states that might be possible to input but just didn't fire (so you don't get mispredictions when the server decides that actually they did happen after all)

#

There's also just a lot of attack surface in LWIM's representation, it's easy to send a Vec2 (or even Dir2) that isn't normalized and get "speed hacks" that way, it's impossible to do that when you quantize the input to a u8 angle and a u8 magnitude πŸ˜‚

bold lodge
#

I wonder if we can consider inputs and actions as separate things from input queue. Sounds insane, but I feel like what input queue does from your description could be used to send all sorts of data. Like when you pick an entity, why send click coordinates, why not just send the clicked entity?

#

You also mentioned that you don't want to send just all inputs. Like why send "Jump" if user pressed it in the air?

#

So I think it might worth having separate networking "commands".

#

That are not the same as inputs/actions

#

And provide a nice way of triggering them.

restive knoll
bold lodge
#

This means that we just to provide a nice way of triggering actions when a command is received.
I designed my crate to allow manually triggering actions. But we need to figure out a nice API. When you release your crate, we can back to it πŸ™‚

crimson stream
#

I'm not sure what is the right terminology here, but I'm talking about "polled" events, like "rotate camera left" which happen continuously as long as the key is held down.

#

For polled events like these, the problem with using observers to set a pressed / unpressed state is that the routing can change while the key is held down.

#

This means that you can easily get into a "stuck key" state, because the entity that received the key down event is not the same as the one that received the key up event.

#

Consider for example the case where you have a camera controller that uses WASD, but while you are holding A down you then click on a text input field. The input field captures A keystrokes, so when you release A, the camera controller keeps spinning because it didn't get the up event.

#

Or the case in Kerbal Space Program, where the WASD keymappings change based on which control panel the mouse is hovering over.

#

I think a more robust approach is to have the input mapper poll for hardware key states, or to listen for raw events regardless of focus and bubbling, but to have an "exclusion plugin" which lets you configure which keys to ignore. So for example, when a text input field is focused, the exclusion plugin says to ignore all key down states that are coming from the keyboard, but not from the mouse or gamepad.

#

So in the previous example, your camera control uses WASD, but if you hold down A to rotate the camera, the instant the text input becomes focused the camera stops rotating.

#

This means extra complexity, because now we have to have two different systems checking focus: the normal bubbling mechanism, and the exclusion function for the input mapper.

#

But the advantage is a much more reliable and bulletproof routing.

restive knoll
#

Having it be bulletproof is pretty important tho ... There's also plenty of "bad states" you'd ideally be handling to avoid the kind of dumb shit you run into on many games (I'm sure we've all seen fun stuff like inputs being held permanently when unfocusing, or having stick drift be picked up then you disconnect the controller cause you weren't using it and it doesn't disappear)

#

Picking up inputs that should be there, or not registering inputs the player clearly pressed intentionally are both extremely frustrating things that shouldn't need to happen

crimson stream
#

The actual exclusion function itself is pretty simple, e.g. "if the current focus element has a Button component, then ignore any pressed states for ENTER and SPACE". The problem is that this logic is both widget-specific and game-specific.

#

That is, different widgets respond to different keys, and when those widgets have focus we want them to take precedence.

maiden trail
#

I'm curious to see how leafwing-input-manager will handle serializing processors. If a user rebinds a key, does it retain that processor? Serializing processors in the first place seems like a bad idea because you might need to change/remove/add processors to a specific input in the future. If you don't serialize it, how do you know which input to apply it to after that input has been rebound.

#

The only idea I've come up with is to have "input slots" and the processor modifies those slots instead of specific inputs. That way you can change the input in the slot without affecting the processors but this would change the simple API a bit.

bold lodge
#

In my crate modifiers can be applied at 2 levels: input level and action level.
Rebinding works by updating the context and since modifiers are defined inside it, no additional care from user side needed.

Writing not to brag, but mostly for Alice since we discussing possible design πŸ™‚

karmic monolith
#

Yeah, I think that you need to apply them at both

maiden trail
#

Do you have an example of what the input level modifier API looks like?

karmic monolith
maiden trail
#

Right, I mean with the API of bevy_enhanced_input

#

Because I only see action level looking at the examples

#

I haven't looked at the rebinding API but this looks like the same API as leafwing and would therefore have the same issue I'm reffering to

maiden trail
#

Well, let's say that this Negate modifier was on an input that the user can rebind. How does that modifier persist after the rebind? Is it possible to trivially remove/update/add to that input's modifier?

bold lodge
maiden trail
#

I was looking at this line: GamepadStick::Left.with_modifiers_each(Negate::all())

bold lodge
bold lodge
# maiden trail Well, let's say that this Negate modifier was on an input that the user can rebi...

When you define implement a context for a component, you define actions with their modifiers.
When you change your bindings, you simply trigger https://docs.rs/bevy_enhanced_input/0.7.1/bevy_enhanced_input/input_context/struct.RebuildInputContexts.html and contexts will be updated with your inputs

maiden trail
#

(psuedo code because I don't know the exact API)

ctx.bind::<Rotate>()
            .to((KeyCode::Space.modify(Negate)));

when I rebind space, how does it know that this Negate modifier was on this "input slot"?

is it based on the order the inputs are bound?

bold lodge
maiden trail
#

ok, so the slots are user defined instead of defined by the library?

bold lodge
#

No slots needed. The context creates and rebuilds from your source of inputs.
Modifiers are not defined in settings, they are defined in the context.

maiden trail
#

I understand, but the source of inputs I imagine needs slots, it can't just be an unordered list of inputs otherwise you won't know which modifier to apply to which input.

bold lodge
#

You assign modifier directly to input or action

maiden trail
#

I imagine the user would do something like:

ctx.bind::<Rotate>()
            .to((settings.rotate.pc_input.modify(Negate)));
bold lodge
#

Ah, I think I know why you are confused. My example with action-level modifiers actually showcases input-level modifiers, I'm sorry.

#

So the API is this for action level:

ctx.bind::<Rotate>().to(settings.rotate.pc_input).with_modifier(Negate);

For input-level:

ctx.bind::<Rotate>().to(settings.rotate.pc_input.with_modifier(Negate));
maiden trail
#

I don't think that's what is confusing me, I understood that

bold lodge
#

Ah, I think I get what you asking.
Action-level modifiers applied to all inputs in the action. They are "global" for the action inputs.

maiden trail
#

I still think we are talking past each other πŸ˜…

#

I think I get how your library handles this though, I would just have to see an example with rebinding to be sure

#

If I had to compare this with leafwing, your library has three layers: inputs (settings), context (adding on modifiers), and then the action api
leafwing combines the first two

bold lodge
maiden trail
#

I like the API with the two combined but I don't see how you can really solve the issue I'm describing with that approach

bold lodge
#

Could you try to elaborate on the issue once again?

umbral linden
karmic monolith
#

@bold lodge how does using observers work for fixed timestep? Do you just hold onto the action events until the schedule is ready?

bold lodge
karmic monolith
#

I think that will trigger them too soon tbh

#

I'll add a section to the doc for this

maiden trail
#

I'll try explaining it again.

When I use the term "input slot," I'm referring to something like this elden ring example. Where for each action, there is a keyboard slot and a mouse slot. Some games provide combined keyboard/mouse slots, and sometimes 2 of those.

Say you have a case where you need to modify a specific slot. If we want to modify the keyboard slot, how does the API allow us to attach a modifier to the slot so that regardless of what it's bound to, it keeps that modifier. The API should also allow for trivially changing/removing the modifier and adding new ones.

With leafwing, modifiers are added something like this:

// binds to the conceptual keyboard input slot
InputMap::default().insert(Action, KeyCode::Space.modify(Modifier);

Once we rebind space, how does leafwing keep the modifier? Does it serialize it along with the new key? Does it remember the order things were bound and apply the modifiers given that order?

bold lodge
bold lodge
# maiden trail I'll try explaining it again. When I use the term "input slot," I'm referring t...

Ah, thanks a lot for the detailed explanations. Now I get it!
Here is how I would implement it:

/// You have a struct with your settings like this
#[derive(Default)]
struct KeyboardSettings {
    jump_key: KeyCode,
    jump_button: MouseButton,
}


// Inside your context you will be able to do this:
ctx.bind::<Move>()
    .to((settings.jump_key, settings.jump_button.with_modifier(Negate::all()));

But it's quite specific to the game.
In my game, for example, I have 3 slots for just the keyboard, but gamepad/mouse are not rebindable.

maiden trail
#

Yep, that's how I imagined your library solves it.

#

But in leafwing it seems like the idea is to serialize the InputMap directly

karmic monolith
#

LWIM's serialization is... questionable currently

#

Which is one of the things that started me down this rabbithole

bold lodge
#

I.e. keyboard bindings usually separate from mouse bindings.

maiden trail
#

Yep, that's why I'm pointing out the issue. I have a input library based on leafwing for another engine and I've been thinking about how to solve this.

#

I think the solution of having the settings feed into the InputMap would definitely work, but now the API doesn't do as much automatically for you. With my library, the user doesn't have to define the input settings, the library just handles it for you.

#

Maybe it's worth changing though because I haven't thought about how to handle migrating the serialized InputMap if you add/remove actions or even add/remove input slots.

bold lodge
maiden trail
#

So the engine this is for is Roblox and to be honest serialization can be complex for users (especially for inputs because they don't have serialization built in)

bold lodge
#

Like it's possible to serialize modifiers and conditions, but the output format won't be very nice.

maiden trail
#

Yeah, I don't want to serialize them because they would be hard to change. Which is why I've been toying around with adding the input slot concept to the library.

bold lodge
#

I think it might be implemented just as an abstraction on top. Since you handle settings creation anyway.

maiden trail
#

Yeah, just need to figure out how to do that in a nice way.

bold lodge
karmic monolith
bold lodge
karmic monolith
#

Yep, that's what I was thinking

#

And defined each action kind (or context in your case) based on whether or not they want to use FixedUpdate

bold lodge
karmic monolith
#

Maybe. FixedUpdate is at least currently pitched as an "optional" feature

bold lodge
#

Ah, It might introduce input delay, which is undesirable.
Maybe you are right, we need to provide a way to configure it.

peak aspen
#

Imo input should be available as soon as possible, and since it's detection is framerate related if should be available in Update.
Providing an accumulated version in FixedUpdate is very nice to have, for users using phycics for example but limiting it to FixedUpdate will have negative impact on other use cases

bold lodge
#

Yep, this is why I agree with Alice that it should be configurable!

restive knoll
#

Even for games that do run almost everything in FixedUpdate (like my game, which needs to do so for networking reasons), there are still a couple of inputs that are entirely handled in Update, like camera controls which I just need to be smooth and don't have any potential for cheating (since I have no such thing as a "limited rate of turn", unlike how it is with movement or using skills)

hard bluff
#

Hi, I am trying to make topdown-like movement with WASD key mappings.

InputMap::default().with_dual_axis(PlayerAction::Move, VirtualDPad::wasd())

This code throws an error Cannot map a DualAxislike input for action Move of kind Button. How can I fix it? I use 1.16.0 version of leafwing-input-manager crate

karmic monolith
bold lodge
#

@karmic monolith thinking about dynamic inputs...
In my crate I went UE route and defined all inputs statically (you can defined custom input sources in UE with their Enhanced Input plugin): https://docs.rs/bevy_enhanced_input/latest/bevy_enhanced_input/input/enum.Input.html
But I have an idea how it can be implemented without dynamic dispatch (to keep the serialization convenience):

  1. Add Input::Custom(u8) variant where u8 is custom input ID.
  2. Create a resouce called something like CustomInput that is a hashmap with u8 as key and https://docs.rs/bevy_enhanced_input/latest/bevy_enhanced_input/action_value/enum.ActionValue.html as value.
  3. User feeds values for its custom input source to this resource and we read it.
    What do you think? Similar approach could be applied to the possible "dream input manager".

Bevy uses similar approach for https://docs.rs/bevy/latest/bevy/prelude/enum.GamepadAxis.html

pliant hornet
#

Hi, i'm trying to use the analog triggers of my gamepad (LeftTrigger2 and RightTrigger2) as brake and throttle controls for a tool i'm making, but i'm only getting 0 or 1 back, but when using Query<&Gamepad> manually i get analog values as expected (for debugging i'm looping over ActionState<DroneAction>::all_action_data)

wicked zephyr
#

Are your brake and throttle actions annotated so that they are axis-like?

#

iirc the default is button-like

pliant hornet
#

they are now, but i'm using InputMap::with(DroneAction::Throttle, GamepadButton::RightTrigger2) to add them which apparently causes the value to stay at 0

karmic monolith
pliant hornet
#

Issue 648 and PR 649 seems to be related, i tried using action.button_value(&DroneAction::Throttle) to get the value but it just returns a binary 0 and 1 (or just 0 if i annotate the enum variant with #[actionlike(Axis)])

pastel cape
#

Hello, I recently upgraded to 0.13 and am working through the KeyCode breaking changes https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/RELEASES.md#breaking-changes-0130. I'm on a dvorak layout, so the new physical key KeyCode behavior is welcome. However, some app hotkeys, like Ctrl+O to open and Ctrl+Q to quit seem like they should remain where users are use to O and Q, regardless of layout. I've done a bit of digging but I can't crack it. Am I missing a straightforward way to use logical keys instead of physical ones?

pastel cape
foggy osprey
#

Does clash handling not work for axislike chords?

#

I bound zoom to scroll wheel and vertical movement to ctrl + scroll wheel, and both actions pick up when I ctrl + scroll wheel

empty delta
#

Hi, this might be a dumb question, but for the life of me I can't figure it out. What does the egui feature do? πŸ₯²

bold lodge
#

If it doesn't work - you have a version mismatch.

empty delta
#

nvm leafwing 0.16 is on bevy_egui 0.31 πŸ™ƒ , thanks for the hint

minor flicker
#

hi, why am i getting this error? :c

minor flicker
#

question: how do i perform control remapping, in a way i can place any input for it to work and clear the binding list for a specific action?

minor flicker
#

how to build an axis from keyboard keys?

minor flicker
#

found it being VirtualAxis and VirtualDPad. i should have read the examples more ^^

#

question: how do i treat a single keyboard key like a single axis?

minor flicker
#

what am i doing wrong in this bit of code setting up defaults? the original insert says it returns regular ownership of Self, but the compiler complains that it's receiving a mutable reference to Self

restive knoll
minor flicker
#

why though, when the source code just says it returns Self?

minor flicker
#

those mappings are for my own game, and i thought it would be good to keep it explicit.

#

how do i pick both controller triggers into a single axis?

minor flicker
#

it seemed to have worked replacing insert_ with with_

restive knoll
#

Huh, I didn't even know we had a set of with_ functions ... That's a lot nicer than needing to store it in a variable to avoid the insert_s from breaking the chain's return type

slender basin
#

@pliant hornet did you ever figure out the analog triggers issue?

pliant hornet
#

sadly not, I ended up just rolling my own input system for gamepad inputs

slender basin
#

@minor flicker were you able to bind a gamepad trigger to an axis and successfully get its clamped value?

torpid hornet
#

Hello,

can I check if one of the buttons of a DualAxis axislike is pressed? .just_pressed() panics when you give it an axislike

#

I want my character to only move, when one of the four buttons has pressed and has to release and press it again for the next movement

torpid hornet
#

hmm okay I added a local bool that is toggled, but that really sucks. Can I slow down the input?

#

nvm, I solved my problem with a timer

toxic lichen
karmic monolith
toxic lichen
karmic monolith
trim wigeon
#

should InputMap<T> require ActionState<T>?

umbral linden
#

Yes I think so

#

I have an observer that adds ActionState automatically whenever InputMap is added

karmic monolith
trim wigeon
#

i can't think of a scenario where you'd want an InputMap without an ActionState, but there could very well be one

karmic monolith
#

The converse is definitely true (bots)

trim wigeon
#

for example, storing settings as entities

karmic monolith
#

I think in that case you can just make a little wrapper

trim wigeon
#

i'll write an issue now πŸ˜„

#

the docs.rs page seems to be broken for lwim? πŸ€”

trim wigeon
#

@karmic monolith the main branch of lwim is 0.16. should version "0.17" ever be mentioned in code (i.e., a deprecation notice) if the crate version in Cargo.toml isn't yet at 0.17?

trim wigeon
#

kinda hard testing lwim. when i do cargo --build tests, my memory usage shoots up to 97% and crashes other programs πŸ˜…

  = note: collect2: fatal error: ld terminated with signal 9 [Killed]
          compilation terminated.
#

kinda a me-issue and it's not crucial to the 2 lines of code this change requires but i thought that was weird

karmic monolith
#

I'm annoyed at it too, and I'm hopeful that Rust 2024's doc test changes will make it not do that

trim wigeon
trim wigeon
#

@karmic monolith in my local fork, i've made the change at hand. i'm now updating the examples and will be making sure the integration test doesn't have any unexpected failures πŸ™‚. (i don't expect any, but no one ever does!)
i've moved the bundle into leafwing_input_manager::bundle::InputManagerBundle, which is defined inside lib.rs, and included in the prelude. is this ok? or should it be removed from the prelude due to it being deprecated? (for reference, SpriteBundle is still in bevy's prelude).

i could also add #[docs(hidden)] since i think having a whole docs page dedicated to a single-member module (of a deprecated feature, no less) would be strange (which is also the case for the bundle.rs associated with sprites in bevy).

#

i think it looks nice πŸ˜„

#[derive(Component)]
#[require(InputMap<Action>(default_player_input_map))]
struct Player;

// Describes how to convert from player inputs into those actions
fn default_player_input_map() -> InputMap<Action> {
    InputMap::default()
        // Let's bind the left stick for the move action
        .with_dual_axis(Action::Move, GamepadStick::LEFT)
        // And then bind the right gamepad trigger to the throttle action
        .with(Action::Throttle, GamepadButton::RightTrigger2)
        // And we'll use the right stick's x-axis as a rudder control
        .with_axis(
            // Add an AxisDeadzone to process horizontal values of the right stick.
            // This will trigger if the axis is moved 10% or more in either direction.
            Action::Rudder,
            GamepadControlAxis::RIGHT_X.with_deadzone_symmetric(0.1),
        )
}

fn spawn_player(mut commands: Commands) {
    commands.spawn(Player);
}
trim wigeon
#

@karmic monolith the work is done! πŸ˜„

though i'll send the PR now, i think the the examples should be looked at at some point before 0.17.

trim wigeon
#

πŸ˜… i didn't catch this since there was no attached issue.

jade helm
#

Hello! I'm using leafwing input manager for a game, but I am struggling a little with chords. I'm trying to implement click and drag. So I have a DualAxislikeChord::new(MouseButton::Left, MouseMove::default()). i.e. Left mouse button starts the movement, mousemove is the axis to inspect. However I'd like to show the user an indicator that they're in this "mode" (.e.g change the cursor from normal to dragging). I can't use just_pressed as it has a debug_assert that fails.

So what options do I have? Do I need to add another action that tracks just MouseButton::Left? Will that clash?

jade helm
jade helm
# midnight bramble why not picking?

I don’t know. I’ve looked at picking but it seemed more abstract and less about user input. Do you have some relevant literature you could point me to please?

jade helm
weary fractal
#

I have some custom first-person shooter camera logic where I use the mouse delta to calculate pitch and yaw. I sum up all mouse delta updates that happened inside of a frame and use that. I tried to move to lwim and use the dual-axis mouse movement functionality but moving the camera just feels… wrong? Very inconsistent. What could be the reason for it?
I compared the summed up mouse updates to the axis_pair and they seem to be completely different values?

#

Ok, I figured it out, Leafwing uses AccumulatedMouseMotion which seems to be quite broken when used inside FixedUpdate? When switching to Update the mouse feels pretty normal (although still a tiiiiny bit off, might just be placebo)

karmic monolith
weary fractal
# karmic monolith Can you open a Bevy bug for this?

I could but it's technically not a bug. The docs for AccumulatedMouseMovement say that the value is reset to zero every frame. Since FixedUpdate doesn't happen every frame, the mouse data is lost for all the frames it skips. This is technically intended behaviour. We could mention this behaviour in the docs more explicitly or add a FixedUpdate version of the AccumulatedMouseMovement.

#

Now that I think about it, it's probably bad practice of me to handle mouse movement in FixedUpdate anyway as that just adds unnecessary input lag.

toxic lichen
#

are triggers treated as unpressed unless they're above a threshold (like 0.6?). It seems that way, I just can't determine why that's happening

karmic monolith
trim wigeon
#

or should i create like a "patch" .rs file where i wrap this stuff in my own types until this gets merged?

#
// src/patches/lwim.rs

#[derive(...)]
#[require(
  leafwing_input_manager::prelude::InputMap<A>
  leafwing_input_manager::prelude::ActionState<A>
)]
#[component(on_add = on_add_input_map)]
pub struct WithInputMap<A: ActionLike>(pub InputMap<A>);

pub fn on_add_input_map(world: DeferredWorld, entity: Entity, component: ComponentId) {
  // remove component? idk the code here xd
}
restive knoll
trim wigeon
#

ohh, right! thank you! i think this should work.

trim wigeon
#

so i could just have my own InputManagerPlugin that does this and then adds the real input manager plugin

restive knoll
#

Yea that would be a decent workaround, and then when we get a release with the require you can swap back to the normal plugin again

trim wigeon
#

i think i'm doing smth wrong?

use std::marker::PhantomData;

use bevy::prelude::*;
use leafwing_input_manager::prelude::*;

pub struct PatchedInputManagerPlugin<A: Actionlike>(PhantomData<A>);

impl<A: Actionlike> Plugin for PatchedInputManagerPlugin<A> {
    fn build(&self, app: &mut App) {
        app.add_plugins(InputManagerPlugin::<A>::default()); // error: the trait `bevy::bevy_app::plugin::sealed::Plugins<_>` is not implemented for `leafwing_input_manager::plugin::InputManagerPlugin<A>`
    }
}
trim wigeon