#leafwing-input-manager
1 messages Β· Page 5 of 1
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
I think if someone need some esoteric input, we should just allow them to update actions state.
So users can create their own enums.
Why? I suggesting to eliminate the dynamic dispatch completely.
π€ I think your issue #620 could be fixed this week
Unfortunately, the default serde tokens generated by InputMap are not aesthetically pleasing
Yeah... Having them very nice is not very important, but nice to have.
The broken deserializations of separate traits have been fixed
I will push the changes after checking that the other parts are error-free
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...
π€ 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
What can be addressed?
I saying that traits are PITA for ser/de and can break just like in this release without anyone noticing.
The complex creation and insertion of InputMap
Ah, got it.
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
π 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
Alternatively you could try to use reflection
We may be able to guard against this with is_plugin_added
not bevy 0.14 here? https://github.com/Leafwing-Studios/leafwing-input-manager/blob/a123723c560502847b4574b6213dcd3ce86e7ae4/Cargo.toml#L48
That should be changed
It doesn't need to be, but it's messy
It means that it will work starting from RC and up to including the latest stable patch release.
So it doesn't affect users in any way
π€ Works in manual code but fails in CI
opened an issue on their repo
added a custom implementation and works
@karmic monolith What do you think about this?
Generally I don't expect users to be implementing this often enough to be worth it
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
Yeah, I generally find macros relatively hard to maintain π
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
You didn't enable the features needed
π an unstable feature?
No, there's a keyboard/mouse/gamepad feature
Ahh, you're right
I think that 0.16: this isn't vital
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?
Does the plugin provides a way to listen for input events in observers?
Not yet, but it would be feasible to write an external layer for this
GamepadControlAxis::LEFT_Y.with_processor(AxisBounds::at_least(0.4))
π€ Perhaps we can add that method back
it could eventually be useful to players, so keeping it simple and uniform should be better
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.
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?
agreed to add a threshold to directional input
I can open an issue is that ok for you ?
btw I could also have a look at the code and if it is not too complex, send a PR.
π€ You can do that but I've already opened a PR for it
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.
@karmic monolith will you help assist me on this issue?
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
There are processors to invert a specific axis or adjust the sensitivity of an input. The scale of controller input and mouse input are very different, since it's 1.0 per pixel moved on a mouse, while a controller has 1.0 while the stick is fully to one side
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(),
)
yep, that did the trick, thank you
I wonder if we could add this as an axis processor TBH
I don't think processors currently have access to time delta, so I guess it would be hard ... Another option could be for it to be a input type level thing, like "give me framerate independant data"
aren't processors supposed to be only the things that players should be able to modify?
Processors and bindings, yeah
We also need to unify processors with GamepadSettings at some point, but that's a post-upstreaming problem
then is there any place to put delta/clamping/normalization for the action itself?
No, just your system logic. Or you can write a system that mutates the action data
I think the latter pattern is better
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
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
people actually used to do that, you know...
Mouse motion, or relative cursor position? Because those are very different types of input
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
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
ehh, it depends
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)
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
That's my whole point. Mouse motion and WASD or a gamepad axis are entirely different units of data. You can't swap them out and expect it to work, of course you can tweak sensitivity so it works with your FPS, but that's a very fragile and poorly designed solution
i don't see a reason why you shouldn't be able to swap anything out though
i've seen the keyboard camera tank controls, and as i've said i've seen mouse being used for precise movement
i've also seen people one guy play FPS on a driving wheel
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
oh, i finally get what you mean
yeah, just lowering sensitivity wouldn't make it framerate-independent...
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 
if you multiply mouse motion for camera by time delta then you get the opposite effect, making it framerate-dependent...
Is anyone able to adopt https://github.com/Leafwing-Studios/leafwing-input-manager/pull/615 ?
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
if triggerlike for keycode returns just 1.0/0.0, doesn't that mean analog keyboards will only work as normal ones?
Winit doesn't expose this info to us
But if they do I'll add it
π€ And do we need Triggerlike versions for VirtualAxis and its friends?
Ideally, but it's non-blcoking
I can try it next weekend. My postgraduate classes start this week
β€οΈ That would be hugely appreciated, but prioritize school over open source if needed
But if every Buttonlike is also going to be a Triggerlike anyway, why don't we just make trigger_value() a method of Buttonlike?
π€
Okay yeah fine
That's soo much simpler
ohh, Triggerlike is only implemented for GamepadButtons, KeyCodes and MouseButtons, not all Buttonlikes
Yeah, but we could easily have a reasonable value for any of them
It's just that GamepadButton needs special logic
Would it be beneficial to allow directional inputs to get value ranging from 0 to 1 or higher?
I'm not sure I follow?
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
Hmm. I suppose you might as well
π€ 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
Truly π
Unfortunately user input is sooo much more complex to get good abstractions for than I ever expected
i kinda did mention that we should make same thing for input as somebody did with ui challenges...
I mightβve missed something. Could you give me a bit more context?
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
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
@candid vigil I see that you fixed ser/de, but methods on InputMap still accept impl Trait.
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 π
We could also impl the traits for boxed objects
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
I'm not sure about that, but now this one exists https://docs.rs/leafwing-input-manager/latest/leafwing_input_manager/user_input/enum.UserInputWrapper.html
A wrapper type to get around the lack of trait upcasting coercion.
But why not Into<Box<dyn Trait>>? You box them internally anyway. So it's just insert, insert_axis, etc.
Yes, looks like it's used for get, but insertion methods are separate and accept impl Trait.
Probably better to keep a single insert with this enum
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
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?
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
gotcha, makes sense
/// 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
Yep, as long as you have access to the system set label or system name π
You can add but not remove run conditions
oh cool! I didn't know that. What's the API for that? Do I just re-add a system or?..
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
Oh hmm, I could have sworn that would work
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
will give it a try :) thank you!
@storm anchor yes please to a 0.15 update PR!
@karmic monolith I've been looking into the Triggerlike PR
but I'm still thinking that maybe we could merge Triggerlike into Buttonlike
Yeah. I think that's the way
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?
You can write your own custom SystemParam with the derive macro π
Although I think we probably want to make SourceData require a SystemParam, not a Resource
Maybe I could create a small PR to do this first
Yeah, that's way more pleasant
βΉοΈ invalid system configuration

π€ 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
π
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
@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.
Ugh. That's gnarly. We should track that in bevy_input as an Option<u8> or something, as a component on the gamepad entity
But I'm not sure how / if we can get that order in a stable fashion on PC
We can use those then
Finally got it building after ~6 hours of work
I hugely appreciate it
@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
maybe I can try to do it tomorrow, just frustrated at the number of regressions that gamepads-as-entities landed with π¦
This sounds really similar to pointers-as-entities in mod_picking. The entity they receive is arbitrary, but they have a stable ID in the PointerID component that comes from winit. The mouse is just... Mouse, but the touch pointers are numbered from the touchID that winit provides.
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.
I'll tackle stable gamepad ids π
At long last, three-axis inputs!! Time to go refactor every space game prototype I ever wrote
And merging the backlog of breaking changes
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
I'm just using the gilrs uuid
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
@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?
Hmm, that shouldn't be happening. @umbral linden's patch should be robust to both fixed and non fixed schedules
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
Okay. Can you open a draft PR so we can take a look?
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
No we have a fancy state swapping mechanism
is that documented anywhere?
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
Open a draft PR for the upgrade and I'll take a look
bevy seems to be ignoring the ordering leafwing wants for InputManagerSystem::Tick and InputManagerSystem::Update, did 0.15 change anything about system ordering?
It shouldn't have. Maybe @full iris's changes to system param validation broke something?
Is the order between those two systems wrong?
it seems to be, based on my logging
even adding more before and after constraints isn't fixing it
this is extremely confusing
Okay, that's really quite bad π€
I can't imagine what else might have touched that
https://github.com/bevyengine/bevy/pull/15519 isn't merged
ok, commenting out all the fixed main loop systems seemed to fix it
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
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
Guarantee you it's retained rendering world
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
I bet it's http://github.com/bevyengine/bevy/pull/14881
Aha, very likely. @abstract meteor
Yes lol
not this time π
@karmic monolith here you go https://github.com/bevyengine/bevy/pull/15685
Fantastic, I really appreciate it
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?
Uh that's a good question
@storm anchor @karmic monolith did I heck up? π
I'm redoing the leafwing bevyup from scratch because I messed up the API
Ah, I ran into that this morning. Do you have a branch?
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
Yes! My response was "wow @candid vigil I didn't even think this was possible"
Pcwalton is working on upgrading LWIM to main
I'm not sure what the exact struggles he's running into with serialization right now
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
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? π€
I think I might have fixed that issue in my branch
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.
ohh i see
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
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
Yes
Yeah, there's a system set in there for manually setting the action state
Use that
Yeah this is a high thrash update
it's almost working I guess, but the unit tests are still busted because gamepads-as-entities broke mocking somehow
We need a :bevy_on_fire:
Gamepad mocking has been really broken for ages. Just ignore them for now and test manually?
noooooooooo it's a schroedinger bug
When I add diagnostics it works π
Guess it's time to move this out of this channel
try restarting several times
I have
and it works every time?
It's always consistent when the diagnostic systems are in there
Yeah πͺ¦
π€ π€ π€
arggghhh, well, I had leafwing updated, but the reviewer in https://github.com/bevyengine/bevy/pull/15685 told me to change the API so now it's not updated anymore
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
I ended up ordering wrt ManualControl and it seems to work, but would I expect to see action states getting clobbered by Update even if no inputs are pressed? π€
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>
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
the irony of a reviewer making me take out Gamepad::new just as code lands in leafwing that makes use of it
@karmic monolith here you go https://github.com/Leafwing-Studios/leafwing-input-manager/pull/644
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
it's been around a while. I just had to start using it with gamepads-as-entities since gamepads aren't in a resource anymore
yeah I found these two in the current doc
I integrated that PR into my branch and it builds and appears to work. Thanks @storm anchor
leafwing tests seems to have broken again with the latest bevy changes and I have no more motivation to fix it, sorry
That's fine, I'll adopt your PR π
I have some changes that remove AccumulatedFoo in favor of the upstream versions, do you want me to push those even though the unit tests are now broken?
Yes please
it seems to be just the tests that are failing -- leafwing itself works fine
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...
@karmic monolith pretty sure it's a system ordering issue
it's intermittent, sometimes tests succeed, sometimes they fail
Ooh I can add ambiguity denial to our CI now that Bevy itself is clean π
ok, updated. since it's not blocking me I'll let you figure it out π
I think it just means that sometimes stuff is 1 frame behind
which should be fixed, but isn't blocking me right now
Once the release candidate is out I'll adopt and finish this PR π
awesome, thanks!
Nice to have it up though for others living on main
yeah, I also don't want you to have to redo my work
it was a lot
had to redo it 3 times π
I strongly appreciate that you did this. I was dismayed looking at the scope of work yesterday morning. π
serialisation for trait objects is easy, the problem is when deserialisation, it is hard to determine the concrete type of the boxed trait object
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
ButtonValue?
βΉοΈ Deriving SystemParam requires lifetime parameters, but the trait and the implementor don't have such parameters, causing "unconstrained lifetime parameter"
Perhaps need a small PR to add GAT to SourceData
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?
This one please
So, when the button is from released to pressed and from pressed to released
should we trigger a ButtonValueChanged with the value?
I think so, but @umbral linden will have more informed opinions
Could you check the logic of computing the updated values of GamepadButtons like the image?
I'm not sure that there're cases like ButtonInput<Button> is pressed but Axis<Button> is 0.0
Can you use 'static in these?
I'm not sure I understand what you mean
For example, say we're checking the state of GamepadButtonType::RightTrigger
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?
It seems to work
I believe that those cases are possible with thresholds, especially the latter
π€ don't gilrs and bevy filter out those values after physical deadzones?
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
Should have added using .with()
insert?
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
Could you elaborate? Not sure if I get you.
So basically
keybind press A => action X
keybind press A + hold B => action X1
so if i think abt it more now, i basically wanna know how exactly i can make sure action X1 is only performed when B is down while A is pressed
Can't you just check if B is pressed when executing X, and if it is run X1 instead? π€
Ah i guess im looking for clash strategy
Handles clashing inputs into a InputMap in a configurable fashion.
or maybe not
yes, this is basically what it comes down to, i just dont know how to implement that with leafwing
I think there's two approaches:
- Make two actions, one for X, one for X1, and make X1 a chord of the modifier + the key
- 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
hmm actually yea ive been thinking too complicated lol
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));
}
}
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.
IDK if this answers your question or if you still need an answer but would something like:
InputMap::default()
.with_dual_axis(Scroll, KeyboardVirtualAxis)
.with_dual_axis(Scroll, MouseScrollAxis);
Work for you? Treat it as rusty pseudocode π
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.
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
@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?
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.
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.
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
Alright I'll take a look into that. Thanks for the suggestion also, could be helpful to make it easier to display yeah.
Question can I use my Input states for my animations I am newbee and dont know if that would be a problem?
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
The examples are busted. It's named something else now, can't remember off the top
Can check tomorrow if you still haven't found it and ping me
Need a bit more context. You can use actions for pretty much whatever but it may or may not be idiomatic
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?
@candid vigil can we just make this pub?
sure
I also had to go in through the Reflect back door with GamepadStick::x
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 π¦
Any ideas for supporting Picking pointers and buttons in LWIM?
I want to play my animations and transition them according to the action state is there a problem with that logic? Or are there better alternatives
Which feature is frustrating to use or confusing? @pcwalton is trying to use LWIM via inspectors to dynamically tune controls. Many of the required fields aren't pub. Expectation The following ...
Nth and DreamerTalin were cooking up ideas for bubbling, but I'm not totally sure what you mean here
LWIM has stuff for combo keys, like shift + click
so just asking whether there are plans to integrate one with the other
No that sounds fine in principle
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;
}
}```
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
)
);```
you can't put them in a tuple, you need to use the method to bind them separately
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.
i'm on 0.15 and it works, what was the last version you used? D:
I'd be happy to accept PRs to expose things as needed
I'm specifically talking about VirtualDPad which replaces KeyboardVirtualDPad on main
Just noticed there was an issue to make VirtualDPad fields public anyway, I'll take a look at it
Fixes #656
Makes it easier to tune controls using inspectors and also to construct VirtualDPads using dynamic values.
And merging π
Thanks!
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?
You need contextual inputs. I faced the same issue. I used to solve this by disabling systems that react on one set of key presses and activate others.
But I ended up writing my own input crate because of it: https://github.com/projectharmonia/bevy_enhanced_input
It implements the same approach as UE.
hmm i'm very hesitant to switch input crates at this point. was there no natural way to support this in lwim?
Sure, I considered contributing it first. But after diving deeper in UE, I realized that it requires full rewrite.
At least in order to make it as in UE. Maybe it's possible to support this in some different way. But I realy liked the UE approach.
You can do what I did before: just disable systems that react on the input based on your state.
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!
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.
If they are multiple states you can just disable the ones you don't use right? Might even be an option to check for that input manually before LWIM checks anything so it picks up other inputs in the same frame on the new action (if that is desired behavior)
i'm doing action disabling with a manual state machine to ensure that only one state gets processed now. for whatever reason i was getting double processing with just naive disabling
you get two action_just_pressed from one tab press?
yes, in two separate action states
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
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
I think the disabled code just makes everything return as not being pressed
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
either way, i think it would make sense to have some sort of abstraction for moving between different action states contextually
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 π
i use States for this so the state change doesn't happen until the next frame :p
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 π€
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
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 π€
inputs are updated for disabled action states now, that was a bug fix
what's the clashing issue? maybe i'm misunderstanding something
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
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
This is a bug. I'd really appreciate an issue (or a failing test) for this π
There are some clashing strategies you can pick, but regardless of which one you pick, disabled action states fighting enabled ones seems undesirable
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
yeah agree with that. i imagine it's possible to have disabled inputs keep updating but be explicitly ignored by the clashing logic
How do I set my own values for dual_axis, like change the vector being sent by the action?
Check the arpg indirection example to see how to manually set action data
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.
@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>,
}
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
you're overthinking it, leafwing is just an input manager
all it's supposed to give you is a vec2 from any kind of input device
Yep, this should be handled in your game's code π
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.
Yeah that makes sense, I guess you would avoid having useless maps to inactive inputs
Tbh sounds like a job for bubbling (at least eventually)
Could you elaborate? I would like to come up with a relatively short-term fix for this
Read the ResMut of the offending events at the right time and then drain it
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
Oh, nice! I missed that, thank you π
argh, now in dependency hell because leafwing's egui is a different egui from my egui π¦
If you're on the latest egui I'll accept a PR to bump that version
But you can probably cargo patch LWIM's version
I downgraded leafwing's version locally
not what I wanted to do but I just wanted to get unstuck quickly
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
@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
you can use dependency ranges in the meantime, assuming they are cross compatible.
Oh right, that's a good idea
Am I dumb? Why is this 0.14 rc? https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/Cargo.toml#L48
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.
Cause main is still bevy 0.14 (and it was ported during the previous rc), there is a PR for 0.15 but idk how far it is
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!
When 0.14 exist, 0.14.0-rc.3 will pick up 0.14
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
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
:<\
Those are called "chords" in LWIM π
@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,
}```
You almost certainly have mismatched Bevy versions
I haven't published the new LWIM yet because I'm moving across the country
thanks for the response despite probably being incredibly busy. Should we just use the main branch in the meantime?
Yes, or prepare a release pr
The main branch is still 0.14 tho
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.
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
I'm getting an error that ActionState is not a component; that's probably due to mismatched bevy versions right?
Ah, my bad
Yes
No worries! Breaking change detection still isn't trivial, which is a bit of a shame
Ha, I knew better for this one
I fixed this in lightyear a while ago: https://github.com/cBournhonesque/lightyear/pull/661
LWIM 15.1 added a new triple axis input type, which breaks Lightyear. There were no tests here, and I didn't add any I added some. I just repeated the logic that was already in place for th...
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()),
(whereinput_mapsis gotten throughinput_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)
For a single player game this is totally fine
But if you split them into different Actionlikes, it's way easier to toggle them on and off as you need to
Yes
thanks alice!
@karmic monolith how goes the move?
Good! I have keys, groceries, a suitcase and a 3D printer!
No furniture or WiFi yet though so uhh may be a few days until I'm properly productive
time to 3d print all your furniture
Extremely efficient and eco friendly
There's a used furniture charity shop nearby that we're probably going to "rent" some from
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
First we need to 3D print bioplastic tools
And a hydroponics setup
hmmm true
In the US that means steal the furniture.
From Habitat for Humanity??!
No I'm going to buy some cheap furniture and then donate it back in a month lol
That's a good plan.
Chaotic Good?
Yes π
can't wait for bevy 0.15 support π₯Ί
let's let Alice get settled in her new home π
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.0when the button is not pressed, and a value of1.0when the button is pressed.
Those docs are outdated and should be removed
why aren't they converted to f32s?
i'd prefer it over as i32 as f32
The previous design where we kinda fuzzily coerced inputs into triggers / buttons / axes was really messy and hard to reason about for both users and LWIM devs
ah i see
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
oh yeah, and now that i think about it, that is kind of in disregard to semantics
It's okay to pun on it like that, but it should probably be explicit in your code
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
i can take some time to look through docs / examples / w/e and see where this behaviour is referenced to, then make a PR.
That would be super appreciated β€οΈ
p.s. - i hope you get settled into (your new home?) :D
The refactor was a huge pain
Yeah, about half-settled now!
There's wifi, one chair each and a cardboard box to use as a table
those are the highest stakes for spilling coffee i've ever heard
Rest of the stuff is in a truck hurtling across the prairies. Or sitting in a shipping yard in Montreal still, who knows π
Skill issue TBH
i haven't had time to look through the codebase - are there many tests? how do you even test an input library?
So, so many tests
Lots of input mocking
Since we're a layer or two above the hardware it isn't too bad
oh awesome. and also my condolences haha
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??"
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 π)
Yeah, that should just work, as long as you register the new input type too
If you run into problems, please let me know / fix it
The trait based design is explicitly designed to support this style of extension
good news, they were updated!
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 π
-# i believe LWIM is the unofficial accronym
ohh yeah ive seen lwim before
it's supposed to be eventually upstreamed but i feel like everyone will still keep calling it LWIM D;
https://github.com/Leafwing-Studios/leafwing-input-manager/pull/664 looks great; what's @mnmaita's handle here again?
There's a bit of cleanup to do and it would be nice to push to their branch
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
Yep, totally agree. Let me spin up an issue before we forget...
https://github.com/Leafwing-Studios/leafwing-input-manager/pull/666 @CosterM pushed a fix for the tests!
Wait is leafwing 0.15 released?
The leafwing version for bevy 0.15 isn't released yet
Not yet, but taking a look today π
GLORY TO ALICE oop oop
Ha, I did like 20% of the work this cycle
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)
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!
Yes this is the contextual input problem. Iβve made a system that carefully enables/disables actions based on player state which is mostly quite reliable now. Itβs quite annoying when inputs βleakβ between states
yeh true, the better solution at my end should be to separate the "Confirm" action on the button from the "Confirm" action that selects the tile in the other state, they have completely different meanings. I guess using a completely different ActionLike enum should be pretty reliable and not too much boilerplate
Yeah I use multiple ActionLikes for this purpose
Are there examples or idioms for assigning multiple Actionlike enums to one entity?
Just add more InputManagerBundle π
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
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.
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.)
We should be able to add a AnyKey ButtonLike TBH π
I think that we should do that
For the second, no current solution, but I'd rather like it
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
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
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?
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?
Nope that seems totally right. Can you PR this to LWIM please?
(although probably flip it for clarity)
sure thing π
is there any way i can treat mouse scroll up/down as virtual button inputs?
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)
}
```?
In 2d yes, in 3d that would be more problematic as you need to project mouse unto the 3d world. You also would need to set a empty axis like.
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
I faced the same issue and designed my own crate.
So yes π
This is what I do for my game (tho it's not split up nicely). You can just disable any inputs that aren't relevant for that 'mode'
Yeah, this is pretty reasonable
Sweet, thank you all! I'll assume for now that I've somehow registered conflicting ActionLike things and take a look with fresh eyes π
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?
Personally I disable and enable the systems that do the input handing rather than "disabling" the resources themselves.
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.
I need to read up on sets, but that looks good for me, ty! The nested enums was getting nasty
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. π
True, it's not, but it's something else I'm taking away from this π
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.
That was one option I was exploring, but I don't think I've seen many examples of that
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 typeAusing a run condition onTickActionStateSystem::<A>.
By setting a specific action state to disabled usingActionState::disable.
By disabling a specific action usingActionState::disable_action.
Looks like I need to read docs better π
You found it π
I was proud of that explanation
Where abouts in the docs? π
It's in the docs for ActionState. π
Maybe I need to sleep - that was pretty obvious π (thank you again!)
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()) π€¦ββοΈ
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?
I was able to fix this issue by deleting a camera2d I was spawning manually in addition to my camera3d. So, all good now!
Y'all, is leafwing input upstreamed in bevy or should I add the crate when starting a new game?
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
Not upstreamed yet
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>
afaik bundles themselves aren't getting deprecated, just the built-in ones that switched to required components
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,
}
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]),
);
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
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 ?
I wish I had a good answer for this; it's a very reasonable use case
I'm second-guessing the fully trait-based design π
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?
Yeah, something along those lines was what I was thinking
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!
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
The big problem with that design is that it breaks cross-device chording
Which sounds fine, until you remember that CTRL+Click exists
Having separate enums for buttonlike/axislike/dualaxis with a Custom(Box<dyn>) variant is not possible?
That's definitely possible π
I think I might have been confused there
Clearly should've made a more efficient enum-dispatch after all π€
I think that this flavor of design might be the right compromise. Let me mess around with a prototype today
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
@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.
I'll take a closer look
Actions modifying inputs feels backwards to me though: why shouldn't they just be consumers of inputs?
Just run simple example and see comments. It's very easy to pick up.
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.
They do read inputs, but it's very useful to be able to modify them.
For example, W, A, S and D are just keys. Their output is boolean. But Action can turn this inputs into directional 2-dimentional axis by modifying their axes.
I wish I could create a thread here π’
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?
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 π
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?
I found enums quite limiting for the following reasons:
- If you have a huge game, having all your actions defined in a single place is very inconvenient.
- If you want to react on inputs inside observers, you have to check them with
if.
What do you mean? π€
Yeah, me too!
You can only have 2 elements in a chord, not n
(just skimming still)
I meant "enums on the player entity about what state it's in"
Chord is a modifier, you can attach arbitrary number of modifiers
Ah okay, sure π
Sorry, not modifier, but condition
Then I'm not sure if I understand you... Could you elaborate?
In terms of 1., I think that the right pattern in LWIM here is to do many different InputMap/ActionState pairs, each with a different Actionlike generic, and then disable and enable them
WRT observers, I agree that the if blocks suck
I super don't demonstrate this though π
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
Right, let's dismiss my point 1!
I think my reasoning was mostly 2.
Yep, exactly because of this.
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)
This is why I wanted to bring it. I really think that contextual inputs is the way to go.
For what?
I need types to just play nicer with observers.
We don't serialize actions. Users serialize inputs.
Sure, but users need to map inputs to something
You can't just save a list of "KeyA, MouseButtonLeft"
Ah, games don't serialize inputs dynamically. In my game I have a settings, for example.
Keybindings rarely correspond to actions.
For example, you don't bind the same key for enter and exit car
It's the same key
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
Yep!
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 π
In theory, they can serialize actions using reflection. I.e. they serialize struct names and inside context creation user can spawn actions.
Is the backend computation of everything done using observers too?
Yes, Unreal provides automation for it.
But I currently don't.
Happy to reconsider.
Or is that still systems, in order to handle clash handling?
Clashes are so annoying, because it forces you to take a somewhat global view of things
It's all done in a single system that iterates over context in specific order.
Yeah, they are very annoying. Here is how I handle them:
- Each context have pripority.
- Actions consume inputs optionally (we aren't writing anything back to Bevy).
- Actions evaluated in their order.
"in their order" = "order added?"
It may sound simple, but switching between context / layering wasn't easy to implement π
Yep
Do you think that observers should be the single blessed way for users to react to inputs being pressed?
(or whatever)
I really do. I enjoy using observer-based reaction on inputs in my game.
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
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.
(that's fixable upstream though IMO)
But I'm open to reconsider
Yeah, both regular events and state-checking feel mostly obsolete to me
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
Another cool thing I discovered is the removal of "Pressed", "Just presed", etc.
Yeah?
Instead we have conditions.
The "hold" etc variants are super useful, but felt very wasteful under the existing design
Users decide when the action is active based on them. For example, "Press for 3 seconds". Or "Tap twice".
It's all can be expressed with conditions.
Yeah. Not impossible to do in LWIM's design either, but a bit messy to implement
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:
- You just slap hold condition.
- Observe for "Completed" for strong attack.
- Observer for "Cancelled" for regular one.
Which is similar to how we're doing things over with picking events
(man observers are so slick)
I got very inspired by them π
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
Do what I've been planning to do for the stupid tick problem with networking ... Upstream a centralized way for things to know there is some UI focused and then have the crates make sure they use the centralized approach π€
I still need to take a crack at that tick problem actually 
That's half of why I added bevy_input_focus this cycle
Events are triggered for the context entity. They are bubbled by default. But I don't provide a way to configure it π€
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.
Yeah, good to bring up at this stage!
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
Makes sense!
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
I think this is what I'm also doing. I have 3 enums, one for motion, one for combat and one for contextual inputs ... Only the enums that can actually do anything are active at once ... I'll soon need to add a 4th one for "build mode" at which point every input will conflict with at least one other input in another enum π
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)
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
@viscid swallow i think you would want in on this
When you first squint at the problem it looks really easy!
Every input-related problem be like that. I thought sending inputs to a game server was easy at first too, but I have a whole crate for it now, and it even has dependencies on code that I have yet to pull out into a dedicated crate!
Agree. This is the reason why I created a separate crate. I wanted to try completely different approach. I had 2 or 3 very drastically different versions of it before I more or less settled on the design.
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 π
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
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
When you work on this, highly suggest to read https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine as additonal prior art.
In theory yes. At the very least it beats input-level networking ... The main issue is just how a lot of the obvious ways to use LWIM, and the fact that it's still just input but mapped in a funny way
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 π
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.
Yea that's actually what I do, for something like selecting a target it makes a lot more sense to just send the entity (and then map it to a server entity) rather than the server trying to figure out the same target and risk getting it wrong
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 π
I actually have reversed my position on this, as I discussed in https://discord.com/channels/691052431525675048/1327005430861271182
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.
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
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.
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.
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 π
Yeah, I think that you need to apply them at both
Do you have an example of what the input level modifier API looks like?
"dead zone calibration for specific gamepads"
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
Could you elaborate?
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?
This is for sets. But you can apply them to inputs with exactly the same API. I probably should add an example or doc comment for it π€
I was looking at this line: GamepadStick::Left.with_modifiers_each(Negate::all())
GamepadStick is a set because in Bevy they are one-dimensional π But yes, the same idea.
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
A trigger that causes the reconstruction of all active context maps.
(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?
In your example you won't hardcode Space, you load it from your settings struct. And when context reloads, it will rebuild the context, including all modifiers.
ok, so the slots are user defined instead of defined by the library?
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.
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.
You assign modifier directly to input or action
I imagine the user would do something like:
ctx.bind::<Rotate>()
.to((settings.rotate.pc_input.modify(Negate)));
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));
I don't think that's what is confusing me, I understood that
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.
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
I wanted to provide it, but it requires writing UI.
I have it working in my game, though. But it's quite complex as for an example.
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
Could you try to elaborate on the issue once again?
Yes that's why I asking my question earlier about whether it was possible to associate data on a given Action/ActionLike. My use-case was networking an action like Shoot(Entity)
@bold lodge how does using observers work for fixed timestep? Do you just hold onto the action events until the schedule is ready?
Good question!
I actually don't do anything special. I just listen for inputs in PreUpdate, right after Bevy's InputSystem.
I think that will trigger them too soon tbh
I'll add a section to the doc for this
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?
Ah, I see... I suspected that it will require some additional workaround to make it work properly π€
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.
Yep, that's how I imagined your library solves it.
But in leafwing it seems like the idea is to serialize the InputMap directly
LWIM's serialization is... questionable currently
Which is one of the things that started me down this rabbithole
You rarely want to serialize any kind of inputs in a single struct. You usually have a separate menu (or logic) for rebinding keyboard, mouse and gamepad.
I.e. keyboard bindings usually separate from mouse bindings.
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.
This sounds a bit limiting π€
With explicit interface you have full control over the serialization. And it's not very boilerplate.
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)
Like it's possible to serialize modifiers and conditions, but the output format won't be very nice.
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.
I think it might be implemented just as an abstraction on top. Since you handle settings creation anyway.
Yeah, just need to figure out how to do that in a nice way.
Updated the docs, thanks for bringing it up:
https://github.com/projectharmonia/bevy_enhanced_input/commit/dc1f41a9605aef97c6a22bac86037bafd51c30cc
@karmic monolith thinking about this... Why it might be a problem?
Bevy read inputs in PreUpdate and we update action state right away and trigger events. And only then FixedUpdate happens. Do you think that it might be limiting for users to missing the control over when to react on actions?
Observers will respond to actions at the end of / during PreUpdate. Which is a problem, because users working in FixedUpdate want to be processing the actions during FixedUpdate, and only the actions corresponding to inputs that have been seen at that particular point in virtual time (which lags behind real time)
Ah, I see!
That will require a rework. Maybe we can read inputs in FixedUpdate based on time and only then trigger actions? π€
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
Maybe always handle them in FixedUpdate then?
Will be simpler to implement.
Maybe. FixedUpdate is at least currently pitched as an "optional" feature
Ah, It might introduce input delay, which is undesirable.
Maybe you are right, we need to provide a way to configure it.
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
Yep, this is why I agree with Alice that it should be configurable!
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)
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
I relied on this example: https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/examples/default_controls.rs
Did you annotate your PlayerAction::Move variant with https://github.com/Leafwing-Studios/leafwing-input-manager/blob/3eda257f56a2e0e6821d50e744f6a7a0dc823a26/examples/default_controls.rs#L17
@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):
- Add
Input::Custom(u8)variant whereu8is custom input ID. - Create a resouce called something like
CustomInputthat is a hashmap withu8as key and https://docs.rs/bevy_enhanced_input/latest/bevy_enhanced_input/action_value/enum.ActionValue.html as value. - 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
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)
Are your brake and throttle actions annotated so that they are axis-like?
iirc the default is button-like
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
I believe this is a bug in the implementation; check the issues?
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?
Never mind. In this case I was using LWIM bind to menu buttons, based on https://github.com/Leafwing-Studios/leafwing-input-manager/blob/v0.14.0/examples/ui_driven_actions.rs which was removed in 0.15 with the ActionStateDriver. So as I upgraded, I went the vanilla bevy route for app hotkeys using Res<ButtonInput<KeyCode>> to catch the modifier with .any_pressed and EventReader<KeyboardInput> to access a keyboard_event.logical_key.
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
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? π₯²
https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/RELEASES.md#version-07
Is the only documented thing I found about it, but it doesn't seem to work for me?
It disables input if egui is using it.
If it doesn't work - you have a version mismatch.
Does this include mouseclicks?
I'm using bevy-inspector-egui, and both it and leafwing are using bevy_egui 0.32 afaik
nvm leafwing 0.16 is on bevy_egui 0.31 π , thanks for the hint
hi, why am i getting this error? :c
lsp bug probably. it's gone for now.
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?
how to build an axis from keyboard keys?
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?
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
I think you need to assign it to a variable first before doing the chain, since each method in the chain returns &mut Self rather than consuming self and returning Self
ooh i see
why though, when the source code just says it returns Self?
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?
it seemed to have worked replacing insert_ with with_
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
@pliant hornet did you ever figure out the analog triggers issue?
sadly not, I ended up just rolling my own input system for gamepad inputs
@minor flicker were you able to bind a gamepad trigger to an axis and successfully get its clamped value?
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
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
I'm running into this :\ anything ever came of it?
This needs a pretty easy PR, but I haven't had the time to tackle it
no worries! I haven't dug into this codebase before iirc.. any very high level suggestion/direction to look toward if I wanted to take a stab at it?
You should be able to just specialize the implementation of the Buttonlike implementation for gamepad axes IIRC
should InputMap<T> require ActionState<T>?
Yes I think so
I have an observer that adds ActionState automatically whenever InputMap is added
I'd merge a PR for this π€
i'll make an issue about it, but i think it should have a bit more discussion first.
i can't think of a scenario where you'd want an InputMap without an ActionState, but there could very well be one
The converse is definitely true (bots)
for example, storing settings as entities
I think in that case you can just make a little wrapper
@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?
That's fine to do π
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
You're going to want to set the max threads: that's doc tests being over-parallelized
I'm annoyed at it too, and I'm hopeful that Rust 2024's doc test changes will make it not do that
ah thank you! yeah. it's a very annoying assumption for doc tests...
@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);
}
Keep it in the prelude
@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.
it looks like you were going to!
https://github.com/Leafwing-Studios/leafwing-input-manager/pull/676
π i didn't catch this since there was no attached issue.
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?
I guess I am better off avoiding a chord and instead mapping the left mouse button to a mode, where I can check pressed?
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?
Thanks! This looks interesting. Iβll see if it fits my needs!
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)
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.
camera movement*
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
I believe there's a setting for this in bevy_input that drives it
im not sure where i should ask this, but is there any good way i can grab this branch of lwim?
https://github.com/Leafwing-Studios/leafwing-input-manager/pull/676
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
}
You can use a [patch.crates-io] entry pointing to that branch to use it ... But given that it seems to just add a require .. You could also just use https://docs.rs/bevy/latest/bevy/prelude/struct.App.html#method.register_required_components to get that behavior as long as you register it for every Actionlike you use π€
ohh, right! thank you! i think this should work.
you need to explicitly add an inputmanager plugin for each actionlike anyway, right?
so i could just have my own InputManagerPlugin that does this and then adds the real input manager plugin
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
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>`
}
}
looks like i needed the trait bounds: A: Actionlike + TypePath + GetTypeRegistration