Skip to main content

mirage_engine/animation/
states.rs

1use core::fmt::Debug;
2use core::time::Duration;
3
4use crate::animation::{Motion, Progress};
5use crate::mesh::Clip;
6
7/// The states a game poses one mesh through, and what each of them plays.
8///
9/// Required if you want a machine to pose a draw: write the states as a
10/// fieldless enum, state what each of them plays and where it goes from
11/// there, and keep an [`Animator`](crate::Animator) of them beside every
12/// mesh the game poses.
13pub trait AnimationStates: Copy + Eq + Debug + Sized {
14    /// The clip vocabulary of the mesh these states pose.
15    type Clip: Clip;
16
17    /// What the states read to decide, the game's own to write; a machine
18    /// reads the default until `animate` first runs it.
19    type Input: Default;
20
21    /// The state a machine starts in.
22    fn entry() -> Self;
23
24    /// What this state plays, read every tick.
25    fn motion(&self, input: &Self::Input) -> Motion<Self::Clip>;
26
27    /// Where the machine goes from this state, read every tick; `at` is how
28    /// far along what it plays is, and nothing at all keeps it where it is.
29    ///
30    /// While a fade runs this is the state being faded into, so this state
31    /// and nothing both leave that fade running, and any other interrupts
32    /// it.
33    fn next(&self, input: &Self::Input, at: Progress) -> Option<Transition<Self>>;
34
35    /// Into this state over `over`; see [`Transition::fade`].
36    fn fade(self, over: Duration) -> Transition<Self> {
37        Transition::from(self).fade(over)
38    }
39
40    /// Into this state with no fade; see [`Transition::at_once`].
41    fn at_once(self) -> Transition<Self> {
42        Transition::from(self).at_once()
43    }
44
45    /// Into this state, entering `fraction` of the way in; see
46    /// [`Transition::entering_at`].
47    fn entering_at(self, fraction: f32) -> Transition<Self> {
48        Transition::from(self).entering_at(fraction)
49    }
50
51    /// Into this state from the start of what it plays again; see
52    /// [`Transition::restarted`].
53    fn restarted(self) -> Transition<Self> {
54        Transition::from(self).restarted()
55    }
56}
57
58/// Where a machine goes next, and how it gets there.
59///
60/// A state on its own reads as one that takes the default fade, so a
61/// transition that states no knob is `Some(State::Idle.into())`.
62#[must_use = "a transition only moves a machine once next returns it"]
63#[derive(Clone, Debug)]
64pub struct Transition<S> {
65    pub(crate) into: S,
66    pub(crate) fade: Duration,
67    pub(crate) entering_at: f32,
68    pub(crate) restarted: bool,
69}
70
71impl<S> Transition<S> {
72    /// The fade a transition takes to blend out of the state it leaves when
73    /// no call sets one: `150` milliseconds.
74    pub const DEFAULT_FADE: Duration = Duration::from_millis(150);
75
76    /// Takes `over` to blend out of the state it leaves;
77    /// [`Transition::DEFAULT_FADE`] until set.
78    ///
79    /// The state it leaves plays on under the fade, and a fade longer than
80    /// what the state it enters plays holds that motion's last key.
81    pub fn fade(mut self, over: Duration) -> Self {
82        self.fade = over;
83        self
84    }
85
86    /// Enters with no fade, which drops the state it leaves at the instant
87    /// it enters.
88    pub fn at_once(mut self) -> Self {
89        self.fade = Duration::ZERO;
90        self
91    }
92
93    /// Enters `fraction` of the way into what the state it enters plays, a
94    /// fraction held inside `0.0..=1.0`; the start of it until set.
95    ///
96    /// Required if you want to skip the first part of a clip a state enters
97    /// part way through.
98    pub fn entering_at(mut self, fraction: f32) -> Self {
99        self.entering_at = match fraction.is_nan() {
100            true => 0.0,
101            false => fraction.clamp(0.0, 1.0),
102        };
103        self
104    }
105
106    /// Plays what the state it enters plays from the start again, which is
107    /// what moves a machine to the state it is already in.
108    pub fn restarted(mut self) -> Self {
109        self.restarted = true;
110        self
111    }
112}
113
114impl<S> From<S> for Transition<S> {
115    /// Into `into` over [`Transition::DEFAULT_FADE`], from the start of
116    /// what it plays.
117    fn from(into: S) -> Self {
118        Self {
119            into,
120            fade: Self::DEFAULT_FADE,
121            entering_at: 0.0,
122            restarted: false,
123        }
124    }
125}