#bevy_enhanced_input
1 messages ยท Page 6 of 1
what I meant is right now how I see it I got this and I do not understand why I have to spawn Action::<Modifier> in order to use modifier binding for CycleTabBack, Modifier component feels like a third leg:
Actions::<ModalInput>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
let modifier = context
.spawn((Action::<Modifier>::new(), bindings![KeyCode::ShiftLeft]))
.id();
context.spawn((
Action::<CycleTab>::new(),
bindings![KeyCode::Tab, GamepadButton::RightTrigger],
));
context.spawn((
Action::<CycleTabBack>::new(),
Chord::single(modifier),
bindings![KeyCode::Tab],
));
context.spawn((
Action::<CycleTabBack>::new(),
bindings![GamepadButton::LeftTrigger],
));
})),
));
is there a better way to structure this?
I think in your case you do not need the Chord in order to use Shift with Tab.
Hold on, let me think about this...
Actions::<ModalInput>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
context.spawn((
Action::<CycleTabBack>::new(),
bindings![(KeyCode::Tab).with_mod_keys(ModKeys::SHIFT), GamepadButton::LeftTrigger],
));
context.spawn((
Action::<CycleTab>::new(),
bindings![KeyCode::Tab, GamepadButton::RightTrigger],
));
})),
I think this will work, but the KeyCode::Tab might need to be adjusted a bit. My BEI is a bit rusty.
Yes, that's how I'd do it.
Fan fact: in Unreal they don't provide a built-in support for keyboard modifiers, so you always need to create a chord for any key combination.
Does with_mod_keys support side unique modifiers like Left or Right Shift only? I haven't looked through the code but the documentation seems to only designate the type of modifier, not side.
Yes, only the modifier itself.
We can easily add support for it, though ๐ค If you're interested - feel free to open a PR!
As I'm looking at possible code changes that would need to be made, I see that ModKeys::iter_keys returns an iterator of [KeyCode; 2]. Would you be alright with returning a Vec<KeyCode> instead or is there another option that would be better?
That's fine
I think iter_keys iterate only over non-combined flags.
If we define things like SHIFT as = SHIFT_LEFT | SHIFT_RIGHT, we can just return KeyCode (non-array).
Okay. I was thinking to use that approach for the values. But that brings up another possible issue. Currently SHIFT is defined as 0b00000010 while I'd like to define it to equal both SHIFT_LEFT and SHIFT_RIGHT so 0b00100010. This would cause a change in how things have been serialized and de-serialized where the previous combined modifier would now only be the left versions. If that is the direction we want to take, would I indicate the change in the Changelog or do we have any separate migration documentation that needs to be updated as well?
The values are serialized as names. So I think the SHIFT would still work.
You know, I updated the test to include the sided modifiers and didn't even process that it was a string.
nice! that's hell of a lot better, modkeys are a game changer, thank you!
I'm trying to get a specific behavior with BEI and not sure how to go about it:
- Grid-based move input with
Ordinal::numpad() Pulsecondition withtrigger_on_start: true- I'd like the input to additionally fire any time the user hits a numpad key, even if one of the keys is already down (so: fire on input changes and re-start the pulse)
perhaps I should just re-implement Pulse with a custom InputCondition::evaluate for my needs?
the problem is basically if the user hits another key within the initial_delay period of the Pulse condition, the input doesn't fire again even if the "cooldown" (i.e. interval) is already up
so the input feels weird and unresponsive
You either need a custom Pulse or use separate actions for each direction
alrighty, a custom Pulse seems somewhat easier so i'll try that, thanks
I'd like to hear user opinions.
We have modifiers represented as bitflags, and right now they work similarly to how OSes handle them.
For example, you can attach SHIFT to a binding, and it will mean "any shift (left or right)".
So CTRL | SHIFT means Any Ctrl + Any Shift.
We decided to extend the bitset by adding side-specific modifiers. PR: https://github.com/simgine/bevy_enhanced_input/pull/301
It adds *_LEFT and *_RIGHT variants and makes the existing variants equal to *_LEFT | *_RIGHT. While this works, it makes the bitset behave inconsistently:
- OR-ing different modifiers means "all of them should be pressed". For example,
CTRL_LEFT | SHIFT_LEFTmeans Left Ctrl + Left Shift. - OR-ing two sides means logical "any of". For example,
CTRL_LEFT | CTRL_RIGHTmeans Any Ctrl, to preserve the current functionality.
This inconsistency is why I'm unsure about the change.
For example:
CTRL_LEFT | CTRL_RIGHT | SHIFT_LEFTmeans Any Ctrl + Left Shift.ModKeys::pressedreturnsSHIFTif the user presses both Shift keys, which is equivalent to Any Shift. I'd expect it to return something that represents Both Shifts.
Yeah 2. Above is a bit unexpected to me.
If I have Ctrl + Shift + P, I expect I need to press all 3 keys to trigger it.
If, all of a sudden, LeftCtrl + RightCtrl + LeftShift + RightShift + P means the same thing, that seems unexpected because it reads as โuser needs to press all 5 keys to triggerโ but I think youโre saying this particular example is the same as Ctrl + Shift + P.
I think if you want to prevent (CONTROL_LEFT | CONTROL_RIGHT) == CONTROL then the solution probably needs to move away from bitflags for how they are represented. Having bitflags for either as well as sided modifiers would require checking for exclusivity between them. I think an enum that has the exclusive variants might be a possible solution:
enum ModSet {
#[default]
Either,
Left,
Right,
Both,
}
struct ModKeys {
shift: Option<ModSet>,
control: Option<ModSet>,
alt: Option<ModSet>,
super: Option<ModSet>,
}
If you still want easy modifier creation, perhaps using a DSL based on how they are usually represented would be best: the modifier itself like "Ctrl" means either, the modifier with direction indicates the side such as "Right Alt", and if both are listed like "Left Super + Right Super" would mean both. This would also move away from possible issues with bit operations.
Do we want to support modifiers from different sides, like LEFT_CTRL + RIGHT_SHIFT or having a global "side" per ModKeys is enough? This might simplify things ๐ค
We might even get away with bitflags in this case.
But this makes sense. We can expose a bunch of constructors and implement OR operator.
I think since you are concerned with (CONTROL_LEFT | CONTROL_RIGHT) having confusing meaning that you already acknowledge the possibility someone will try to use it or other combinations of different sided modifiers and I agree that should be considered. My mouse has programmable buttons and can output either side of modifier keys and input devices that follow the USB HID Keyboard specification to output key presses should be able to do the same.
While I don't think it will be common I do think if any games wants to support user defined keybindings then we probably should plan to allow any valid set of bindings. The fact that third party software like AutoHotkey, Joy2Key, and Joystick Gremlin exists to make it possible to do bindings that were not valid within games or other software shows that users will try to find a way.
Users aren't limited by what ModKeys support. They always can create a Chord for those rare cases.
ModKeys exists to reduce boilerplate for common cases. Originally I thought extending it to side-specific modifiers will be trivial, but I haven't considered the mentioned inconsistencies ๐ค
I'm fine with making the struct more complicated, but I want to make sure we won't introduce weird behavior to other parts of the API.
Is there a reason for the separation between ModKeys and Chord? If there is a benefit to using ModKeys over Chord then I think that benefit should apply to even the rare cases otherwise it might cause further confusion to developers as to why certain modifier combinations work with ModKeys while others don't seem to. I think you previously mentioned that Unreal doesn't provide built in support for keyboard modifiers so in that case it makes me wonder if perhaps ModKeys is an extra API that creates two sets of input modifiers that points to a possible underlying issue.
Chord is very flexible because it's an input condition, so you can bind anything to it.
But it would be annoying to create a separate action for cases like Ctrl + C. It always felt unergonomic in UE to me. This is why I added a shortcut via ModKeys. It's ergonomic and enough for most cases.
Ok, this works, but If I press shift+tab what happens is first the CycleTabBack action started, and then immediately the CycleTab fired. is there a way to specifically tellthe action that it should not be fired when this key is with mod?
You need to enable keys consumption
oooh, I forgot about this. thanks
I'm trying to add controller input, but I'm a little confused on what Swizzle does.
Can somebody explain how it works in this example?:
(KeyCode::KeyW, SwizzleAxis::YXZ),
Swizzling is basically like taking a thing, usually vectors and things, and outputting specific parts in a specific order. In this case instead of default Vec3 X, Y, Z order you would get it back in Y, X, Z order. You can go crazy with it. You could even go crazy and do Z, Z, Z if you were so inclined depending on your use case
Ok, but I still don't get why that's helpful for mapping bindings.
Also, judging from the docs, I'm probably going to have to redo my inputs. Currently, each movement direction is its own Action, while it seems that the intended usage is a single "Movement" Action that encompasses all of them.
That's where swizzling would be more useful. For example if you map an analog stick you have an X and Y input in the same action but if you swizzle you could transform that into Y and Z respectively for instance without having to manually map it yourself each time. It's just a convenience feature really
This depends on your game. If you want separate action for each direction - it's fine.
Sometimes having each direction be a discrete input is preferable. a lot of these libraries have a lot of extra bells and whistles that you may not need. I think it's important to know what tools are available but don't feel like you have to use it just because it's there. If it makes more sense for your control scheme to be able to remap up, down, left, right for example then just do that. You can also have multiple physical inputs bound to the same thing like I have games that accept WASD, arrow keys, analog sticks etc under one Action. It may or may not be preferable to do it that way but it's possible.
I think my system doesn't work the same as that. Currently it's just with "LeftAction", "RightAction", "ForwardAction", and "BackwardAction".
Whereas GamePad has just x and y representing two directions per axis.
Basically, I'm back to thinking if I need to redesign my input system.
Unless there's some way to associate only the positive or negative values of an axis (possibly as an absolute value).
I don't think this works with axes, might be something worth adding:
https://docs.rs/bevy_enhanced_input/latest/bevy_enhanced_input/preset/bidirectional/struct.Bidirectional.html
A preset to map 2 buttons as 1-dimensional input.
That's what I was getting to in my other message. It's fine to do it that way. Only you know your game and how you think it's easiest to work with. There's not a universal right answer
Anyone interested in writing a controller example for BEI using pull-style API? ๐
You mean observers? hmm, I guess not, the current controller example already uses observers
Pull-style is getting values using regular systems. Similar to how it's done in LWIM.
We have a draft for it, but requires polishing: https://github.com/simgine/bevy_enhanced_input/pull/173
But creating an example from scratch is also an option. I'd do a 2D example ๐ค
On the internet there is a bunch of mario-style controllers, it would be nice to adapt one of them, for example.
Having a proper complex controller for Bevy as an example would be great.
Just listing options, I'd welcome any example ๐
I'm just a bit lazy It's a great opportunity for new contributors to learn
sure, I can help with that. IIUC I can adapt linked example same way I use BEI actions in Update systems
Is there an easy answer to the question: "In what schedule, or where, should I enable/disable my actions?" (as in, add/remove Disabled component).
Tension: If you call mock_once and then immediately disable the action (after BEI's systems have run), BEI's update system will not see this action until you enable it again. So if you have that action disabled for 27s (for example), at the end of 27s the action enables again, BEI's update system now sees it and the mock you called 27s ago runs, even though it was supposed to be a "once" mock that only ran one time.
Right now, Disabled seems to act a bit like "pause" for BEI actions in some cases. But I wonder if it should instead be more like "stop" or "reset". So, for example, in this case, it would actually reset any pending or in progress action mock. I haven't thought through the consequences of doing this, just thinking out loud.
I'd expect Disabled adding ContextAvtivity::<MyContext>::INACTIVE. IIUC it prevents BEI from listening to the input altogether, right?
ContextActivity::<MyContext>::INACTIVE lets you disable the whole context. Adding Disabled to individual actions lets you control which actions within a context you want to disable.
We provide system labels where the update happens. You can order your systems precisely if needed.
I'd like to avoid adding special handling for Disabled. Right now it "just works" because it simply prevents BEI from seeing the entities.
Yeah I know, and I use it. Just wondered if there was any clear advice there. I currently disabled actions before BEI runs.
It's a valid approach!
I can see that desire. At least in this case I managed to solve the problem by not allowing that mock_once to even happen. That way, when the action became enabled again there was no pending mock that BEI executed.
I think if you want this behavior, you can additionally reset the mock ๐ค
Hmmm yes maybe I can. Yeah I probably can sort it out on my end. I wrote a test case where I ran 1000 updates and that โonceโ mock was still just sitting there and on frame 1001 after Disabled was removed, and the action was โunpausedโ the โonceโ mock ran, and so it stood out to me as possibly weird so I thought Iโd ask here. But Iโm happy to leave it to being solvable in user land for now.
I'm not against simplifying it somehow by extending the API, but I think Disabled specifically should work predictably - it should just make the entity invisible from systems, kinda like pausing it, preserving the state.
How do I translate right axis to mouse movement?
You mean the whole right stick? Because right axis is X or Y.
Right stick, yes.
In fact. I change my question. How do I Bind MouseMotion?
Is it also a Vec2?
I'm trying to tie MouseMotion and the right stick to the same Action.
We have a built-in preset for it to map 2 axes of any stick to Vec2
Yes
Check examples for how to map multiple bindings to the same action.
I was able to tie them together, and while the Gamepad stick feels fine, the mouse feels weird, like dragging it trough mud.
Is there a way to get something like MouseMotion.delta()?
You can adjust the sensitivity by adding a modifier
Check the basic_action_management example
Still feels weird. I decided to just separate the two inputs.
So, this doesn't seem to be getting triggered.
(Action::<CameraScroll>::new(),bindings![Binding::mouse_wheel()]),
#[derive(InputAction)]
#[action_output(f32)]
pub struct CameraScroll;
fn camera_zoom(camera_scroll: On<Start<CameraScroll>>, mut spring_arm: Single<&mut RayCaster, With<PlayerSpringArm>>,) {
spring_arm.max_distance += 5. * camera_scroll.value.signum()
}
Which is a bit confusing.
A an input bound to an Action<C>.
I combine mouse and stick movement in my game just fine.
Jeez triggers are slow
i'm never using that for camera movement
Makes me wonder if there might be input latency for buttons/keys
What do you mean?
Anything that is a constant input like mouse motion results in bevy stuttering to activate the trigger, and results in the camera moving in disjointed sections
Only thing I can imagine it to be is that triggers are slowing it down, so I use Single<&Action<>> instead
Triggers have also been said to be slow by others as well
I would love to see (and upstream) actual benchmarks for this, rather than trying to reason about this by vibes
I think it's the case only if you execute heavy logic inside it.
Running a system for a small logic adds overhead from the parallelism.
I use only observers in my game just fine ๐ค
Has anyone come up with a good solution for stopping typing in bevy inspector egui's text box to search for an entity triggering BEI actions?
Never said they decrease fps
I'm only noticing effects visually when using it for mouse motion/camera movement, which is why i'm merely stating that it might be a concern that it increases input latency, which isn't the easiest thing to measure visually
I think the quick start guide mentions a workaround for bevy_ui, but it's similar for egui.
Thanks, I had missed this page in the docs, looks like it could help.
Not sure If I get you.
If you having problems with observers, you need to provide a minimal reproducible example because you're the first who reports this.
I remember in egui they expose a resource which tells whether keyboard or mouse is occupied.
I would include an example for it as well, but it requires depending on egui.
Even when it's in dev-dependencies, it prevents me from following RC.
But it's simple, just instead of Query, you check its resources.
Perhaps if not an example, adding to the quick start could be an option if that interests you.
I meant a doc example, it requires depending on egui.
Unless I mark it as no_compile, but this could quickly become broken ๐ค
I think when we upstream it, it would be better for egui to provide an example.
#[derive(InputAction)]
#[action_output(Vec2)]
pub struct MoveCamera;
#[derive(Component)]
pub struct PlayerCameraPivot;
#[derive(Resource)]
pub struct CameraValues {
pub sensitivity: f32,
pub rotation: Vec2,
}
//spawn this under any input context
(Action::<MoveCamera>::new(),bindings![Binding::mouse_motion()]),
fn camera(
move_camera: On<Start<MoveCamera>>,
mut camera_pivot: Single<&mut Transform, With<PlayerCameraPivot>>,
mut camera_values: ResMut<CameraValues>,
time: Res<Time>,
) {
let sensitivity = camera_values.sensitivity;
camera_values.rotation -= move_camera.value * sensitivity * time.delta_secs();
camera_values.rotation.y = camera_values
.rotation
.y
.clamp(-89f32.to_radians(), 89f32.to_radians());
camera_pivot.rotation = Quat::from_rotation_y(camera_values.rotation.x)
* Quat::from_rotation_x(camera_values.rotation.y);
}
This?
pub fn pointer_check(mut actionsources: ResMut<ActionSources>, egui: Res<EguiWantsInput>) {
if egui_wants_any_pointer_input(egui) {
actionsources.mouse_buttons = false
} else {
actionsources.mouse_buttons = true
}
}
pub fn keyboard_check(mut actionsources: ResMut<ActionSources>, egui: Res<EguiWantsInput>) {
if egui_wants_any_keyboard_input(egui) {
actionsources.keyboard = false
} else {
actionsources.keyboard = true
}
}
Woops, fixed this
Awesome thanks for sharing. Here's what I did which works well enough for now (system is verbose but I didn't make any effort to shorten it):
app.add_systems(
PostUpdate,
toggle_action_sources
.after(EguiPostUpdateSet::ProcessOutput)
.run_if(resource_changed::<EguiWantsInput>),
);
fn toggle_action_sources(
egui_wants_input: If<Res<EguiWantsInput>>,
mut action_sources: If<ResMut<ActionSources>>,
) {
if egui_wants_input.wants_any_pointer_input() {
if action_sources.mouse_buttons == true {
action_sources.mouse_buttons = false;
debug!("Disabled mouse button input");
}
if action_sources.mouse_motion == true {
action_sources.mouse_motion = false;
debug!("Disabled mouse motion input");
}
if action_sources.mouse_wheel == true {
action_sources.mouse_wheel = false;
debug!("Disabled mouse wheel input");
}
} else {
if action_sources.mouse_buttons == false {
action_sources.mouse_buttons = true;
debug!("Enabled mouse button input");
}
if action_sources.mouse_motion == false {
action_sources.mouse_motion = true;
debug!("Enabled mouse motion input");
}
if action_sources.mouse_wheel == false {
action_sources.mouse_wheel = true;
debug!("Enabled mouse wheel input");
}
}
if egui_wants_input.wants_any_keyboard_input() {
if action_sources.keyboard == true {
action_sources.keyboard = false;
debug!("Disabled keyboard input");
}
} else {
if action_sources.keyboard == false {
action_sources.keyboard = true;
debug!("Enabled keyboard input");
}
}
}
You could remove the if statements for the actionsources and just set them if egui wants input or not but overall yeah that's about right
Yeah the if statements were just to avoid mutating ActionSources resource for no reason. Probably overkill, but ๐คท
iirc the most cost you'll get from mutable queries is querying them and ResMut
past that assigning just has the cost you'd expect
Fixed by replacing Start with Fire
Apologies, used to old observers
is it somehow possible to add actions to a context from different systems, currently I have the following systems but they seem to replace the actions for the Player context instead of adding both sets of actions:
fn add_player_input_actions(
e_player_added: On<Add, Player>,
mut commands: Commands,
) {
info!("Adding player input actions.");
commands.entity(e_player_added.entity).insert(actions!(
Player[
(
Action::<WalkVertical>::new(),
Bindings::spawn((Bidirectional::new(KeyCode::KeyW, KeyCode::KeyS), Bidirectional::new(KeyCode::ArrowUp, KeyCode::ArrowDown)))
),
(
Action::<WalkHorizontal>::new(),
Bindings::spawn((Bidirectional::new(KeyCode::KeyD, KeyCode::KeyA), Bidirectional::new(KeyCode::ArrowRight, KeyCode::ArrowLeft)))
)
]
));
}
fn add_player_input_actions_open_inventory(
e_general_controls_added: On<Add, Player>,
mut commands: Commands,
) {
commands
.entity(e_general_controls_added.entity)
.insert(actions!(
Player[(Action::<OpenInventory>::new(), bindings![KeyCode::KeyI])]
));
}
Yes.
insert(actions![...]) overrides the Actions component, that's why it doesn't work.
You need to use commands.insert_related::<Actions<C>>. Or just spawn entities with ActionOf<C> component. Exactly the same way you would do with parent/children.
Iโm going to try adding a user land version of mock_once that does nothing if the action is Disabled and see how that design works for me. I am disabling actions at the start of every frame (as I once said), but inside a frame, itโs so easy to call mock_once on AI entities but forget to check if the action youโre mocking is even enabled. Even if you do remember to check if the action is enabled, it feels unergonomic to have to fetch the actionโs Disabled state just before calling a method on commands. I guess I might call it mock_once_if_enabled.
No action needed here, just sharing my current thinking.
But if the action is disabled, you won't see it in queries, no?
Iโd use Has<Disabled> when querying.
Note: maybe to help visualise this, imagine a system that runs every 1s and for each AI entity it cycles through each of the actions it has. Each time it picks one action, it calls mock_once. But in the case the action is disabled, we get in the weird state I previously described. So instead I wrap the mock_once call in a check to only do it if the action is enabled (and that data comes off a query on this system that uses Has<Disabled>).
But why use Has<Disabled> if you don't want to mock disabled actions? ๐ค
Has<Disabled> makes the query see the disabled actions.
Without Has<Disabled> it will skip disabled entities automatically.
I guess not every call to mock_once originates from action query data.
Imagine some logic that triggers an action after 10s. Well, I donโt need to have queried for the action, so I wonโt know if itโs disabled or not. All I need access to in order to call mock_once is the context entity.
To make it clearer: Imagine a unit was told to charge after 10s. I can just trigger the action using mock_once: thereโs no action query data in sight.
Not sure if I get it, could you elaborate?
I would imagine if you have Has<Disabled> in your query, it's expected to do the necessary checks it.
fn on_selecting_magic_item_target_started(
start: On<Start<SelectingMagicItemTargetAction>>,
mut commands: Commands,
charge_magic_item_action_query: Query<
&ActionOf<SelectingMagicItemTargetContext>,
With<Action<ChargeMagicItemAction>>,
>,
) {
let magic_item_entity = start.context;
commands
.entity(magic_item_entity)
.try_insert(ContextActivity::<SelectingMagicItemTargetContext>::ACTIVE);
// If the magic item has a charge magic item action, trigger it because
// starting a selecting magic item target action also starts charging the
// magic item (if it can be charged).
if charge_magic_item_action_query
.iter()
.any(|action_of| **action_of == magic_item_entity)
{
commands
.entity(magic_item_entity)
.try_mock_once::<SelectingMagicItemTargetContext, ChargeMagicItemAction>(
TriggerState::Fired,
true,
);
}
}
- Not sure if the following gives enough context to you on the action/context setup, but:
SelectingMagicItemTargetActionis an action in the magic item's root context- The observer activates
SelectingMagicItemTargetContext - But immediately also fires off a
ChargeMagicItemActionaction (which is an action inSelectingMagicItemTargetContext) - In my PreUpdate system I will disable
ChargeMagicItemActionif it cannot be fired (e.g., not enough mana) - But in this observer, I don't currently check if
ChargeMagicItemActionis actually enabled/disabled so right now, thistry_mock_onceis going to incorrectly sit "queued" in BEI until the action becomes enabled. - My two options for fixes are a) Write my own
try_mock_once_if_enabled, or b) wrap thetry_mock_oncecall in a check that first queries for the action'sDisabledstate.
- The general pattern here is just:
try_mock_once::<C, A>can be called from anywhere, and you needn't have fetched actionAbeforehand.
- To be clear: I know this example does have a query
charge_magic_item_action_queryand therefore it does have a gate on it, but not every call totry_mock_oncehas to look like this.
Ah, I get it now!
I'd check for Disabled. But I'd also consider moving the gameplay logic (like the check for mana) outside of BEI.
I'm using BEI heavily for AI actions. The features that are good so far are:
- Disabled: I can just make an action disabled if all the conditions are not true for it to be fire-able.
- Start/Complete events: These are really handy.
- Parity between AI/player unit actions: More or less, players and AI are the same it's just that players have bindings / AI has action mocks enabled.
I mention this because the mana check is done as part of working out if the BEI "use magic item" or "charge magic item" action can even be enabled, and so the BEI action having all of the above features is providing a lot of value in achieving that and not having to have two different ways of doing gameplay logic or enablement checks. By this I mean that players have many actions that are definitely BEI because they are triggered by bindings. AI have the same actions just triggered by mocks. Using/charging magic items is yet another action that a player can do and therefore AI can do, and so currently there's a strong link between gameplay logic / enablement and BEI actions. Whether that's the right approach I guess I'll find out, or maybe I will be convinced otherwise ๐
(To give you an idea on what it looks like, this WIP system runs before BEI every frame and you can see that it just amounts to working out if an action should be enabled or not: https://gist.github.com/mgi388/917653868844aaa8237b0dff9e6c183d)
I see, this sounds like a convincing use case. Okay, play with it and let me know.
Opened a draft that targets the latest Cart's branch with BSN:
https://github.com/simgine/bevy_enhanced_input/pull/305
pull based api example ready
@inner sky why doing character controller in Update is antipattern? ๐ค
there's different layers of answer to that question. Are you asking why moving the character around in Update is an antipattern?
Yep ๐
I wrote a piece on that here: https://bevy.org/examples/movement/physics-in-fixed-timestep/
Handles input, physics, and rendering in an industry-standard way by using a fixed timestep
Could you take a look at it? It's a bit hard to summarize ๐
otherwise I would do so
Ah, thanks!
While I agree with what you wrote, it's only relevant to games with physics. Even in Godot's "First game tutorial" they just multiply the velocity by delta.
So I think it's fine for an example ๐ค Maybe just worth linking the article just in case.
I agree in principle, but the example shows something that does not actually work in practice
also, "physics" is relative here. A 2D game with top-down controls will also run into problems with tunneling if you don't use deltatime
so that already counts as "physics" for our definition
Bevy in general is kinda bad at teaching people that game logic fundamentally belongs in a fixed timestep, and this example would make that situation a little bit worse by suggesting that a character controller is fine in Update and presenting code that you would have to modify in cumbersome ways to get the actual code you want
especially the second part is what irks me, since it makes it hard for people that want to do it right
IMO better to show the cumbersome API and be honest about it rather than showing the simple API that won't work on machines with low FPS
that, or change the example to just show the pull-based API in a vacuum outside the context of character controllers
Anyhoot, I'll leave it up to your call of course ๐
No harsh feelings if you disagree or anything!
In my opinion, since BEI is slated to be upstreamed I think it would be better to show best practices as well.
I agree. Quick question, to add the mentioned workaround we need to use push or pull-style API?
both work as long as you capture the input in PreUpdate and apply it piecemeal in FixedUpdate, sec
A fun 3D Kinematic Character Controller for Bevy. Contribute to janhohenheim/bevy_ahoy development by creating an account on GitHub.
here's how I do it with push-style
Got it! Here is what I've been thinking:
- Rework our
local_multiplayerexample into a snake game with 2 snakes. In this kind of game we can mention that it's ok to useUpdate. It also looks fun. - Add
pull_style_apithat mirrorsbasic_action_management, but uses pull-style API. - Write a proper character controller example instead of the current one.
Note that if you do pull-style, the system should be in PreUpdate after the BEI system set, as FixedUpdate happens before Update. So if you pull in Update, you will get a one-frame lag.
that sounds excellent ๐
(this is the kind of papercut that would be great to have correctly in an example, as otherwise people have to Just Know that kind of stuff in advance)
We can mention it in the comment in the example. I just think it would be annoying to duplicate the example for push and pull-style ๐ค
yeah that's why I use push in Ahoy ๐
bit less hassle
yeah comment totally works ๐
Opened: https://github.com/simgine/bevy_enhanced_input/issues/307
I'm a bit busy, but I think it's a good opportunity for new contributors to play with the crate and Bevy in general ๐
We need to make the following changes to our examples to better teach users: Write a proper character controller example instead of the current one. See https://bevy.org/examples/movement/physics-i...
that is an excellent idea, I also think those are really good as a first contribution 
@sharp warren FYI you did a little oops on 0.24: ActionState is correctly a deprecating alias to TriggerState, but it's not in the prelude, so updates don't point you in the correct direction
i.e. I got a compilation error, had to import the type now, compile again, and then I got the deprecation warning
That's on me, must have forgot a spot.
nono, you didn't forget a spot, you just need to remember to add the new alias to the prelude too! ๐
That makes sense.
I don't have enough experience with deprecation in libraries. If I remove the warning in the library by adding #[allow(deprecated)] above ActionState in the prelude will it also remove the warning for downstream users of the library?
it will not ๐
#[allow] is always just for your own linting, it does not get propagated
That's good to know.
Yep, I noticed it as well while migrating my game, but got a bit lazy to fix since RA suggests to import the dedicated type and after that it shows the deprecation.
But I can draft a new release with the fix
yeah nothing terrible ofc
Tsudico submitted the fix and I drafted a patch ๐
@inner sky I am trying to understand more about how you are doing input accumulation in bevy_ahoy regarding your NPCs/AIs.
Your BEI context is registered to the PreUpdate schedule so when you call mock_once 1 this inserts ActionMock, and then BEI will process that in PreUpdate and fire the observers in PreUpdate. Now, I know that this is effectively the same as when a player presses a binding (the point of it is to be a mock!), and input accumulation will run no matter if it's a physical binding-based action or a mocked on.
But what I am having trouble wrapping my head around is the fact that you now have some code running in a non-deterministic schedule for your AI (edit: right?). But couldn't you just have your AI system update your AccumulatedInput component directly and have it update it in a FixedUpdate system?
To be fair, I can't quite put my finger on what could go wrong by relying on your AI action being driven by PreUpdate, but your AI will never be in a situation that will miss inputs (because your code essentially sets them), and you've already proved that you can bypass BEI with the AccumulatedInput, hence: Can you just set it directly for AI?
Yep, totally
there's no non-determinism here IMO since PreUpdate is the next frame. There is a one frame delay here however. That is annoying when it happens to users, but I figured it doesn't matter for AI
But yeah it's entirely possible that this specific design would be more elegant by setting stuff directly, or having convenience methods on AccumulatedInput ๐
One reason I came across this is that I was really keen on using mocking because it allowed me to get the BEI machinery for free: I could just do stuff in BEI Complete observers. But once I realised those observers fire in PreUpdate I realised that they should never be used for game logic (also said in https://github.com/simgine/bevy_enhanced_input/issues/106 so this is not new information).
So then, I have all of these AI mock_once calls that ultimately make observers fire, and inside that where I currently do game logic I need to change that to instead do input accumulation.
But now we've come full circle. The AI should just set the input accumulation directly.
And also worth noting when you use an ActionMock things like conditions are disabled, so I'm now thinking maybe this round about way of AI setting their action state is not as useful as I thought.
yeah good point ๐
you're absolutely right
I should change that
thank for sharing your thoughts on this
I still don't have a concrete argument to say it's right or wrong, but I'm also thinking of pausing the game. I believe you can get far with a pause implementation by pausing FixedTime. 1) In the bevy_ahoy example your NPC update system is in Update so you can't easily pause that without using other state. 2) If you rely on ActionMocks/mock_once that will go in PreUpdate and you also now cannot rely on pausing those BEI events firing.
the npc system is in Update?!!?!??!
eek
that's wrong haha
should have checked that better when I reviewed it
No worries. Thanks for the input yourself. I'm going around in circles on this stuff but I think slowly retaining the context needed to get it right.
Thought I was on to something with using BEI observers all over the place against mocked actions, but I've got some rewriting to do I think.
BTW pausing FixedTime is a litmus test I've been using lately to try and work out if my design / game logic is right and that's what made me think of the AI/NPC thing leaking out into PreUpdate as unpausable.
(Assuming it's the right thinking and noting maybe it doesn't apply to every game).
If I want to insert a ContextActivity can I just spawn it anywhere for the effect to apply?
ContextActivity is a required component of your context. So you can just insert it on the context entity.
commands
.entity(context_entity)
.try_insert(ContextActivity::MyContext>::INACTIVE);
that's absolutely the right framing imo
any context entity?
The one on which you want to disable the context.
So then if all the contexts were spawned in the same bundle wouldn't I just need one entity to access all their activities?
Yes
is it possible to manually trigger an action without binding? eg I want a button clickable in ui with also an optional shortcut.
same applies for eg picking if you want to have action that's trigerred both my shortcut and clicking. do I add additional indirection instead?
I guess I should just add indirection.
Yep, you can do this using mocking
Hiya, I'm fairly new to this crate and I'm wondering what the best way is to program an orbiting camera using this? As in, when holding right click, mouse motion should spin the camera?
I tried using context switching, and while it works mostly great, there is an issue where any smoothing just abruptly cuts off if I let go of right click early as it switches off the context activity. Is there a better way to do this, by chance?
Okay, realised what I was actually looking for for this "right click and drag" behaviour was Chord. Still don't know how to get it to sort of carry the momentum with the smoothing thing, but that's whatever
Code below in case anyone else comes across this :]
fn setup(mut commands: Commands) {
info!("Spawning camera.");
commands.spawn((
Transform::from_xyz(5., 0., 0.).looking_at(Vec3::ZERO, Vec3::Y),
OrbitCamera::default(),
Actions::<OrbitCamera>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
let modifier = context
.spawn((
Action::<OrbitModifier>::new(),
bindings![MouseButton::Right],
))
.id();
context.spawn((
Action::<Orbit>::new(),
DeltaScale::default(),
SmoothNudge::new(16.),
Scale::splat(1.5),
Chord::single(modifier),
bindings![(Binding::mouse_motion(), Negate::all())],
));
context.spawn((
Action::<Zoom>::new(),
Scale::splat(0.2),
SmoothNudge::new(12.),
bindings![(Binding::mouse_wheel(), SwizzleAxis::YXZ)],
));
})),
));
}
You multiply by delta and later smooth it, but you most likely want to smooth it first.
Hello! Do you have any aspect of the original plugin that the crate won't be really inspired by?
Hi! Could you elaborate? I'm not sure I get the question.
I didn't like that modifiers weren't supported out of the box. And common things like WASD should be done by hand.
And, of course, I like the ECS-oriented API in BEI way more.
Do you have any existing solution to use bindings-bindings pair directly for Bidirectional? So it goes something like
let bindings_forward = bindings![KeyCode::KeyW, KeyCode::ArrowUp];
let bindings_back = bindings![KeyCode::KeyS, KeyCode::ArrowDown];
// ...
Bidirectional::new(bindings_forward, bindings_back)
(I may be sleepy, so I might have missed some in the documentation; My apologies in that case)
You need to use Bidirectional twice.
Looks good!
Regarding BEI, I assume you're busy this release cycle? If yes, it's fine to wait. Maybe I can make some BSN-related improvements after 0.19.
Yep, I wanna see what the bsn! macro does to our API here ๐
Particularly, I'm thinking about utilizing templates for presets
And curious if I can optimize how it can be defined inside BSN. Maybe we won't need actions! and bindings! ๐ค
That's my hope!
The fewer macros the better
Both for maintenance, and in terms of helping people identify the right mental model for what's going on
Agree! Do you think we might also remove children! in the future?
Maybe: we'll have to play with the exact traits in practice to see if that's viable IMO
Speaking of the guide, hypothetically (assuming BEI is upstreamed), if both features are enabled, would BEI provide, for example, EnhancedFreeCameraPlugin?
- If so, and the guide doesn't mention this, should we include a note about it (encouraging crate-to-crate compatible solution)?
- Who should provide it by the way?
#1459437342409359504 message
We would just incorporate action management as a required feature for dev tools
There's no need for anything more complex
Probably worth adding a bit of a note about how integration work should be done once upstreaming is complete
It could be bizarre especially more than two crates
How so?
i meant agreeing that, this would lead to for example discussion over XY XZ XYZ plugins for X, Y, and Z plugins
Generally that's pretty easy to resolve in practice ๐ thinking about user patterns makes it straightforward to decide that problems
I'd expect many things in Bevy to utilize BEI.
There is also picker, and shortcuts in UI that would benefit from actions.
I migrated a project from leafwing-input-manager to BEI this week. I'd hoped to find the repository that hosted #1297361733886677036 message to compare notes on my total blundering approach, NiseVoid, I think?
It's a private repository, unfortunately
No worries.
This crate made me want to have add_observers so that i can:
impl Plugin for FooPlugin {
fn build(&self, app: &mut App) {
app.add_observers((a,b,c,d,e,f,g,h,i,j,k));
}
}
Probably worth asking Bevy devs about it.
But I suspect they didn't add it to avoid increasing compile times.
For systems they added it to avoid making a look for each added system.
It'd be just my lazy ass. Hmm... If that's the case, I can't assert clearly. Aren't we given any compile time threshold (neither a formal methodology) for this particular section?
It'd be more efficient to check past relevant issues first.
It's just my assumption. Variadics with tuples usually hurt compile time.
What would be the best way to get the current highest ContextPriority value?
Only via linear search in a Query.
Or create observers that track context insertions and store this information in a resource.
Hi
I want to increment key binding by 1 instead of smooth movement forward
like snaped 1 step
forward
each key binding
instead of this behavior I want to step one square ahead
Do you want to keep the movement smooth, but snap it to the center?
snap in the center of each square on move
is this an option in the crate or should I make it ?
You can observe for Start or Complete events. This will give you only a single event per prace. And in your movement logic you snap the character to a direction
how does DeadZone works?
can i speed up the complete event Observer?
now it is working by use a Boolean and Complete event by it take time as you can see :
I am using a Res<Time> instead of complete event to tweak release move
Have you thought about exposing ContextInstances when a dev/debug feature is on?
?
Oh sorry! It's to the maintainers.
๐ sorry
It's similar to normalization
I'm not sure what you mean
Why you need it?
I don't need it, but it's nice to have. It improves my debugging.
Well, In that logic, i don't need to work on development tooling since i don't have such scale of game to dealt with tooling
lol
It has a lot of stuff that doesn't reflect nicely, so I'm not sure
it's ok. i can bottle it down
We can add reflect ignore for most fields ๐ค
If you open a PR, I'll accept it
Cool! I'll get back to it in a few days.
I mean when you push a button and release it it take too much time to get to complete event change the boolean and make the use push again. can we minimize this time?
Yes, I'd put this to the fields that can't be reflected.
Events are instant. If you want to react on press, use Start event instead of Complete.
ok
does Release::default() works with #[derive(InputAction)] #[action_output(Vec2)] pub(crate) struct Movement;
with boolean is fine
but with Vec2 not
You will need to configure the actuation.
It computed as length of the value (Vec2 in your case).
Is ActionMock now added by default to all Action entities?
Yes, it's a required component:
https://docs.rs/bevy_enhanced_input/latest/bevy_enhanced_input/action/struct.Action.html#impl-Component-for-Action<A>
Component that represents a user action.
Hi! I wanted to implement state-context integration in my project but I soon realized that it won't work properly with SubStates. I get a panic, probably because the observer that syncs the ContextActivity expects the state resource to exist, and this is not always true with SubStates. Is this something that will be supported in the future? I don't know how difficult to implement this could be, but I can always take a stab at it provided I get some help and it's something people would find useful. Thanks, and have a great day!
State-context integration for synchronizing Bevy States with input contexts.
Looks like we forgot to make the state access optional in state.rs. Could you open an issue in GitHub?
If you want to tackle it yourself, take a look at the system where the panic occurs. It should be a simple fix.
Sure. I'll take a look at the code later and if I can't figure it out I'll open an issue. Thanks for your reply ๐
Does it make sense to re-use contexts with different actions?
Like I want to be able to swap cameras (which also means swapping ContextActivity::<_>::INACTIVE/ACTIVE for inputs)
but right now its a bit of a pain with different context types
I guess actually I could just throw an ActiveCamera component on and observer change ContextActivity...
sorry accidentally rubber ducked here
Ok let me finish lol
I gave this a bit of thought after glancing at the code and I think there might be a case (maybe more than one) where the optional States resource can be None when sync_on_insert observer triggers. I'm thinking a sub-state whose source State was not yet set, but ActiveInStates was just inserted.
Let's say we have an entity with some bindings that gets spawned in a system running On(State::First)with an ActiveInStates<Context, SubState::SomeVariant> component. If said SubState source is State::Second it could trigger this issue, right?
Would this be concerning? We can always add some warning log if this happens. I haven't tested this though, as soon as I have some time I'll implement the change and verify if my hypotetical case even triggers. Wanted to think out loud as I might be overthinking. ๐
If I remember correctly, the substate is not present in the world when its main state is not set. And I think that's what causes the problem.
I think what aceeri said wasn't about debugging
Anyway, I reviewed the PR ๐
it wasn't. haha. It should help though.
If you open an issue on GitHub, I'll be able to ping the feature author.
When running the following line on a barebones project with DefaultPlugins and no changes to window, no errors show, the process starts and a tab is created on the taskbar, but the window itself is invisible, unless I remove the .finish(); Running on linux with Wayland
app.add_plugins(EnhancedInputPlugin).add_input_context::<Character>().finish();
Most likely wayland feature is not related.
I don't think you need finish(). I think that is automatically done as a step when the app is run() so you calling prior to that could be the reason for the issue. If you look at the Plugin documentation it explains it:
https://docs.rs/bevy/latest/bevy/prelude/trait.Plugin.html
A collection of Bevy app logic and configuration.
I seee ok I wondered whether it was necessary, thanks for confirming ๐
https://github.com/simgine/bevy_enhanced_input/issues/313 sorry for the delay!
just want to do it with the ignore attribute? no opaque? https://github.com/simgine/bevy_enhanced_input/pull/312#discussion_r2983806922
I don't understand the question. What opaque has to do with this?
i thought it was convenient. Let's iterate with ignore attribute
That's why we have this Discord server!
hmm how would you like to have this lifetime shortname for reflect?
Ah, it's not static?
lol i thought short_name.rs was under context/
Bueno? (2nd iteration)
#[derive(Debug)]
#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Default))]
pub struct ContextInstance {
pub(super) entity: Entity,
#[cfg(not(feature = "reflect"))]
pub(super) name: ShortName<'static>,
#[cfg(feature = "reflect")]
pub(super) name: String,
#[cfg_attr(feature = "reflect", reflect(ignore))]
type_id: TypeId,
#[cfg_attr(feature = "reflect", reflect(ignore))]
priority: usize,
#[cfg_attr(feature = "reflect", reflect(ignore))]
is_active: fn(&Self, &FilteredEntityRef) -> bool,
#[cfg_attr(feature = "reflect", reflect(ignore))]
actions: for<'a> fn(&Self, &'a FilteredEntityRef) -> Option<&'a [Entity]>,
#[cfg_attr(feature = "reflect", reflect(ignore))]
actions_mut: for<'a> fn(&Self, &'a mut FilteredEntityMut) -> Option<Mut<'a, [Entity]>>,
}
I'd prefer it to be ShortName. If you can't reflect it, looks like we need to skip it.
And I'd skip only things that can't be reflected, like function pointers.
And I'd remove Debug. It won't print nicely due to function pointers.
sรญ o no (3rd iteration)
#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Default))]
pub struct ContextInstance {
pub entity: Entity,
#[cfg_attr(feature = "reflect", reflect(ignore))]
pub name: ShortName<'static>,
#[cfg_attr(feature = "reflect", reflect(ignore))]
type_id: TypeId,
#[cfg_attr(feature = "reflect", reflect(ignore))]
priority: usize,
#[cfg_attr(feature = "reflect", reflect(ignore))]
is_active: fn(&Self, &FilteredEntityRef) -> bool,
#[cfg_attr(feature = "reflect", reflect(ignore))]
actions: for<'a> fn(&Self, &'a FilteredEntityRef) -> Option<&'a [Entity]>,
#[cfg_attr(feature = "reflect", reflect(ignore))]
actions_mut: for<'a> fn(&Self, &'a mut FilteredEntityMut) -> Option<Mut<'a, [Entity]>>,
}
Why reflect(Default)? The type doesn't have the Default impl.
#[cfg(feature = "reflect")]
impl Default for ContextInstance {
fn default() -> Self {
Self {
entity: Entity::PLACEHOLDER,
name: ShortName::of::<()>(),
type_id: TypeId::of::<()>(),
priority: 0,
is_active: |_, _| false,
actions: |_, _| None,
actions_mut: |_, _| None,
}
}
}
lol
But why add it?
I feel like you don't really understand what're doing ๐ค
That's why we work this out together. Enlighten me please.
btw I wanted to use it for my tooling (of that debugging pad): https://cdn.discordapp.com/attachments/1459437342409359504/1486139770429243513/uno.mp4?ex=69c7b6c4&is=69c66544&hm=aa1b1dbf526648515037137ad3cafec2ff7abe0db615f70a72d67c255f0aac63&
Oh did you ask why i add it? Sorry it was right before I slept I couldn't see it. I must push you little bit because I do care about your crate:
- I don't know exactly what you want (what have you said so far?).
- Compilation error leads to the approach.
- Of course, there are more ways to resolve but (if this is the case you imply) I cannot keep writing code and see if you like or not. I'm not your LLM, buddy! What have you specified so far? Scroll up please. Based on what, should I understand how deep what these are doing?
Yes, I asked why ๐
What I think we should do:
- Make the resource and the instance public.
- Since we now export them, worth adding reflect so users can see the context entity in the editor/inspector.
- Export getters for the name and the entity in the instance.
So can i still add the default?
Is this required by reflect? ๐ค
Of course ShortName is not required by reflect (required by you lol)
error[E0277]: the trait bound `bevy::prelude::ShortName<'_>: core::default::Default` is not satisfied
--> src/context/instance.rs:42:40
|
42 | #[cfg_attr(feature = "reflect", derive(Reflect))]
| ^^^^^^^ the trait `core::default::Default` is not implemented for `bevy::prelude::ShortName<'_>`
|
= note: this error originates in the derive macro `Reflect` (in Nightly builds, run with -Z macro-backtrace for more info)
Ah, I played with it locally and I afraid we can't derive reflect at all ๐ข
But without reflection I don't think the list of instances is useful at all. Users can just use query instead which provides the information in a much more readable way.
hmm... can you go to the disqualified and beg ask for the reflective ShortName?
It's not only that, looks like all fields need to have Default :(
And you don't like this: #1297361733886677036 message
It's not enough, it won't compile since fields don't have Default.
Ah so you want me to go deeper than that
No, I mean we can't add Reflect in a useful way.
And without reflect I don't think this struct is useful ๐ข
Drafted a patch release with a fix.
So would you like to take the first and the third bullet points for now?
I can leave some comment (for the second) if you like?
I mean that making the struct pub and providing access to information (such as name and entity) is not useful without reflect.
You can simply query all contexts already. Why export the instances list? I use it internally to sort them, but do you think it's useful to users?
I haven't thought so much
If you find a use case, I'm open exporting it. But right not it looks like something useful for BEI internals (you saw it stores pointers, ID, etc.).
we talked about its being public when a (new?) dev/debug feature is on
Yes, but what is the use case? You can iterate over instances with queries. It's faster.
I'm lazy to write a query just for a few takes in debugging Personally it's a debugging workflow. There's no use case with a tangible application.
Just to clarify: it's ok to expose things for easy debugging. I just don't think ContextInstances beats Query ๐
Well this makes me hard to argue haha.
Were you talking about it versus having a separate component independent from ContextInstance in debugging (so to query)?
Ah, I forgot all context instances are generic and you probably want to iterate over all of them, right?
Maybe you could do register_required_component<C, YourDebugMarker> for each C registered as context. To do this, you'll need to create your own register_context_dev. But I don't think it worth upstreaming since in BEI entities could have mutliple contexts.
ContextInstances is also generic over a schedule, so it wouldn't help you even if we expose it.
I'm lazy this would be another bulk in my debugging, proportional to the number of context while ContextInstances can always be iterated over finite in dev somewhat fixed number of schedule label, which still helps.
well in that case you can still suggest writing a macro for this or adding up many other things (observer, event, ... or you can suggest writing more test maybe). There are so many ways to achieve it on the user-side, versus having nothing but the mundane iteration over the exposed resource.
By the way, out of topic, can we imagine if we must do this for all third-party Bevy crates (which hasn't happened; maybe in the future ecosystem? ). If they phenomenize artificially encouraging everyone to wait for the beautiful full-reflection ecosystem, I won't wait for it (I like reflection though). I have to proceed.
Okay, let's make it pub and add getters to the ContextInstance for name and entity.
ContextInstances already derefs to a slice, so no getter for the instances field is needed.
Would that make sense? No need to put it under a feature or anything.
It should do it. I'll change the PR tomorrow. Thanks.
(nudges Shatur)
Reviewed!
@tardy scroll drafted a new release with the change.
Awesome!
@sharp warren I got a design question for how to best model the following commands:
I have some PlayerInputContext that's working great, but I now need to add some debugging controls. I'm thinking that holding Alt enters debug mode, which allows things like middle-clicking the thing you're looking at to reset its state
off the top of my head, this sounds like a DebugInputContext that should be added while Alt is held and removed after
is that the right approach?
or should it instead be actions on the regular context that are gated on holding the alt key?
note that the debug mode should stay in the final game, so modders have access to it
so I'm not worried about compiling it out or anything
followup question: even if I go the route of adding and removing a dedicated context, I'm still curious, what's the best way to say "fire this action when middleclicking, but only if Alt is also held"? A chord?
I'd do a separate context just for clarity ๐ค
You don't need a chord, the support for keyboard modifiers is built-in. Just do with_modifiers
aah neat
would you insert and remove it with Down and Release?
No, I'd keep it always active.
Just give it a priority. And add your Reset action with Alt+MiddleClick
how do you do priorities ๐
ContextPriority component
the reason I'm thinking of adding and removing it is because Alt would enable all debug actions
You you can even put it inside your existing context
You can do this, but you don't have to
I think not removing is easier
yeah I just want to hear your recommendation mainly ๐
You just create Alt+whatever for every debug actions
ok, I'll do that then
I probably wouldn't remove and insert.
do I need SpawnWith for with_modifiers?
I'm just confused where in here I'm supposed to add it
No, it's just MouseButton::Middle.with_modifiers(..)
oh it's with_mod_keys now I think?
Ah, yes
last question: which entity gets the ContextPriority?
The context entity
for that one I would need SpawnWith, right?
No, why?
hmm I'm misunderstanding
It's just a component on the context entity. Just insert alongside your actions!
Oh okay. Hmm, so right now I'm inserting both contexts on the Player
since they don't make sense outside the context of the player
that wouldn't fly with ContextPriority, right?
since then the context priority would be set equally for PlayerInputContext and DebugInputContext
ContextPriority is generic over context
No problem ๐
I'm too dumb to read today haha
It's ok ๐
I assume higher priority wins?
I got confused by this sentence:
Determines the evaluation order of the input context C on the entity.
which makes me feel like it's an order, i.e. 0 comes before 1
ah wait
1 overrides 0 then
okay yeah that makes sense
I've been doing a lot of 3D art lately, and there layers are always evaluated like layers of paint
i.e. the ones that come first in the list are painted on top of everything else
(since the layers are usually drawn top-to-bottom)
so I need to switch my mental model here
Maybe something to consider if one day BEI contexts are shown in-editor @eager aspen
Higher priority wins. It's already in the docs, but probably worth mentioning on the priority component as well
runs to the computer to check if that's why the input code was running weirdly ๐๐
Ooooh I'm not alone haha
@sharp warren I'm trying to bump BEI to bevy main for the Bavy jamthread 'main' (590280) panicked at /home/hhh/.cargo/git/checkouts/bevy-50d7e162b728c6c6/d06aa47/crates/bevy_ecs/src/query/state.rs:216:13:
error[B0001]: Query<', ', EntityMut<'>, With<ActionFns>> in system (bevy_ecs::system::commands::Commands<', '>, bevy_ecs::change_detection::params::Res<', bevy_enhanced_input::context::instance::ContextInstances<bevy_app::main_schedule::PreUpdate>>, bevy_ecs::system::query::Query<', ', bevy_ecs::world::entity_access::filtered::FilteredEntityRef<', '>, bevy_ecs::query::filter::Without<bevy_enhanced_input::action::fns::ActionFns>>, bevy_ecs::system::query::Query<', ', bevy_ecs::world::entity_access::entity_mut::EntityMut<'_>, bevy_ecs::query::filter::With<bevy_enhanced_input::action::fns::ActionFns>>) accesses component(s) ContextInstances<PreUpdate> in a way that conflicts with a previous system parameter. Consider using Without<T> to create disjoint Queries or merging conflicting Queries into a ParamSet.
@sharp warren I'm trying to bump BEI to bevy main for the Bavy jam and get this
thread 'main' (590280) panicked at /home/hhh/.cargo/git/checkouts/bevy-50d7e162b728c6c6/d06aa47/crates/bevy_ecs/src/query/state.rs:216:13:
error[B0001]: Query<'_, '_, EntityMut<'_>, With<ActionFns>> in system (bevy_ecs::system::commands::Commands<'_, '_>, bevy_ecs::change_detection::params::Res<'_, bevy_enhanced_input::context::instance::ContextInstances<bevy_app::main_schedule::PreUpdate>>, bevy_ecs::system::query::Query<'_, '_, bevy_ecs::world::entity_access::filtered::FilteredEntityRef<'_, '_>, bevy_ecs::query::filter::Without<bevy_enhanced_input::action::fns::ActionFns>>, bevy_ecs::system::query::Query<'_, '_, bevy_ecs::world::entity_access::entity_mut::EntityMut<'_>, bevy_ecs::query::filter::With<bevy_enhanced_input::action::fns::ActionFns>>) accesses component(s) ContextInstances<PreUpdate> in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`.
any idea where to look?
it appears when adding add_input_context
okay it's context::apply
so uuuh
what to do 
I don't know, depends on what changed on main.
oh it's a resource
I bet that's resources-as-entities
yep, slapping a Without<ContextInstances<S> on the actions works: mut actions: Query<EntityMut, (With<ActionFns>, Without<ContextInstances<S>>)>
Ah, makes sense
I've had a set of packages up to date since the jam
Could you PR it to https://github.com/simgine/bevy_enhanced_input/pull/305 ? ๐
yeah, can do
Hi all . I am using version 0.24.3. I would like the output value to go to (0.,0.) immediately when all keys are released. Current code below uses SmoothNudge which works OK. But I do not want nudging because a quick key tap moves the player a little too far for my liking. When I remove SmoothNudge there is no event coming in; and my player continues to move.
How do I implement an immediate stop for my character on key release?
Action::<MoveAction>::new(),
DeadZone::default(),
// Able to handle multiple keys down
bevy_enhanced_input::prelude::Down::new(0.01),
// This is needed to bring movement to a stop
SmoothNudge::new(16.0),
Bindings::spawn((Cardinal::arrows(), Axial::left_stick(),)),
You might need a custom modifier for this. Take a look at SmoothNudge and tweak it to your liking in your game code. Don't forget to register the new modifier.
I fixed my problem by changing the system that processes the output value. Which is fine - and sensible in a way. My new solution now depends on nudge to bring down the speed gradually.
Yeah, this also makes sense.
Hi is it possible to have Key instead of KeyCode in the keyboard bindings? Having trouble with implementing hotkeys compatable with different language kbs
No, Key will be different on different keyboard layouts. You most likely want to use KeyCode.
KeyCode::KeyZ is Z in english, but Y on german keyboards
KeyCode::KeyZ is a key location, it's not language specific
So they reach Ctrl + Z even on QWERTZ?
In that case we might need to allow Key ๐ค
Yup, one of my testers is swiss and encountered this issue. they expected ctrl z to undo but instead it does redo
Could you open an issue? Shouldn't be hard to implement. We just need to add an additional variant.
yup
Thank you!
If you have some free time, a PR would be welcome ๐
yup working on it
oh key also has undo redo
undo keycode is cursed
will need to test if I can just use that
I think it's dedicated undo / redo buttons.
Maybe instead of having a whole separate Binding variant we could have a special enum for Binding::key field with a From impl
Sun USB keyboard what the fuck, was USB even around when Sun made keyboards
xddd
xdd
man software is weird
but we can't convert Key to KeyCode?
we would need to store and check Key separately either way I think
they're tracked in diff bevy resources too
having issues w this impl because
Binding and KeyCode is Copy but Key is sadly not, so would need wrapping struct
oh wait enum field would fix both true
Yes, but we can have an enum that holds either Key or KeyCode
yup working on that rn
but
key does not impl copy :(
prolly cuz it does some string stuff?
Hm... Probably. It would be unfortunately to not have Copy ๐ค
Not sure what to suggest at the moment.
You can try to remove the Copy for now.
yup
is ignored() used only for AnyKey?
also removing copy is an absolute nightmare xd
It's used for input consumption. Some action may consume inputs, preventing others from firing
Yeah, I can imagine ๐
worried about properly ignoring keycode and key
@tame umbra have you considered converting Key to KeyCode somehow?
yeah that's what I looked into first
but they seem very separate
otherwise I wouldn't need any changes
I'd expect it to be possible to get KeyCode from Key if the keyboard type is known ๐ค
Yeah, but things like Character('Z') should be mappable.
I mean this could even be possible to do outside of BEI
maybe
But on second thought, I think something like this should be baked in BEI ๐ค
Because I think it's quite common
bevy has separate resources for processing both
not sure why they would do that if they could just map it
up to you I guess, I'll hold off on the copy stuff for now
well
it doesnt work but its what im working on
I was just thinking out loud. I don't see any option other than support Key
And store BindingKey (the enum you added) inside ignored cache as well.
hmm alright, are we de-copying Binding?
Yep
oh yeah i still have compile issues
mostly just cloned my way thru it but copy should be removed now
This is strange
it's what happened last time too xd
yup i saw
also worried about if i handled ignored_keys correctly, especially in the context of AnyKey
A bit sad that we need to remove Copy, but in most cases it will be trivially clonable, so it's fine
Looks correct to me
Both key and key codes should to be checked for any.
yup
Once CI passed, I'll do full review. Also would be nice to hear @outer wyvern opinion
Ah, looks like she upvoted the issue
Should be good to go
maybe ill just require players to get a sun usb keyboard to undo
Link?
Just saw the message, but looks like you already replied ๐
Mhmm!
Hi folks ! BEI has been amazing so far, however I am confused about context layering , I thought I had made a mistake, but running the example in the BEI repo has the same "issue":
ie
- when pressing Enter, the context switches from
DrivingtoPlayerand immediatly back again toDriving, which is not what I would have expected. (I thoughtrequire_resetwas supposed to prevent exactly this) - however If I I add
consume_input: true,to theExitCaraction's settings , then it behaves like I would expect : ie
(
Action::<ExitCar>::new(),
ActionSettings {
// We set `require_reset` to `true` because `EnterCar` action uses the same input,
// and we want it to be triggerable only after the button is released.
require_reset: true,
consume_input: true,
),
So am I missing something obvious ? it this the intended way to deal with context layering ? or is this a bug ?
Thanks ! ๐
Ah, we switched the default for consume_input from true to false, which break the example ๐ข
So yeah, you need to add consume_input: true to ActionSettings for the ExitCar.
Could you open a PR?
No problem ! Do you mean a PR just for the example(s) ?
Yep
No problem ๐ I'll do that later this afternoon
Working with bevy_picking for my game and I feel like we'll need to heavily change it after BEI upstreaming.
Either use a BEI context for it or entirely split casting logic from events ๐ค
Doesn't it have the ability to be disabled? Seems like having it enabled within a BEI context probably would require the least modification.
Picking doesn't allow me to configure ordering/consumption and bindings are hardcoded.
Ouch, I was investigating "re-binding" the picking logic using BEI, but other than re-inplementing some built-ins/ hacking around.I had not found a way to override things like the on-press & co but thought it was due to being new to Bei...
. So if I understand correctly you are confirming that there is currently no graceful solution?
For UI I use this glue to create button shortcuts:
https://github.com/simgine/simgine/blob/master/ui/src/widget/button/action.rs
And in the code I react on Activate, which triggered on both - clicks and hotkey presses.
For picking with physics I think I'll write a tiny abstraction over SpatialQuery to cast things from cursor.
In my game a lot of things are context-dependent. So I use picking only for UI clicks.
@outer wyvern I need your call on https://github.com/simgine/bevy_enhanced_input/pull/320
To put it short:
- We wanted to support
Keyto have shortcuts likeCtrl + Zto be location dependent. - But
Keydoesn't implementCopy, so this introduces a clone hell.
My suggestion is to support only char instead of the whole Key. This would solve the problem without giving up Copy on Binding.
I don't think users will ever need the full Key support in actions. What do you think?
Why can't we just work upstream and make Key Copy?
Because it supports weird things that require SmolStr:
https://docs.rs/bevy/latest/bevy/input/keyboard/enum.Key.html
The logical key code of a KeyboardInput.
Hmm ๐ค
So we would have KeyCode (a physical position on a keyboard) and something like KeyChar (the semantic meaning of a single keystroke)?
Are there applications for this beyond the modifier key problem?
Because we already have a solution to that IIRC
Yep
Can't think of any except Ctrl + Z, so on QWERTZ keyboard it will be a different location
Why is Ctrl+Z special? Or is it?
I think because it's mnemonic. People used to Ctrl + Z. So on QWERTZ it's still Ctrl + Z, but the location is different.
Maybe for other shortcuts like this it could also make sense
Do other shortcuts work this way?
Ah, the problem in Ctrl + Z that you're talking about here is that the Z moves around, not that you should be able to move either left or right control
Yes!
Okay, I think I understand the problem more clearly ๐
(This is going to need a ton of docs about best practices)
Yes, using the scancode values for UI-flavored hotkeys makes sense to me
We should support this
Using a single char alone doesn't seem sufficient though
Things like Home are important to be keybindable
And don't map to this
I feel like we need a restricted version of https://docs.rs/bevy/latest/bevy/input/keyboard/enum.Key.html, where everything is the same except:
- Character stores a
char Unidentifiedis removedDeadis removed
We don't need to support complex Chinese characters or emoji as action-bindable variants
As funny as that would be
Key itself is only useful for text input
(what a mess...)
But KeyCode already include Home
Oh hmm!
And all the F keys ๐ค
Okay, I think I'm down!
Yeah, I think KeyCode includes everything needed, just for some cases we want only characters to be keyboard related.
Alright, I'm on board
Wait, Character says:
A key string that corresponds to the character typed by the user, taking into account the userโs current locale setting, and any system-level keyboard mapping overrides that are in effect.
Which meansZwon't work with different locales, right? Like with somethin cyrilic.
If they can type "Z" it should work
I still think this is the right choice to support, and should be the default for things like editor hotkeys
Cyrilic doesn't have Z. I need to switch to English for it.
If this works how I think it works - it won't be good.
Vim has the same problem: keyboard shortcuts based on logical keys, which might not work when you switch layout.
@tame umbra could you try adding any cyrilic layout to the system and see if Ctrl + Z works with your solution?
Like add Ukranian or Russian layout.
they use Ctrl ั?
No, the location should be Ctrl + Z, just like on QWERTY
Well, the button is "ั". But it should work with Z
Yep. So you want to bind Ctrl + Z, but it should work as Ctrl + ั on Ukranian layout and as Ctrl + Z on QWERTZ keyboard.
If the cyrilic option doesn't work - it's definitely not what you want.
You just want consistent behavior based on keyboard type. The keyboard layout (i.e. language) shouldn't matter.
I'd also suggest to check if Key::Character('Z') works with cyrilic. If it works - great.
hmm my pr seems to break things pretty badly
im going to just check w the existing bevy resource
same "ctrl z" position in 3 different languages
so because the ukraine one is in the same position, it has the same Keycode and Keycode would work, but Key would not, however, in the german keyboard Keycode would not work, but Key would.
as it KeyZ is Character Y and germans use Character Z to undo
for my personal game i might just bind < and > tbh xd
but not sure how you would approach it here
So this confirms my theory ๐ข
While binding via Key works for QWERTZ, it breaks for Cyrillic layout.
I.e. I think it's not the right approach to solve the problem you're having with Ctrl + Z.
yeah im building a game so i can just rebind it nbd
but for productivity apps i could see this being a headache
@tame umbra On second thought, users could technically bind to both Key::Character('Z') and KeyCode::Z. Would you mind trying this?
I mean Ctrl + those keys
that would make german ctrl z undo and redo at the same time maybe
which would be weird?
Right, not a good solution ๐ข
The more I hear about this the more I think this sounds more like it might be a localization issue than an issue with the binding API.
It's just how winit exposes things.
KeyCode is a logical key and doesn't depend on the keyboard layout.
Key depends on the layout, but it also depends on the locale :(
The issue seems to stem from German keyboards switching the placement of Y and Z while still using Ctrl-Z for undo. It is unique to a locale based situation. That is why it seems to me that the solution is to alter the bindings based on the locale would be the proper choice for this case and isn't necessarily a good use case for altering the API.
As for altering the API to support Key as a binding, I'm not necessarily opposed. I honestly don't know enough about how winit works to determine the pros and cons so would have to look into it more. I think I'd need to see a use case that doesn't have such a specific scenario surrounding it.
The problem with Key is that it breaks for other layouts.
So we something like "if Key matches, use it, but also try KeyCode"
This is why I suggested to try to map both
I think this is where my confusion lies. Key breaks for different layouts so the preference, at least for most games, is to use KeyCode which has consistent placement across layouts. Is there a reason other than localization where using Key provides benefits?
If there is a way for the user to bind inputs then it seems better to take the inputs as KeyCodes directly. So that seems to leave possible developer scenarios. I'm trying to determine a non-localization use case for Key but having trouble with that.
If there isn't a use case that lies outside of localization then it seems to me that it is a localization issue and not an API issue.
possibly typing?
Yes, I think it's only for localization.
But exposing it as a solution is a bad because it doesn't solve the problem.
Yeah, I understand it is still an issue. But if we know it is only for localization we could see if there is a solution that lies in that space. Unfortunately, what that might be eludes me.
A brief thought I had was just using separate bindings and swapping based on locale but I'm not sure if Bevy, winit, or any other underlying library provides the OS locale. I'm not sure how easily we can do partial or complete binding swaps but that would be useful for other cases as well.
@sharp warren is BEI (or even bevy_input) aware of MIDI controllers?
I'm not sure. I don't know how bevy_input represents it.
Looks like bevy_input knows nothing about that
So I'm thinking of reading MIDI messages in PreUpdate and translating them to BEI actions
We have an issue about supporting custom inputs, but I didn't implement it yet.
But yeah, you can also just mock them.
Exactly
I
mocking API
bindings aren't hardcoded, you can stick anything on the input side of picking
that's how you can make e.g. virtual pointers driven by software instead of any sort of hardware
it's pretty well documented: https://docs.rs/bevy/latest/bevy/picking/index.html#the-picking-pipeline
Because pointer positions and presses are driven by these events, you can use them to mock inputs for testing.
@sharp warren we are discussing using BEI for keybindings of addons for Jackdaw
I just noticed there is add_input_context, but not remove_input_context
if every addon uses its own input context, that would mean a memory leak when an addon is unloaded at runtime (e.g. when it was hot-reloaded or disabled by the user)
Is that an API hole, or the wrong approach?
should we use a single input context for all of jackdaw instead, and have user-written addons add their actions to that global context?
Just an API hole IMO
Ah, makes sense. So it's possible to disable the built-in input system and manually hook BEI into it.
But if we upstream BEI, it would be nice to use the BEI context system for driving input.
Yup totally, was driving by just to let you know the input part of the pipeline is designed to be pluggable. We should def change the default to use BEI instead of HW directly though.
How hot reloading works? It's a full shared library loading unloading? I.e. the type actually changes on hot-reload?
Yeah Something like
- tell the old dynamic lib to unregister plz
- load in the new one
- tell it to register itself
Cc @eager aspen
fyi, itโs not โhot reloadingโ itโs just registering/unregistering the plugin - effectively a on/off switch for each plugin
Itโs not dynamic lib loading
But we obviously donโt want that extension to continue listening for inputs if itโs been disabled
Ie, the extension API supports custom keybind overrides within BEI
for context - i have a custom extension here in jackdaw i added, which prints to the log when a user presses โF9โ
How Bevy messages work with this approach? ๐ค
Can you unregister a message?
Not yet*
IIRC that inserts a resource and adds some systems, both of which are reversible
Now that we have system removal, at least
Should probably also add a remove_message upstream for convenience
What about required components? BEI registers some of them.
Same with reflection.
I don't see the issue with required components?
It's not like we want to remove all of BEI at runtime
just unload a specific action context and load a new one
BEI components are generic over the context. This is what happens when you register a context.
oh I see what you mean now
If there is no hot library reloading yet, I'd just despawn the context and don't bother unregistering it.
makes sense
@outer wyvern can I get your approval on https://github.com/simgine/bevy_enhanced_input/pull/322 ?
Done ๐
Hi, I'm struggling a bit with understanding how to modals with BEI ๐ I use the same key to spawn and dismiss a modal and thought I could use On<Start<input>> to do both. But, it seems that if the key is already pressed when the entity is spawned, Start fires then. So I get in an infinite loop.I switched to Complete but, then I needed an up/down axis to move between options and reorder them, and On<Complete<axis>> doesn't give me a direction - always returns Vec2(0.0,0.0) because (I guess) that is the actual current value of the axis. And anyway, I really would prefer Start and be consistent between different inputs.
No problem I thought! I'll just have a permanent ModalController entity that already exists when the key is pressed, and can deal with the input. But then I have to add the context components to the ModalController and not to the modal, which needs more queries and ties code together that I'd rather be separated.
I feel like I'm just not using this system the way it's intended. Can anyone give me some advice?
Maybe consume_input which i see referenced above? But I guess that might have the same problem on not being consumed when the entity with the action doesn't exist yet.
You may need to enable require_reset in your ActionSettings so that it only can reactivate after the key is released.
Ok, that looks promising
aha i finally found the context switch example...
I think the trouble was I was for some reason stuck to the idea this would be something that contexts were useful for, but it isn't
I guess require_reset works ok with sticks and dead zones ? I can't test that atm
anyway much better, thank you
An example for modals would be really helpful if you're up for a PR ๐ This is something that we should teach directly
Well I'm not sure what the best approach is tbh. My game has an unholy mixture of entities and resources because I have been slowly learning and updating since 0.8, so I am not a trustworthy Bevy developer
I would say though my expectation of Start<input> was that it would detect the input as having actually just started (i.e. with require_reset built in) Something like First<input> would fit the behaviour better (although this is perhaps bikeshedding), or maybe a note in the docs of Start would be enough
I'm happier putting in docs PRs than code ones ๐
Similarly Complete providing a zero value confused me for a while though I guess it is logical.
As mentioned in the docs, the event depends on on the assigned conditions.
Events just represent state transitions.
Yes and that seems like a good architectural choice. I'm coming at it from the top down though and would find that kind of note useful.
Did you read the quick start guide?
It was good! I got started quickly. It was only the behaviour on spawn tripped me up and I didn't think of looking in ActionSettings.
90% of the work was done by then ๐
Maybe worth metioning in the guide ๐ค
@inner sky what do you think about upstreaming DidFixedUpdateRunThisFrame (maybe under a different name) into Bevy?
yeah I use it all the time
in fact, it's even in one of the examples
Handles input, physics, and rendering in an industry-standard way by using a fixed timestep
I had to use it there as well
one of the review comments even was "this could be upstream"
I just didn't bother haha
Great, asking because of https://github.com/simgine/bevy_enhanced_input/pull/323
Could you also take a look at the PR?
I'm in the middle of something, but I can later, sure ๐
oh I just now realized we don't have that in BEI yet
I thought that's what you were referencing
but you actually meant this, eh? Oh or maybe I have it in Ahoy and you saw it there
well I see why you pinged me about it haha
Yep, I think it would be nice to upstream this resource ๐ค
I too have copied this resource into my game ๐ฌ
is it the best way to unpack Vec2 input from Query<&Action<MyVec2Input>>? its the one that works, but I doubt its the best
let movement: Vec2 = (*(*movement)).into();
One more thing - I want to query Action<MyVec2Input> for one of the entities that have the context with this action, how do I do that? for the sake of example - I want to get movement vec2 on custon event observer and use observer entity to query Action. I verified that target entity I get to observer is actually the one with the context
Just deref the value.
If you have multiple contexts with this action, you query for the context first to get the list of actions and later get the action from the list using a 2nd query.
@sharp warren I looked at https://github.com/simgine/bevy_enhanced_input/pull/323/changes and while it looks correct, it will not actually behave as expected once you pull in e.g. avian
since avian runs in FixedPostUpdate, I would not run any input application there, otherwise you will at random be either on time or one frame behind, or change the physics stuff while Avian is processing it
sure you can do .after(PhysicsSystems::Last), but IMO it's safer to apply the inputs to FixedPreUpdate
Since you will also run your own game's logic in FixedUpdate, applying the input in FixedPostUpdate will guarantee that your own logic sees them one frame delayed
which doesn't matter at high FPS, but is definitely noticeable on low-end machines
want me to do a PR for that?
It would be great ๐
Hi everyone, I am having issues with consume_input setting. I have 2 different input contexts. One for navigation and one for widget control. They have Actions with same bind. Widget action has consume_input set to true and it is evaluated before navigation. For some reason if Actions have Press condition than both contexts fire events. But if Actions have Pulse or Down condition than input consumed as expected. Is it intended behaviour for Press condition?
Input consumed only when an action returns non-None for it. And since Press fires only once, on 1 frame one action will fire and on 2 frame the second action will fire.
Hi there! I'm trying to implement a cooldown for a complete 3-action Combo (basically I want to trigger the cooldown on the last action, preventing the first one from being triggered), but I'm not sure how to properly do it. This is a (probably incorrect) code snippet of roughly what I'd like to have:
let combo_timeout = 0.5;
let hack = context
.spawn((
Action::<Hack>::new(),
BlockBy::single(other_action),
Press::default(),
bindings![...],
))
.id();
let combo_step = ComboStep::new(hack).with_timeout(combo_timeout);
context.spawn((
Action::<Slash>::new(),
Combo::default().with_step(combo_step).with_step(combo_step),
));
context.spawn((
Action::<Slam>::new(),
Combo::default()
.with_step(combo_step)
.with_step(combo_step)
.with_step(combo_step),
));
Should I go for some custom logic? I was wondering if there's a solution using Cooldown but I tried some combinations of it and it doesn't work (the combo just keeps getting blocked at one point when using it).
Not sure if I can help by just looking at the code, but you can try to enable trace level logging and see what is happening under the hood. The messages should be easy to read, you don't need to fully understand internals in order to debug it.
Ok I'll try that, thanks. Yet I was just thinking if I could use action mocking to temporarily disable the initial action when the last one in the combo fires. Does this sound reasonable?
Sounds a bit tricky ๐ค
I'd either try to figure out why it doesn't work, or go with hand-written logic.
Another option is to create a custom condition.
Thanks, I'll try some stuff and see what works!
@inner sky could you take a look at https://github.com/simgine/bevy_enhanced_input/pull/327 ? The explanation makes sense, but I want to hear your opinion.
I remember you were mentioning that it would be better to put input handling in FixedPreUpdate ๐ค
yeah this binds gamepad handling to the tickrate
while only the gameplay logic should be
also this PR will break catch up
if multiple fixed updates happen in one frame, but you clear the inputs in the fixed updates themselves, the second fixed update that frame won't have any input
so e.g. if you have a game where you need to hold space to jump higher, you will randomly jump short since the input was interrupted
or you will slow down for one tick, since only the first tick that frame has the W key held
PR should be closed imo
I can take some time this weekend to fix the input handling in the examples
and add some comments why the things are done a certain way
as you can see, input handling in Bevy still has quite a few footguns :/
Thanks for the input!
wish all of this happened in the background without you noticing
Yeah, I wish we have a crate for character controller ๐ค
ahoy does all of this for you, but yeah it's not exactly "shareable" code
as in it's tied to ahoy itself
I just wanted to say that it's great to have ahoy ๐
And yeah, I wish it was simpler
hehe thx 
@inner sky sorry for bothering, but could you take a look at https://github.com/simgine/bevy_enhanced_input/pull/329 ? It's a new PR ๐
looks almost perfect ๐
the scheduling is not entirely realistic: applying the input would usually be done in FixedUpdate, and the physics in FixedPostUpdate
but that can be a followup
I'd merge it as-is
Could you suggest it in the PR? I'd probably do it at once since it's a minor change.
sure, sec
Thank you!
done
can i get a sanity check on this pr for jackdaw?
is there a better way to disable all input contexts (such as when typing, where you don't want actions to do anything) when there could be any arbitrary number of contexts from extensions than what i did?
tldr: i made a wrapper for registering input contexts which also sets up systems to disable them via ContextActivity::INACTIVE when a global resource is changed. i couldn't just add it without a wrapper because ContextActivity is generic over the context type
You might want to look into ActionSources.
oh it's so much easier than whatever i was doing haha can't believe i didn't find it earlier
Will draft a new release for 0.18 with accumulated changes and 0.19 RC after custom input support PR:
https://github.com/simgine/bevy_enhanced_input/pull/335
The PR looks good now. @outer wyvern can I get your approval?
Does BEI make sense for "global" actions that aren't really associated with a specific entity? Should I just spawn an empty entity with actions!?
Just create a context, call it "GlobalContext" and attach to an entity.
Resources will be entities in 0.19
Ok, that makes sense
Oh, does that mean we can use resources for BEI in 0.19?
Possibly ๐ค
But we'll probably need to adjust filters since resource entities excluded from queries by default.
Sounds promising!
Lgtm
That behavior has changed fyi ๐
Ah, by any chance you have a link?
On vacation!
Sorry!
Random question- As part of upstreaming, are there any plans to make the "pull" workflow more ergonomic? Having to have a separate systemparam for each input and an iter_many(children).next() unless you're using Single feels pretty far from the ergonomics I've seen in other engines.
You don't need a separate system param for each action, they can be in a single query.
About iter_many - this is something Bevy should improve, I don't think I can do much about it.
Oh yeah I guess this would be relationship queries, which I think I've said on the github issue for it is probably one if the biggest ergonomic speedbumps in the engine
I thought each Action exists on a separate Entity? How would you go about using a single Query for multiple actions? Option?
@sharp warren Something I have been wanting to discuss is a bridge between BEI and bevy_settings - so that keyboard shortcuts can be saved as user preferences.
- This will need to be strictly opt-in, since many games won't want this feature (or may not have settings at all)
- bevy_settings currently requires that settings be stored as a resource, while BEI stores its key bindings in components, so it's a good bet that we would need to copy / sync between the two - saving directly from components is not possible
- Copy / sync is probably a good idea anyway, because the way that shortcuts are grouped and organized in the settings file (and in the settings ui) might not be the same way as they are grouped and organized in the entities that they are attached to.
- To avoid writing a lot of complex code, we need some way to declaratively map between these different contexts.
- For games with large numbers of shortcuts, we'll probably want to group them into different sections in the settings TOML file, which can either be done via multiple resources, or by a single resource with multiple submaps
Yes, they're are on separate entities. But you can just query for ActionValue in a single query.
If you want typed values, you need multiple queries, yes.
We have a keybinding_menu which showcases how to serialize settings. It's done via reflection. That's the approach I use in my game.
So yeah, we basically copy from a resource.
Just set this up in my game, was easy to get going! (on bevy main)
Hey man, this crate is an absolute godsend. It's very easy to use and I had a blast adapting my project to it the moment I discovered it, made everything so much better (and ready for multiplayer too!).
I actually have a question, suppose I have some Player entity with a very simple action child:
actions!(GameInput[
(Action::<PrimaryFire>::new(), bindings![MouseButton::Left]),
...
])
Is there any way that I can trigger the Start<PrimaryFire> event manually within a system? Like a commands.trigger but for the start of an action without pressing MouseButton::Left?
https://docs.rs/bevy_enhanced_input/latest/bevy_enhanced_input/action/mock/struct.ActionMock.html
I think this is the mechanism i am aware of. not super in depth with it myself.
Mocks the state and value of Action<C> for a specified span.
Thanks!
Looks like it suffices to insert it into the action entity and it fires on its own, so that's going to make it simpler
Thanks, I'm glad to read it ๐
The previosly linked mocking should be what you want.
Hi all. I recently discovered what seems like an oversight. I am binding an action to Binding::mouse_wheel and it's much more sensitive -- i.e. yields larger deltas -- on macOS than on Linux/X11.
I tracked this down to the unhandled MouseWheel.unit: MouseScrollUnit field. macOS uses MouseScrollUnit::Pixels and Linux uses MouseScrollUnit::Lines.
I found that "mouse wheel" events generated via trackpad gesture on macOS generates the Pixels variant while using an actual mouse yields the Lines one. Windows generates Lines with a mouse too. I see the projection_zoom Bevy example has the same issue with much larger scrolls using a trackpad.
Should this disparity be addressed in BEI, i.e. with a conversion via MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR (for now)?
Ah, yeah, I wanted to do so, but forgot ๐
Could you PR it?
Sure! I assume we want to use the Line -> Pixels conversion, so only trackpad users are "broken" (in case clients are already working around this)?
Why trackpad users will be broken? ๐ค
But yeah, I planned to add the conversion via MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR.
Why trackpad users will be broken? ๐ค
Well, if someone noticed this behavior and didn't know about the fix ๐
Ah, okay ๐
Is there a way to delay the rapid fire of an action, similar to text input boxes where you'd need to hold down your key for a bit before you can spam a letter?
Try adding the Hold component on your Action entity.
I can't find the Hold component, do you mean Pulse?
I tried that, but it's not quite what I wanted. The behavior you have with text input boxes is something like this:
the key gets held down -> behavior immediately triggered -> nothing happens for 1-2 seconds -> rapid fire starts
It's at https://docs.rs/bevy_enhanced_input/0.25.0/bevy_enhanced_input/condition/hold/struct.Hold.html (available from the prelude).
Returns TriggerState::Ongoing when the input becomes actuated and TriggerState::Fired when input remained actuated for the defined hold time.
It looks like the one_shot: false in Hold would satisfy the repetition.
Hmm, I tried playing around with this, but it seems that you'd need two observers to make it work. One for On<Start<T>> so that the action gets immediately triggered a single time on first press, and On<Fire<T>> so that it gets triggered again when rapid fire starts
Is there a way to do this with a single observer?
There is also Pulse
In my own usage, I tend to make a system for this that does stock ECS queries and tests flags in ActionEvents rather than using Observers (mainly because I migrated from leafwing-input-manager). But it will let you handle all the event variants in one system.
Isn't Pulse basically just for rate-limiting the rapid firing?
What I need is the same behavior in native text inputs.
When you press down the key you immediately get a single one-time response. But you need to continue holding it for a few seconds before the action starts rapidly firing.
The pulse should be able to do exactly what're describing
You'll need to set the initial delay
Thank you! I think this is what I needed. Gonna play around with it some more.
There is also an option to write your own custom condition ๐ See existing conditions for examples. You can write them directly inside your own crate. Just don't forget to register them in the app.
Thanks. Btw, what is actuation supposed to be? I'm guessing that this is feature only in analog controllers?
Yep, it's for analog input.
Hey, I'm doing a bit of a weird stuff with inputs, but I want to ask a question. I have the following input stream:
ButtonInput { pressed: {}, just_pressed: {}, just_released: {} }
ButtonInput { pressed: {KeyA}, just_pressed: {KeyA, ControlLeft}, just_released: {ControlLeft} }
ButtonInput { pressed: {}, just_pressed: {}, just_released: {KeyA} }
I intended this to trigger KeyCode::KeyA.with_mod_keys(ModKeys::Control), but it's currently not working.
I'm guessing that the funky way that ControlLeft is in both just_pressed and just_released is messing with the detection.