fyrox_animation/machine/
state.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! State is a final "container" for animation pose. See [`State`] docs for more info.
22
23use crate::{
24    core::{
25        algebra::Vector2,
26        pool::{Handle, Pool},
27        rand::{self, seq::IteratorRandom},
28        reflect::prelude::*,
29        visitor::prelude::*,
30    },
31    machine::{AnimationPoseSource, ParameterContainer, PoseNode},
32    Animation, AnimationContainer, AnimationPose, EntityId,
33};
34use fxhash::FxHashSet;
35use fyrox_core::uuid::{uuid, Uuid};
36use fyrox_core::{NameProvider, TypeUuidProvider};
37use std::{
38    cell::Ref,
39    ops::{Deref, DerefMut},
40};
41use strum_macros::{AsRefStr, EnumString, VariantNames};
42
43#[doc(hidden)]
44#[derive(Default, Debug, Visit, Reflect, Clone, PartialEq)]
45pub struct StateActionWrapper<T: EntityId>(pub StateAction<T>);
46
47impl<T: EntityId> TypeUuidProvider for StateActionWrapper<T> {
48    fn type_uuid() -> Uuid {
49        uuid!("d686fac8-5cc1-46b1-82a4-7f4438cc078d")
50    }
51}
52
53impl<T: EntityId> Deref for StateActionWrapper<T> {
54    type Target = StateAction<T>;
55
56    fn deref(&self) -> &Self::Target {
57        &self.0
58    }
59}
60
61impl<T: EntityId> DerefMut for StateActionWrapper<T> {
62    fn deref_mut(&mut self) -> &mut Self::Target {
63        &mut self.0
64    }
65}
66
67/// An action, that will be executed by a state. It usually used to rewind, enable/disable animations
68/// when entering or leaving states. This is useful in situations when you have a one-shot animation
69/// and you need to rewind it before when entering some state. For example, you may have looped idle
70/// state and one-shot attack state. In this case, you need to use [`StateAction::RewindAnimation`]
71/// to tell the engine to automatically rewind the animation before using it. Otherwise, when the
72/// transition will happen, the animation could be ended already and you'll get "frozen" animation.
73#[derive(Default, Debug, Visit, Reflect, Clone, PartialEq, VariantNames, EnumString, AsRefStr)]
74pub enum StateAction<T: EntityId> {
75    /// No action.
76    #[default]
77    None,
78    /// Rewinds the animation.
79    RewindAnimation(Handle<Animation<T>>),
80    /// Enables the animation.
81    EnableAnimation(Handle<Animation<T>>),
82    /// Disables the animation.
83    DisableAnimation(Handle<Animation<T>>),
84    /// Enables random animation from the list. It could be useful if you want to add randomization
85    /// to your state machine. For example, you may have few melee attack animations and all of them
86    /// are suitable for every situation, in this case you can add randomization to make attacks less
87    /// predictable.
88    EnableRandomAnimation(Vec<Handle<Animation<T>>>),
89}
90
91impl<T: EntityId> TypeUuidProvider for StateAction<T> {
92    fn type_uuid() -> Uuid {
93        uuid!("c50a15cc-0f63-4409-bbe0-74b9d3e94755")
94    }
95}
96
97impl<T: EntityId> StateAction<T> {
98    /// Applies the action to the given animation container.
99    pub fn apply(&self, animations: &mut AnimationContainer<T>) {
100        match self {
101            StateAction::None => {}
102            StateAction::RewindAnimation(animation) => {
103                if let Some(animation) = animations.try_get_mut(*animation) {
104                    animation.rewind();
105                }
106            }
107            StateAction::EnableAnimation(animation) => {
108                if let Some(animation) = animations.try_get_mut(*animation) {
109                    animation.set_enabled(true);
110                }
111            }
112            StateAction::DisableAnimation(animation) => {
113                if let Some(animation) = animations.try_get_mut(*animation) {
114                    animation.set_enabled(false);
115                }
116            }
117            StateAction::EnableRandomAnimation(animation_handles) => {
118                if let Some(animation) = animation_handles.iter().choose(&mut rand::thread_rng()) {
119                    if let Some(animation) = animations.try_get_mut(*animation) {
120                        animation.set_enabled(true);
121                    }
122                }
123            }
124        }
125    }
126}
127
128/// State is a final "container" for animation pose. It has backing pose node which provides a set of values.
129/// States can be connected with each other using _transitions_, states with transitions form a state graph.
130#[derive(Default, Debug, Visit, Clone, Reflect, PartialEq)]
131pub struct State<T: EntityId> {
132    /// Position of state on the canvas. It is editor-specific data.
133    pub position: Vector2<f32>,
134
135    /// Name of the state.
136    pub name: String,
137
138    /// A set of actions that will be executed when entering the state.
139    #[visit(optional)]
140    pub on_enter_actions: Vec<StateActionWrapper<T>>,
141
142    /// A set of actions that will be executed when leaving the state.
143    #[visit(optional)]
144    pub on_leave_actions: Vec<StateActionWrapper<T>>,
145
146    /// Root node of the state that provides the state with animation data.
147    #[reflect(read_only)]
148    pub root: Handle<PoseNode<T>>,
149}
150
151impl<T: EntityId> NameProvider for State<T> {
152    fn name(&self) -> &str {
153        &self.name
154    }
155}
156
157impl<T: EntityId> State<T> {
158    /// Creates new instance of state with a given pose.
159    pub fn new(name: &str, root: Handle<PoseNode<T>>) -> Self {
160        Self {
161            position: Default::default(),
162            name: name.to_owned(),
163            on_enter_actions: Default::default(),
164            on_leave_actions: Default::default(),
165            root,
166        }
167    }
168
169    /// Returns a final pose of the state.
170    pub fn pose<'a>(&self, nodes: &'a Pool<PoseNode<T>>) -> Option<Ref<'a, AnimationPose<T>>> {
171        nodes.try_borrow(self.root).map(|root| root.pose())
172    }
173
174    /// Collects all animation handles used by this state.
175    pub fn collect_animations(
176        &self,
177        nodes: &Pool<PoseNode<T>>,
178        animations: &mut FxHashSet<Handle<Animation<T>>>,
179    ) {
180        if let Some(root) = nodes.try_borrow(self.root) {
181            root.collect_animations(nodes, animations);
182        }
183    }
184
185    pub(super) fn update(
186        &mut self,
187        nodes: &Pool<PoseNode<T>>,
188        params: &ParameterContainer,
189        animations: &AnimationContainer<T>,
190        dt: f32,
191    ) {
192        if let Some(root) = nodes.try_borrow(self.root) {
193            root.eval_pose(nodes, params, animations, dt);
194        }
195    }
196}