game_toolkit_platformer/anim/graph.rs
1//! [`Graph`]: a compiled, validated state graph: states with clips, transitions by
2//! priority, the parameter template. Build one with [`GraphSpec`](super::GraphSpec).
3
4use super::{Clip, Cond, Name, ParamId, Params};
5
6/// A state, by index into its [`Graph`].
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8pub struct StateId(pub(crate) u16);
9
10/// How fast a state plays its clip.
11#[derive(Clone, Debug, PartialEq)]
12pub enum Rate<P = ParamId> {
13 /// A fixed multiplier.
14 Fixed(f32),
15 /// Scaled by a float parameter's magnitude: `|param| / per`, clamped to `min..=max`
16 /// (a run cycle that keeps pace with the speed; a climb that stops when the hero does).
17 Param {
18 /// The float parameter.
19 param: P,
20 /// The parameter value that plays at rate 1.
21 per: f32,
22 /// Slowest rate.
23 min: f32,
24 /// Fastest rate.
25 max: f32,
26 },
27}
28
29/// A state of the graph.
30#[derive(Clone, Debug, PartialEq)]
31pub struct State {
32 /// Its name.
33 pub name: Name,
34 /// What it plays.
35 pub clip: Clip,
36 /// How fast.
37 pub rate: Rate,
38 /// Seconds after entering during which only lock-breaking transitions are considered.
39 pub lock: f32,
40 /// Where to go when the clip finishes and no transition fired.
41 pub next: Option<StateId>,
42}
43
44/// Where a transition can start.
45#[derive(Clone, Debug, PartialEq)]
46pub enum Source {
47 /// From these states.
48 States(Vec<StateId>),
49 /// From any state except these.
50 Any {
51 /// States it doesn't apply to.
52 except: Vec<StateId>,
53 },
54}
55
56impl Source {
57 /// Whether the transition applies in state `s`.
58 pub fn applies(&self, s: StateId) -> bool {
59 match self {
60 Source::States(v) => v.contains(&s),
61 Source::Any { except } => !except.contains(&s),
62 }
63 }
64}
65
66/// A transition between states.
67#[derive(Clone, Debug, PartialEq)]
68pub struct Transition {
69 /// Where it can start.
70 pub from: Source,
71 /// Where it goes.
72 pub to: StateId,
73 /// When it fires.
74 pub when: Cond,
75 /// Higher goes first; ties in declaration order.
76 pub priority: i32,
77 /// Considered even while the current state is locked.
78 pub breaks_lock: bool,
79 /// May fire into the state it starts from, restarting it.
80 pub restart: bool,
81}
82
83/// A compiled state graph.
84#[derive(Clone, Debug, PartialEq)]
85pub struct Graph {
86 /// The parameters, with their initial values; every animator gets a copy.
87 pub params: Params,
88 /// The states.
89 pub states: Vec<State>,
90 /// The transitions, highest priority first.
91 pub transitions: Vec<Transition>,
92 /// The starting state.
93 pub entry: StateId,
94}
95
96impl Graph {
97 /// The state `id`.
98 pub fn state(&self, id: StateId) -> &State {
99 &self.states[id.0 as usize]
100 }
101
102 /// The state called `name`.
103 pub fn find(&self, name: &str) -> Option<StateId> {
104 self.states
105 .iter()
106 .position(|s| s.name == name)
107 .map(|i| StateId(i as u16))
108 }
109
110 /// Every state id.
111 pub fn ids(&self) -> impl Iterator<Item = StateId> {
112 (0..self.states.len()).map(|i| StateId(i as u16))
113 }
114}