-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Systems are the answer! #6
Conversation
ME, on the phone responding to I haven't confirmed everything yet, but I think the concept of this PR is very good. However, I have the following two concerns
I feel that the key to this PR is whether this is an issue that can be ignored. |
Hey Elm, Very good point. I hadn't considered that. It'd be nice if there was some measurements we could make. At worst the deferred commands are executed before the next frame. I suppose there are a couple alternatives:
I prefer solution 0. But solution 1 might be useful: I'd suggest we could take approach 1 and make it optional. Add something to the plugin like The downside of solutions 1 and 2 are they in some sense reduce parallelized performance of the app overall to provide delivery guarantees. Let me know what's best. -Shane |
oh! I missed that I think solution 1 is necessary when the sending and receiving systems must operate on the same schedule. For Instance, in the code below, Exampleuse bevy::prelude::*;
use bevy_input_sequence::prelude::*;
#[derive(Clone, Debug)]
enum Direction {
Clockwise,
CounterClockwise,
}
#[derive(Event, Clone, Debug)]
struct MyEvent(Direction);
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(InputSequencePlugin::default()
.run_in(Update) // run on `Update`
)
.add_event::<MyEvent>()
.add_systems(Startup, setup)
.add_systems(Update, event_listener) // run on `Update`
.run();
}
#[rustfmt::skip]
fn setup(mut commands: Commands) {
// Specify key codes directly.
commands.add(
KeySequence::new(
action::send_event(MyEvent(Direction::Clockwise)),
[KeyCode::KeyW,
KeyCode::KeyD,
KeyCode::KeyS,
KeyCode::KeyA],
)
.time_limit(Duration::from_secs(1)),
);
// Use keyseq! macro.
commands.add(
KeySequence::new(
action::send_event(MyEvent(Direction::CounterClockwise)),
keyseq!(W A S D),
)
.time_limit(Duration::from_secs(1)),
);
println!("Press W D S A to emit clockwise event.");
println!("Press W A S D to emit counter clockwise event.");
}
fn event_listener(mut er: EventReader<MyEvent>) {
for e in er.read() {
println!("{e:?} emitted.");
}
} However, I don't think solution 1 is necessary because I can't think of a situation where it is absolutely necessary to run on the same schedule. |
One more point, since For example, in the code below, we can cancel only a specific sequence by attaching a marker component along with Example// Marker Component
#[derive(Component)]
struct KonamiCommand;
#[rustfmt::skip]
fn setup(mut commands: Commands) {
commands.spawn((
KonamiCommand,
KeySequence::new(
action::send_event(MyEvent(Direction::CounterClockwise)),
keyseq!(ArrowUp ArrowUp ArrowDown ArrowDown ArrowLeft ArrowRight ArrowLeft ArrowRight B A),
)
.time_limit(Duration::from_secs(1))
));
println!("Press W D S A to emit clockwise event.");
println!("Press W A S D to emit counter clockwise event.");
}
fn event_listener(
mut commands: Commands,
sequences: Query<Entity, With<KonamiCommand>>
) {
for entity in sequences.iter() {
// cancel
commands.entity(entity).despawn();
}
} However, would it require a slightly more extensive modification to solve this? proposed amendmentmod user {
use bevy::prelude::Commands;
use crate::input_sequence::lib::Sequence;
fn spawn_sequence(
mut commands: Commands
) {
commands.spawn(Sequence::new(
action::send_event(MyEvent(Direction::CounterClockwise)),
keyseq!(ArrowUp ArrowUp ArrowDown ArrowDown ArrowLeft ArrowRight ArrowLeft ArrowRight B A),
));
}
}
mod lib {
use bevy::prelude::{Added, Commands, Component, Entity, Query};
use crate::input_sequence::InputSequenceBuilder;
#[derive(Component)]
pub struct Sequence(Option<InputSequenceBuilder>);
#[derive(Component)]
struct SequenceInner(InputSequence);
fn initialize(
mut commands: Commands,
mut sequences: Query<(Entity, &mut Sequence), Added<Sequence>>
){
for (entity, mut sequence) in sequences.iter(){
if let Some(builder) = sequence.0.take(){
commands.add(move |world|{
world.entity_mut(entity).insert(SequenceInner(builder.build(world)));
})
}
}
}
} |
Beggin' your pardon, Elm, but fn event_listener(mut er: EventReader<MyEvent>, query: Query<&KeySequence>) {
for e in er.read() {
println!("{e:?} emitted.");
println!("there are {} key sequences", query.iter().count());
}
} You're right there is a slight bit of obfuscation now because we're no longer spawning them directly. We're using the Create a KeySequence entity and component.commands.add(KeySequence::new(…)); Create a KeySequence component and add it to an entity.commands.entity(id).add(KeySequence::new(…));
// OR
commands.spawn(…)
.add(KeySequence::new(…)); Create a KeySequence component but don't add it to anythingcommands.add(|&mut world| {
let builder = KeySequence::new(…);
let key_sequence = builder.build(world);
// And then put it somewhere?
}); Perhaps I should add a few of these details into an advanced section of the README. |
Sorry, I made a mistake in my statement. As for the documentation, I am thinking of adding it myself after merging. The only remaining concern is If there are no particular problems, I will also verify the operation (I cannot check the operation of the gamepad because my Playstation Controller is broken) and merge it after I get home. Thank you! P.S. Also, I would like to add or transfer you as the authority owner of this repository.What do you think? |
Admittedly returning a builder from Sure thing. Happy to take on some maintainership duties if you like. You can add me as a collaborator to this repo. That'll give me push access to the repo. You can also add me as a owner on crates.io if you're comfortable with me publishing releases there. |
Merged after checking operation and no problems were found! |
ME, calling up Elm on the phone a la Back to the Future: Hey Elm, you know that new gamepad handling you've been looking for? Well, look at this!
Heh heh.
Before
After
More earnestly, I think this patch has some good stuff in it. I set out to deal with the gamepad issue, and what I came up with was this: Why don't we just fire systems when we match a key sequences? It's still remarkably easy to fire events as before we just use our helper
action::send_event(MyEvent)
, but now you can just invoke whatever you like.But the real cherry on top is that systems can have input, and button sequences expect a system that accepts a
Gamepad
as its input, which means you can send an event, populate it with your gamepad info without anyGamepadEvent
shenanigans; in factGamepadEvent
trait has been thrown out.Doing things this way means that we're not specializing on the event type
E
anymore. That allowed me to setup us up with aInputSequencePlugin
. While I was adding plugin, I decided to follow in bevy_defer's foot steps and allow the user to declare whatSchedule
and whatSystemSet
they'd like to use. I've actually managed to avoid writing a ton of code with some judicious uses ofrun_if()
s withbevy-input-sequence
.Let me know what you think. Be well, Elm!