#Using Generics with EventReader

29 messages · Page 1 of 1 (latest)

shell sundial
#

Hey, I'm pretty new to bevy and wanted to know if I'm doing something correctly or overcomplicating things.

In my game I want to handle Death of enemies and bosses and basically anything that has the Health component in the same generic Observer that listens to DeathEvents

#[derive(Event)]
pub struct DeathEvent {
  pub target: Entity,
}

if health.get_current() <= 0.0 {
  println!("entity {target} has 0 health, sending death event");
  commands.trigger(DeathEvent { target });
}

But if I'm understanding correctly if I have an observer like so

pub(super) fn plugin(app: &mut App) {
    app.add_observer(death_event_trigger::<Enemy>).
        add_observer(death_event_trigger::<Boss>);
}

pub trait OnDeath {
    fn on_death(&self) {
        println!("Default");
    }
}
pub trait DeathComponent: Component + OnDeath {}
impl<T: Component + OnDeath> DeathComponent for T {}

fn death_event_trigger<T: DeathComponent>(
    trigger: Trigger<DeathEvent>,
    mut transform_query: Query<(&mut Transform, &T)>,
) {
    let entity = trigger.event().target;

    if let Ok((mut transform, mut death_component)) = transform_query.get_mut(entity) {
        death_component.on_death()
    }
}

Will it even work, or is it going to create a race condition as there are going to be multiple observers reading from the same events being sent,

Thanks in advance!

sick mesa
#

The generics are part of the type so with different generics it will be a different type

#

and since Bevy identifies these things by type, it should be a different event with its own queue

#

but I've never tried this specific thing

ebon aurora
#

You could put the death behaviour in a ZST that implements a trait and store a box dyn to that in the health component

#

or actually just store a function pointer

#

probably easiers

#

and couldn't you run whatever callback you want at the point of sending the death event?

#
struct Health {
    value: f32,
    callback: fn(slf: &mut Self) -> (),
}
impl Health {
    fn set(&mut self, value: f32) {
        self.value = value;
        if value < 0.05 {
            (self.callback)(self);
        }
    }
}
#

Function pointer method

ebon aurora
#

The ZST way:

use std::marker::PhantomData;

struct Health {
    value: f32,
    callback: Box<dyn OnDeath>,
}

trait OnDeath {
    fn on_death(&self, /* args */);
}

struct OnDeathMarker<M>(PhantomData<M>);

struct Enemy0 {
    // put anything here
}
struct Enemy1 {
    // put anything here
}
impl OnDeath for OnDeathMarker<Enemy0> {
    fn on_death(&self, /* args */) {
        println!("Enemy0")
    }
}
impl OnDeath for OnDeathMarker<Enemy1> {
    fn on_death(&self, /* args */) {
        println!("Enemy1")
    }
}
#

The function must accept an &self to by dyn compatible even though &self only points to phantom data

#

I think the function pointer way is better

shell sundial
#

Hmmmm so you say actually to store the callback method on the health component, i guess this makes sense, makes it so I don’t need observers

#

Is it possible to access queries inside the function pointer method

ebon aurora
#

No, only bevy systems and observers can query

#

If you want that information it has to be provided by the function that calls the callback

#

That would be an advantage of using an observer

#

You could even store the function pointer in the event if you like

#

Idk if there’s much advantage to that

shell sundial
#

So if i want it to be as flexible as possible I should pass the callback function to the as part of the eventwriter

ebon aurora
#

I suppose so, but only do that if you actually need that flexibility

shell sundial
#

@ebon aurora what would you do if you needed to implement different after death effects for each boss in this scenario, prob 10-15 of those in the game

#

Maybe the ZST method is the cleanest

shell sundial
sick mesa
#

I think both of these sound overcomplicated, didn't click link

#
fn handle_mob_death(dead_mobs: Query<(Entity, &Enemy), Without<Boss>>) {
``` but maybe I'm missing something
shell sundial
#

Yeah that would work but then I have to create that system times every boss and mob I want to have special death clauses for