如何在不消耗Bevy事件的情况下阅读它们?

问题描述

我目前正在尝试使用事件来表示角色在我的Bevy游戏中何时跳跃。我希望处理玩家输入的系统发送一个JumpedEvent,然后其他系统可以接收它执行适当的操作(设置正确的玩家动画,力度,声音等),但是第一个读取该内容的系统事件会消耗掉它。

Bevy是否提供一种无需消耗事件即可读取事件的方法

这是我当前的代码

// Sends a JumpedEvent when the jump key is pressed
fn player_jump_event_system(
    keyboard_input: Res<Input<KeyCode>>,mut jump_events: ResMut<Events<JumpedEvent>>,mut query: Query<(&Controlled,&mut Jumps)>,) {
    for (controlled,mut jumps) in &mut query.iter() {
        if keyboard_input.just_pressed(controlled.jump)
            && jumps.jumps_remaining > 0
        {
            jumps.jumps_remaining -= 1;
            jump_events.send(JumpedEvent {
                jump_number: jumps.max_jumps - jumps.jumps_remaining,});
        }
    }
}

// This system consumes the JumpedEvent that was sent
fn control_player_jump_system(
    jump_events: ResMut<Events<JumpedEvent>>,mut jump_event_listener: ResMut<JumpedEventListener>,&mut Jumps,&mut VeLocity)>,) {
    const PLAYER_JUMP_SPEED: f32 = 450.0;
    const MULTI_JUMP_MODIFIER: f32 = 0.9;

    // For every jump event,make player jump slightly less high
    for jump_event in jump_event_listener.jumped_event_reader.iter(&jump_events)
    {
        for (_,_,mut veLocity) in &mut query.iter() {
            *veLocity.0.y_mut() = PLAYER_JUMP_SPEED
                * MULTI_JUMP_MODIFIER.powi(i32::from(jump_event.jump_number));
        }
    }
}

// This is the system that cannot receive the event because the above system consumes it
fn select_animation_system(
    texture_atlases: Res<Assets<TextureAtlas>>,jump_events: ResMut<Events<JumpedEvent>>,mut query: Query<(
        &Jumps,&VeLocity,&Animatedplayer,&mut TextureAtlassprite,&mut Handle<TextureAtlas>,)>,) {
    for (_,veLocity,animations,mut sprite,mut texture_atlas) in
        &mut query.iter()
    {

        // Check if the player just jumped
        let just_jumped = jump_event_listener
            .jumped_event_reader
            .iter(&jump_events)
            .next()
            .is_some();

        // Omitting irrelevant details...

        if just_jumped {

            sprite.index = 0;
        }
    }
}

解决方法

我刚刚意识到自己做错了。我正在使用单个全局资源EventReader来监听正在发送的JumpedEvent实例。每个EventReader仅读取每个事件一次。相反,我需要为每个需要监听事件的系统使用一个单独的EventReader。我通过对每个需要监听事件的系统使用Local EventReader来做到这一点。参见下面的修改后的代码:

// Sends a JumpedEvent when the jump key is pressed
fn player_jump_event_system(
    keyboard_input: Res<Input<KeyCode>>,mut jump_events: ResMut<Events<JumpedEvent>>,mut query: Query<(&Controlled,&mut Jumps)>,) {
    for (controlled,mut jumps) in &mut query.iter() {
        if keyboard_input.just_pressed(controlled.jump)
            && jumps.jumps_remaining > 0
        {
            jumps.jumps_remaining -= 1;
            jump_events.send(JumpedEvent {
                jump_number: jumps.max_jumps - jumps.jumps_remaining,});
        }
    }
}

// This system consumes the JumpedEvent that was sent
fn control_player_jump_system(
    jump_events: ResMut<Events<JumpedEvent>>,// See that this line now specifies that the resource is local to the system
    mut jump_event_listener: Local<JumpedEventListener>,&mut Jumps,&mut Velocity)>,) {
    const PLAYER_JUMP_SPEED: f32 = 450.0;
    const MULTI_JUMP_MODIFIER: f32 = 0.9;

    // For every jump event,make player jump slightly less high
    for jump_event in jump_event_listener.jumped_event_reader.iter(&jump_events)
    {
        for (_,_,mut velocity) in &mut query.iter() {
            *velocity.0.y_mut() = PLAYER_JUMP_SPEED
                * MULTI_JUMP_MODIFIER.powi(i32::from(jump_event.jump_number));
        }
    }
}

// This is the system that cannot receive the event because the above system consumes it
fn select_animation_system(
    texture_atlases: Res<Assets<TextureAtlas>>,jump_events: ResMut<Events<JumpedEvent>>,mut query: Query<(
        &Jumps,&Velocity,&AnimatedPlayer,&mut TextureAtlasSprite,&mut Handle<TextureAtlas>,)>,) {
    for (_,velocity,animations,mut sprite,mut texture_atlas) in
        &mut query.iter()
    {

        // Check if the player just jumped
        let just_jumped = jump_event_listener
            .jumped_event_reader
            .iter(&jump_events)
            .next()
            .is_some();

        // Omitting irrelevant details...

        if just_jumped {

            sprite.index = 0;
        }
    }
}

因此,每个EventReader在读取事件时都会消耗每个事件。因此,要拥有多个使用系统,每个系统都需要一个Local阅读器。