bevy_activation/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3use bevy_app::{App, Plugin, PostUpdate};
4use bevy_ecs::prelude::*;
5use bevy_reflect::Reflect;
6use bevy_time::{Stopwatch, Time};
7use std::time::Duration;
8
9/// Bevy Entity Activation Plugin;
10pub struct ActivationPlugin;
11
12impl Plugin for ActivationPlugin {
13    fn build(&self, app: &mut App) {
14        app.register_type::<ActiveState>()
15            .add_event::<TimeoutEvent>()
16            .add_systems(PostUpdate, check_timeout);
17    }
18}
19
20/// Timeout Event
21#[derive(Event)]
22pub struct TimeoutEvent;
23
24/// Activation State Component
25#[derive(Debug, Component, Reflect)]
26#[reflect(Component)]
27pub struct ActiveState {
28    /// is active
29    active: bool,
30    /// stop watch
31    watch: Option<Stopwatch>,
32    /// Time to Interactive (TTI)
33    time_to_idle: Duration,
34}
35
36impl Default for ActiveState {
37    fn default() -> Self {
38        Self::always()
39    }
40}
41
42impl ActiveState {
43    /// no timeout
44    pub fn always() -> ActiveState {
45        ActiveState {
46            active: true,
47            watch: None,
48            time_to_idle: Duration::MAX,
49        }
50    }
51
52    #[inline]
53    pub fn is_active(&self) -> bool {
54        self.active
55    }
56
57    #[inline]
58    pub fn is_idle(&self) -> bool {
59        !self.is_active()
60    }
61
62    /// set active
63    #[inline]
64    pub fn active_mut(&mut self) -> &mut bool {
65        &mut self.active
66    }
67
68    /// setup timeout time
69    #[inline]
70    pub fn time_to_idle_mut(&mut self) -> &mut Duration {
71        &mut self.time_to_idle
72    }
73
74    /// new active state with timeout
75    pub fn new(timeout: Duration) -> ActiveState {
76        ActiveState {
77            active: true,
78            watch: Some(Stopwatch::new()),
79            time_to_idle: timeout,
80        }
81    }
82
83    /// set timeout time
84    pub fn set_tti_time(&mut self, timeout: Duration) -> &mut ActiveState {
85        self.time_to_idle = timeout;
86        if self.watch.is_none() {
87            self.watch = Some(Stopwatch::new());
88        }
89        self
90    }
91
92    /// toggle active state
93    pub fn toggle(&mut self) {
94        self.active = true;
95        if let Some(watch) = &mut self.watch {
96            watch.reset();
97        }
98    }
99}
100
101/// set timeout to not active and send timeout event
102fn check_timeout(
103    mut commands: Commands,
104    time: Res<Time>,
105    mut active_state_query: Query<(Entity, &mut ActiveState)>,
106) {
107    for (entity, mut active_state) in active_state_query.iter_mut() {
108        if active_state.is_idle() {
109            continue;
110        }
111        let timeout = active_state.time_to_idle;
112        if let Some(watch) = &mut active_state.watch {
113            watch.tick(time.delta());
114
115            if watch.elapsed() >= timeout {
116                commands.trigger_targets(TimeoutEvent, entity);
117                watch.reset();
118                active_state.active = false;
119            }
120        }
121    }
122}