1use bevy_app::{prelude::*, AppExit, ScheduleRunnerPlugin};
2use bevy_ecs::prelude::*;
3
4use bevy_sequential_actions::*;
5
6fn main() {
7 App::new()
8 .add_plugins((ScheduleRunnerPlugin::default(), SequentialActionsPlugin))
9 .add_systems(Startup, setup)
10 .add_systems(Update, countdown)
11 .run();
12}
13
14fn setup(mut commands: Commands) {
15 let agent = commands.spawn(SequentialActions).id();
16 commands.actions(agent).add((
17 ParallelActions::new(actions![
18 PrintAction("hello"),
19 CountdownAction::new(2),
20 PrintAction("world"),
21 CountdownAction::new(4),
22 ]),
23 |_agent, world: &mut World| {
24 world.send_event(AppExit::Success);
25 false
26 },
27 ));
28}
29
30struct ParallelActions<const N: usize> {
31 actions: [BoxedAction; N],
32}
33
34impl<const N: usize> ParallelActions<N> {
35 const fn new(actions: [BoxedAction; N]) -> Self {
36 Self { actions }
37 }
38}
39
40impl<const N: usize> Action for ParallelActions<N> {
41 fn is_finished(&self, agent: Entity, world: &World) -> bool {
42 self.actions
43 .iter()
44 .all(|action| action.is_finished(agent, world))
45 }
46
47 fn on_add(&mut self, agent: Entity, world: &mut World) {
48 self.actions
49 .iter_mut()
50 .for_each(|action| action.on_add(agent, world));
51 }
52
53 fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
54 std::array::from_fn::<bool, N, _>(|i| self.actions[i].on_start(agent, world))
55 .into_iter()
56 .all(|b| b)
57 }
58
59 fn on_stop(&mut self, agent: Option<Entity>, world: &mut World, reason: StopReason) {
60 self.actions
61 .iter_mut()
62 .for_each(|action| action.on_stop(agent, world, reason));
63 }
64
65 fn on_remove(&mut self, agent: Option<Entity>, world: &mut World) {
66 self.actions
67 .iter_mut()
68 .for_each(|action| action.on_remove(agent, world));
69 }
70}
71
72struct PrintAction(&'static str);
73
74impl Action for PrintAction {
75 fn is_finished(&self, _agent: Entity, _world: &World) -> bool {
76 true
77 }
78
79 fn on_start(&mut self, _agent: Entity, _world: &mut World) -> bool {
80 println!("{}", self.0);
81 true
82 }
83
84 fn on_stop(&mut self, _agent: Option<Entity>, _world: &mut World, _reason: StopReason) {}
85}
86
87struct CountdownAction {
88 count: u32,
89 entity: Entity,
90}
91
92impl CountdownAction {
93 const fn new(count: u32) -> Self {
94 Self {
95 count,
96 entity: Entity::PLACEHOLDER,
97 }
98 }
99}
100
101impl Action for CountdownAction {
102 fn is_finished(&self, _agent: Entity, world: &World) -> bool {
103 world.get::<Countdown>(self.entity).unwrap().0 == 0
104 }
105
106 fn on_add(&mut self, _agent: Entity, world: &mut World) {
107 self.entity = world.spawn_empty().id();
108 }
109
110 fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
111 let mut entity = world.entity_mut(self.entity);
112
113 if entity.contains::<Paused>() {
114 entity.remove::<Paused>();
115 } else {
116 entity.insert(Countdown(self.count));
117 println!("Countdown({}): {}", self.entity, self.count);
118 }
119
120 self.is_finished(agent, world)
121 }
122
123 fn on_stop(&mut self, _agent: Option<Entity>, world: &mut World, reason: StopReason) {
124 if reason == StopReason::Paused {
125 world.entity_mut(self.entity).insert(Paused);
126 }
127 }
128
129 fn on_remove(&mut self, _agent: Option<Entity>, world: &mut World) {
130 world.despawn(self.entity);
131 }
132}
133
134#[derive(Component)]
135struct Countdown(u32);
136
137#[derive(Component)]
138struct Paused;
139
140fn countdown(mut countdown_q: Query<(Entity, &mut Countdown), Without<Paused>>) {
141 for (entity, mut countdown) in &mut countdown_q {
142 countdown.0 = countdown.0.saturating_sub(1);
143 println!("Countdown({}): {}", entity, countdown.0);
144 }
145}