Coder Social home page Coder Social logo

hikikones / bevy-sequential-actions Goto Github PK

View Code? Open in Web Editor NEW
35.0 1.0 1.0 230 KB

A Bevy library for executing various actions in a sequence.

Home Page: https://docs.rs/bevy-sequential-actions

License: Apache License 2.0

Rust 100.00%
bevy gamedev ecs game-development rust bevy-plugin bevy-engine

bevy-sequential-actions's Introduction

Bevy Sequential Actions

crates.io docs.rs 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.

demo.mp4

๐Ÿ“œ 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.

๐Ÿ“Ž Examples

See the examples for more usage. Each example can be run with cargo run --example <example>.

Example Description
basic Shows the basic usage of the library.
pause Shows how to pause and resume an action.
repeat Shows how to create an action that repeats.
parallel Shows how to create actions that run in parallel.
sequence Shows how to create an action with a sequence of actions.
schedule Shows how to use the plugin with two different schedules.
custom Shows how to use a custom system for advancing the action queue.
despawn Shows how to properly despawn an agent.

๐Ÿ“Œ Compatibility

bevy bevy-sequential-actions
0.14 0.11
0.13 0.10
0.12 0.9
0.11 0.8
0.10 0.7
0.9 0.6
0.8 0.3 โ€“ 0.5
0.7 0.1 โ€“ 0.2

๐Ÿ”– License

bevy-sequential-actions is dual-licensed under either

at your option.

bevy-sequential-actions's People

Contributors

braymatter avatar hikikones 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

Watchers

 avatar

Forkers

braymatterorg

bevy-sequential-actions's Issues

Panic After adding Any Actions

I get a panic from the framework at bevy-sequential-actions-0.9.0\src\plugin.rs:151:76: with the current crates release; I've added the ActionsBundle to the agent, it does not panic if there are no actions added.

Construction of ent:

        let mut system_state = SystemState::<Commands>::new(world);
        let mut cmds = system_state.get_mut(world);

        let agent = cmds
            .spawn((
                MaterialMeshBundle {
                    mesh,
                    material,
                    transform: Transform::from_translation(self.tsf.translation),
                    ..default()
                },
                Collider::cuboid(0.5, 0.5, 0.5),
                Sensor,
                SpellImmune,
                DamageOnCollision {
                    amt: self.explosion_cfg.dmg,
                },
                Animator::new(Tween::new(
                    EaseFunction::QuadraticOut,
                    Duration::from_millis(500),
                    TransformScaleLens {
                        start: Vec3::ZERO,
                        end: self.tsf.scale,
                    },
                )),
                GameCleanup,
                ActionsBundle::new(),
                self,
            ))
            .id(); 

        cmds.actions(agent).add_many(actions![
            WaitAction::new(Duration::from_millis(500)),
            DespawnAction(agent)
        ]);

        system_state.apply(world);

Commands:

pub struct DespawnAction(pub Entity);

impl Action for DespawnAction {
    fn is_finished(&self, _agent: Entity, _world: &World) -> bool {
        info!("Despawning Action is_finished");
        true
    }

    fn on_start(&mut self, _agent: Entity, world: &mut World) -> bool {
        info!("Starting DespawnAction");
        let mut system_state = SystemState::<Commands>::new(world);
        let mut cmds = system_state.get(world);

        let Some(entcmds) = cmds.get_entity(self.0) else {
            warn!("Could not get entity commands for despawn action");
            return true;
        };

        entcmds.despawn_recursive();

        system_state.apply(world);

        true
    }

    fn on_stop(&mut self, _agent: Entity, _world: &mut World, _reason: StopReason) {}
}

pub struct WaitAction {
    pub duration: Duration,
}

impl WaitAction {
    pub fn new(duration: Duration) -> Self {
        Self { duration }
    }
}

impl Action for WaitAction {
    fn is_finished(&self, agent: Entity, world: &World) -> bool {
        let Some(entref) = world.get_entity(agent) else {
            warn!("Could not get entref from world for WaitAction");
            return true;
        };

        let Some(timer) = entref.get::<WaitTimer>() else {
            warn!("No wait timer found on entity for WaitAction");
            return true;
        };

        timer.timer.finished()
    }

    fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
        let Some(mut entref) = world.get_entity_mut(agent) else {
            warn!("Couldn't get entity to add WaitTimer to!");
            return true;
        };

        entref.insert(WaitTimer::new(self.duration));
        false
    }

    fn on_remove(&mut self, agent: Entity, world: &mut World) {
        let Some(mut entref) = world.get_entity_mut(agent) else {
            warn!("Couldn't get entity to Remove WaitTimer after action!");
            return;
        };

        entref.remove::<WaitTimer>();
    }

    fn on_stop(&mut self, _agent: Entity, _world: &mut World, _reason: StopReason) {
        info!("OnStop WaitAction")
    }
}

#[derive(Component, Reflect)]
pub struct WaitTimer {
    pub timer: Timer,
}

impl Default for WaitTimer {
    fn default() -> Self {
        Self {
            timer: Timer::new(Duration::from_secs(1), TimerMode::Once),
        }
    }
}

impl WaitTimer {
    pub fn new(duration: Duration) -> Self {
        Self {
            timer: Timer::new(duration, TimerMode::Once),
        }
    }
}

fn tick_wait_action_timers(mut wait_timer: Query<&mut WaitTimer>, time: Res<Time>) {
    for mut wait_timer in wait_timer.iter_mut() {
        wait_timer.timer.tick(time.delta());
    }
}

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.