-
I got a player and a weapon: #[derive(Component)]
struct Player {
my_weapon: Entity,
}
#[derive(Component)]
struct WeaponDamage {
amount: f32,
}
#[derive(Component)]
struct WeaponCooldown {
amount: Duration,
until: Duration,
} If I want to use my weapon, that's pretty easy. I just need an #[derive(Event)]
struct Attack;
fn attack(mut cmd: Commands, player: Single<&Player>) {
// input processing ...
cmd.entity(player.my_weapon).trigger(Attack);
}
// Plugins to register these observers ...
fn damage_observer(trig: Trigger<Attack>, mut cmd: Commands) { /* Apply damage */ }
fn cooldown_observer(trig: Trigger<Attack>, time: Res<Time>) { /* Update cooldown timer */ } Neat and clean. But if I just want to check whether the weapon is ready, in order to show some visual hints for player or something else, it starts to get awful: fn attack(
mut cmd: Commands,
player: Single<&Player>,
weapon: Query<&WeaponCooldown>,
time: Res<Time>,
) {
if let Ok(cooldown) = weapon.get(player.my_weapon) {
if time.elapsed() > cooldown.until {
cmd.entity(player.my_weapon).trigger(Attack);
} else {
// "Your weapon is not ready yet!"
}
} else {
cmd.entity(player.my_weapon).trigger(Attack);
}
} It introduces the And it could be even worse with multiple fn attack(
mut cmd: Commands,
player: Single<&Player>,
weapon: Query<(&A, &B)>,
other: Query<&C>,
time: Res<Time>,
) {
if let Ok((comp_a, comp_b)) = weapon.get(player.my_weapon) {
if A::check_ready(comp_a, comp_b, &other) {
cmd.entity(player.my_weapon).trigger(Attack);
} else {
// "Your weapon is not ready yet!"
}
} else {
cmd.entity(player.my_weapon).trigger(Attack);
}
}
impl A {
// a normal function with a SystemParam?
fn check_ready(comp_a: &A, comp_b: &B, query: &Query<&C>) -> bool {
if comp_a.condition() && let Ok(other) = query.get(comb_b.other_entity) {
// no idea what's going on here
} else {
false
}
}
} This makes completely no sense and is unreadable. Of course I can just push these logic into observers, but then I cannot get feedback from these observers and display those necessary hints for players, and I have to merge the two separate observers Here is what I want: #[derive(Event)]
struct Ready(bool);
fn attack(mut cmd: Commands, player: Single<&Player>) {
// input processing ...
cmd.entity(player.my_weapon).queue(|entity: EntityWorldMut| {
let result = entity.check::<Ready>();
match result {
Some(true) => entity.trigger::<Attack>(),
Some(false) => (/* "Your weapon is not ready" */),
// `None` if nobody answers
None => entity.trigger::<Attack>(),
}
});
}
// observer-like
fn participator(mut cond: Condition<Ready>, query: Query<&WeaponCooldown>, time: Res<Time>) {
if let Ok(cooldown) = query.get(cond.entity) {
if time.elapsed() > cooldown.until {
cond.replace(Ready(true));
} else {
cond.replace(Ready(false));
}
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
For this kind of logic I usually do this, which basically boils down to using #[derive(Event)]
struct Ready {
flag: bool,
}
let mut entity: EntityWorldMut = /* ... */;
let id = entity.id();
// Something listening to `Ready` can set `Trigger::event_mut().flag = false`.
let mut ready = Ready { flag: true };
entity.world_scope(|world| world.trigger_targets_ref(&mut ready, id));
if ready.flag {
entity.trigger(Attack);
} |
Beta Was this translation helpful? Give feedback.
For this kind of logic I usually do this, which basically boils down to using
World::trigger_targets_ref
: