Expand description

§Bevy Sequential Actions

crates.io github.com MIT/Apache 2.0

A Bevy library that aims to execute a queue of various actions in a sequential manner. This generally means that one action runs at a time, and when it is done, the next action will start and so on until the queue is empty.

§📜 Getting Started

§Plugin

The quickest way for getting started is adding the SequentialActionsPlugin to your App.

use bevy_sequential_actions::*;

fn main() {
    App::new()
        .add_plugins(SequentialActionsPlugin)
        .run();
}
§Modifying Actions

An action is anything that implements the Action trait, and can be added to any Entity that contains the ActionsBundle. An entity with actions is referred to as an agent. See the ModifyActions trait for available methods.

fn setup(mut commands: Commands) {
    let agent = commands.spawn(ActionsBundle::new()).id();
    commands
        .actions(agent)
        .add(action_a)
        .add_many(actions![
            action_b,
            action_c
        ])
        .order(AddOrder::Front)
        .add(action_d)
        // ...
}
§Implementing an Action

The Action trait contains 3 required methods:

  • is_finished to determine if an action is finished or not.
  • on_start which is called when an action is started.
  • on_stop which is called when an action is stopped.

In addition, there are 3 optional methods:

  • on_add which is called when an action is added to the queue.
  • on_remove which is called when an action is removed from the queue.
  • on_drop which is the last method to be called with full ownership.

A simple wait action follows.

pub struct WaitAction {
    duration: f32, // Seconds
    current: Option<f32>, // None
}

impl Action for WaitAction {
    fn is_finished(&self, agent: Entity, world: &World) -> bool {
        // Determine if wait timer has reached zero.
        // By default, this method is called every frame in the Last schedule.
        world.get::<WaitTimer>(agent).unwrap().0 <= 0.0
    }

    fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
        // Take current time (if paused), or use full duration.
        let duration = self.current.take().unwrap_or(self.duration);

        // Run the wait timer system on the agent.
        world.entity_mut(agent).insert(WaitTimer(duration));

        // Is action already finished?
        // Returning true here will immediately advance the action queue.
        self.is_finished(agent, world)
    }

    fn on_stop(&mut self, agent: Entity, world: &mut World, reason: StopReason) {
        // Take the wait timer component from the agent.
        let wait_timer = world.entity_mut(agent).take::<WaitTimer>();

        // Store current time when paused.
        if reason == StopReason::Paused {
            self.current = Some(wait_timer.unwrap().0);
        }
    }
}

#[derive(Component)]
struct WaitTimer(f32);

fn wait_system(mut wait_timer_q: Query<&mut WaitTimer>, time: Res<Time>) {
    for mut wait_timer in &mut wait_timer_q {
        wait_timer.0 -= time.delta_seconds();
    }
}
§⚠️ Warning

One thing to keep in mind is when modifying actions using World inside the Action trait. In order to pass a mutable reference to world when calling the trait methods, the action has to be temporarily removed from an agent. This means that depending on what you do, the logic for advancing the action queue might not work properly.

In general, there are two rules when modifying actions for an agent inside the action trait:

  • When adding new actions, you should either set the start property to false, or push to the ActionQueue component directly.
  • The execute and next methods should not be used.

Macros§

  • Helper macro for creating an array of boxed actions.

Structs§

Enums§

Traits§

Type Aliases§