Giter Site home page Giter Site logo

bevy_eventlistener's People

Contributors

aevyrie avatar bonsairobo avatar jnhyatt avatar paul-hansen avatar tristancacqueray avatar vultix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

bevy_eventlistener's Issues

Make `On::send_event` more flexible

    pub fn send_event<F: Event + From<ListenerInput<E>>>() -> Self {

This is the current signature of On::send_event.

However, I want to be able to quickly do things like "send an AppExit event when this button is clicked".

I propose three related methods:

  1. send_event: takes an event of type F and captures it in the closure.
  2. convert_and_send_event: current behavior: converts E to F and sends it.
  3. send_default_event: sends an event with the Default value.

Pattern: Behaviors

This crate is awesome! It's solving exactly the problem that was driving me nuts when trying to represent intents & actions in a turn-based roguelike prototype I'm using to play around with bevy.

An architecture for representing behaviors emerged pretty directly from the way bevy_eventlistener is set up. I'm sharing it here because 1. maybe it's useful for someone 2. maybe it's generic enough to be supported with a bit of extra library code, either here or in a separate crate.

Goals

  • Minimal boilerplate and duplication
  • Intents (i.e. "I want to move here", "I want to skip this turn") should be EntityEvents
  • Behaviors triggered by these intents should be listener systems, and fully encapsulate all the logic

What it looks like

Prototype: abesto/treeffect@8bc7403 (this is a commit that moves from intents being components, and behaviors being regular systems)

Intent

Nothing interesting here, just so that the rest makes sense.

#[derive(Clone, Event, EntityEvent)]
pub struct MovementIntent {
    #[target]
    pub actor: Entity,
    pub vector: IVec2,
}

EntityEvents are registered in a plugin with some macro magic for further DRY:

// short for "event listener plugins"
macro_rules! elp {
    ($app:ident, $($events:ident),+ $(,)?) => {
        $($app.add_plugins(EventListenerPlugin::<$events>::default());)+
    };
}

pub struct EventsPlugin;

impl Plugin for EventsPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<took_turn::TookTurn>();
        elp!(app, MovementIntent, AttackIntent, WaitIntent);
    }
}

On reflection, elp should probably be replaced with an App extention trait with .add_intents or something more generic.

Side-track: that might actually be a meaningful separate feature request. app.add_entity_event::<WaitIntent>() is sensible. It still wouldn't be DRY enough, so I'd probably still have a similar wrapper macro.

Behavior machinery

A trait so we can hang logic somewhere, implemented for tuples (spoiler: think about bundles):

pub trait Behavior {
    fn behavior() -> Self;
}

#[impl_for_tuples(1, 16)]
impl Behavior for BehaviorIdentifier {
    fn behavior() -> Self {
        for_tuples!( ( #( (BehaviorIdentifier::behavior())  ),* ) )
    }
}

Simple Behavior

fn movement(
    mut query: Query<&mut Position, With<Active>>,
    map: Res<Map>,
    intent: Listener<MovementIntent>,
    mut ev_took_turn: EventWriter<TookTurn>,
) {
    let entity = intent.listener();
    let Ok(mut position) = query.get_mut(entity) else {
        return;
    };
    let new_position = map.iclamp(&(position.xy.as_ivec2() + intent.vector));
    if map.is_walkable(&new_position) {
        position.xy = new_position;
    }
    ev_took_turn.send(entity.into());
}

behavior!(movement);

I'm ignoring event propagation here, because I'm not using entity hierarchies. I'm probably picking the wrong one out of (Listener, Target) somewhere.

behavior! is a simple macro that, if I was an adult, I'd implement as a derive macro. It's coupled very tightly to my naming conventions, unsure how feasible this would be as library code. It generates code like this:

pub type MovementBehavior = On<MovementIntent>;

impl Behavior for MovementBehavior {
    fn behavior() -> Self {
        On::<MovementIntent>::run(movement)
    }
}

Composed Behavior

pub type ActorBehavior = (MovementBehavior, AttackBehavior, WaitBehavior);

I guess you could call this a behavior bundle.

Used in a Bundle

You can pretty much figure this out from the rest, but for the record:

type Behaviors = ActorBehavior;  // Could add more specific behaviors here

#[derive(Bundle)]
pub struct PlayerBundle {
    ...
    pub behaviors: Behaviors,
}

impl Default for PlayerBundle {
    fn default() -> Self {
        PlayerBundle {
             ...
            behaviors: Behaviors::behavior(),
        }
    }
}

Support targeting multiple entities

Some events intrinsically needs to targets multiple entities.

For example collision events will need to be able to report to multiple entities. You can currently simulate this by cloning the event (which can contain lots of data like a vector of manifolds) and sending them to each entity.

I think the ability to natively target multiple entities would help a lot for this kind of use-case.

Add the ability to have multiple callbacks in a single listener

This feature is necessary because there's currently no way to have more than one of a callback of a single type on an entity.

This is usually just a papercut, but it is required in bevy_mod_picking, in order to merge on_click / on_drag etc into a single callback.

In turn, we want to do that in order to handle the ordering of input events correctly„ using bevyengine/bevy#12100

Add a default impl for `On`

This would be useful to construct bundle types with On components that have no effect.

I'm running into this while trying to construct ButtonBundle equivalents with On<Pointer<Click>> and On<Pointer<Hover>> fields.

Event sinking

We currently can traverse up the hierarchy via bubbles, which is great!
I think we can extend this by also allowing for sinking - letting an Event propagate down the tree from the calling node.

While this may prove worse in terms of performance (linearly going up the tree vs recursively going down children) for most situations, there are times when having a parent node and propagating events generated on the parent to all the children is a good design choice.

This feature would work like another trait on EntityEvents, : #[can_sink]

This would not block #[can_bubble], and if you define both then your event gets sent both up and down the tree.

Custom events sometimes get eaten

Here's a minimal example that illustrates the issue I discussed last week on the Discord. When I sent a custom event "Clicked" (not "Click"), the first event is sometimes not received:

  • The event is sent properly.
  • The event is always received by the global event reader.
  • The event is NOT received by the On handler, but only the first time.
  • Subsequent events are properly received by the On handler as expected.

The problem is intermittent - you may have to run the example multiple times in order to see it. For me, happens about 50% of the time, so it shouldn't take many runs to see it.

When it is not working, you'll see output like this:

--> Sending Clicked id='One' target=4v0
? Reading global clicked: id='One' target=4v0

And when it is working, you'll see output like this:

--> Sending Clicked id='One' target=4v0
<-- Received Clicked Button id='One' target=4v0
? Reading global clicked: id='One' target=4v0

main.rs.zip

Upgrade to `bevy` `0.11.0`

Hi there! I'm using this as a standalone crate in a project and looking to update Bevy to latest. I'm happy to help migrate the code, just wanted to open this issue to verify that you don't already have a branch out for this. Let me know and I'll get in a pull request ASAP.

Does not compile if bevy_reflect's "documentation" feature is enabled after updating to 0.5

I ran into this issue updating the editor for my game where I use the bevy_reflect "documentation" feature to show component documentation for easy access. It used to work with bevy 0.10 and in bevy 0.11 it is now failing with the below error, repeated for every type that impl_reflect_value is used on.

error[E0599]: no method named `with_docs` found for struct `type_info::ValueInfo` in the current scope
   --> /home/paul/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_reflect-0.11.3/src/impls/uuid.rs:6:1
    |
6   |   impl_reflect_value!(::bevy_utils::Uuid(
    |  _-
7   | |     Serialize,
8   | |     Deserialize,
9   | |     Default,
...   |
12  | |     Hash
13  | | ));
    | |  ^
    | |  |
    | |__method not found in `ValueInfo`
    |    in this macro invocation
    |
   ::: /home/paul/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_reflect-0.11.3/src/type_info.rs:177:1
    |
177 |   pub struct ValueInfo {
    |   -------------------- method `with_docs` not found for this struct
    |
   ::: /home/paul/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_reflect_derive-0.11.3/src/lib.rs:381:1
    |
381 |   pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
    |   ------------------------------------------------------------ in this expansion of `impl_reflect_value!`

Minimal Reproduction

  1. Create a new project with this Cargo.toml

    [package]
    name = "bevy_docs_test"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    bevy_eventlistener = "0.5"
    
    bevy_reflect = {version="0.11", features=["documentation"]}
  2. Run cargo run

Changing bevy_reflect to version 0.11 and bevy_eventlistener to 0.4 makes it work again.
Removing the documentation feature from bevy_reflect also makes it work again.

calling `stop_propagation()` on immutable events

I was disappointed to discover this won't work:

On::<ScrollWheel>::listener_component_mut::<Scrolling>(move |ev, scrolling| {
    // (Do stuff)
    ev.stop_propagation(); // ERROR: 'ev' is immutable
}),

The problem is that listener_component_mut gives me an immutable listener, rather than a mutable one; most of the other convenience methods have the same limitation. So if I want to call stop_propagation() I have to use run().

I generally want to call stop_propagation() in most handlers, which means that the convenience methods hardly ever get used.

Add `On::set_next_state`

This is a common simple pattern that uses a surprising amount of boilerplate. We could simplify this to a single method call.

Running entity systems in fixed update

Hello, is there a way, to run systems from On::run in fixed update?

My usecase is that i want to simulate physics for separate world assigned to separate entities, meaning each entity has a physics world and I want to process the input with bevy_eventlistener and then simulate the physics from that input in fixed update.

Thanks in advance.

Re-export `ListenerInput`

If I understand correctly, ListenerInput should probably be re-exported with other callbacks here:

pub use bevy_eventlistener_core::{
callbacks::{Listener, ListenerMut},
event_listener::{EntityEvent, On},
EventListenerPlugin,
};

as it's commonly used like in this bevy_mod_picking example:

struct DoSomethingComplex(Entity, f32);

impl From<ListenerInput<Pointer<Down>>> for DoSomethingComplex {
    fn from(event: ListenerInput<Pointer<Down>>) -> Self {
        DoSomethingComplex(event.target, event.hit.depth)
    }
}

Run if feature

Bevy systems have very helpful control flow extension methods called run_if.

Is there a way to use them with this library that is documented? I have tried a simple run_if and am getting a compiler error as shown below.

error[E0277]: the trait bound `NodeConfigs<std::boxed::Box<dyn bevy::prelude::System<In = (), Out = ()>>>: bevy::prelude::IntoSystem<(), (), _>` is not satisfied
  --> src/game/character.rs:99:17
   |
98 |             on_drag_start: On::<Pointer<DragStart>>::run(
   |                            ----------------------------- required by a bound introduced by this call
99 |                 drag_start_character_system.distributive_run_if(tool_active(ToolType::Arrow)),
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `bevy::prelude::IntoSystem<(), (), _>` is not implemented for `NodeConfigs<std::boxed::Box<dyn bevy::prelude::System<In = (), Out = ()>>>`
   |
note: required by a bound in `On::<E>::run`
  --> /Users/stevenbeshensky/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_eventlistener_core-0.6.1/src/event_listener.rs:41:39
   |
41 |     pub fn run<Marker>(callback: impl IntoSystem<(), (), Marker>) -> Self {
   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `On::<E>::run`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.