1use crate::{
25 animation::{AnimationPlayer, AnimationPoseExt},
26 core::{
27 pool::Handle, reflect::prelude::*, type_traits::prelude::*, variable::InheritableVariable,
28 visitor::prelude::*,
29 },
30 define_widget_deref,
31 message::{KeyCode, MouseButton, UiMessage},
32 widget::{Widget, WidgetBuilder, WidgetMessage},
33 BuildContext, Control, UiNode, UserInterface,
34};
35use fyrox_animation::machine::Parameter;
36use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
37use fyrox_graph::SceneGraph;
38use strum_macros::{AsRefStr, EnumString, VariantNames};
39
40pub type RootMotionSettings = crate::generic_animation::RootMotionSettings<Handle<UiNode>>;
42pub type PoseNode = crate::generic_animation::machine::PoseNode<Handle<UiNode>>;
44pub type PlayAnimation =
46 crate::generic_animation::machine::node::play::PlayAnimation<Handle<UiNode>>;
47pub type BlendAnimations =
49 crate::generic_animation::machine::node::blend::BlendAnimations<Handle<UiNode>>;
50pub type BlendAnimationsByIndex =
52 crate::generic_animation::machine::node::blend::BlendAnimationsByIndex<Handle<UiNode>>;
53pub type BlendPose = crate::generic_animation::machine::node::blend::BlendPose<Handle<UiNode>>;
55pub type IndexedBlendInput =
57 crate::generic_animation::machine::node::blend::IndexedBlendInput<Handle<UiNode>>;
58pub type BlendSpace =
60 crate::generic_animation::machine::node::blendspace::BlendSpace<Handle<UiNode>>;
61pub type BlendSpacePoint =
63 crate::generic_animation::machine::node::blendspace::BlendSpacePoint<Handle<UiNode>>;
64pub type LayerMask = crate::generic_animation::machine::mask::LayerMask<Handle<UiNode>>;
66pub type Event = crate::generic_animation::machine::event::Event<Handle<UiNode>>;
68pub type Machine = crate::generic_animation::machine::Machine<Handle<UiNode>>;
70pub type MachineLayer = crate::generic_animation::machine::MachineLayer<Handle<UiNode>>;
72pub type Transition = crate::generic_animation::machine::transition::Transition<Handle<UiNode>>;
74pub type State = crate::generic_animation::machine::state::State<Handle<UiNode>>;
76pub type BasePoseNode = crate::generic_animation::machine::node::BasePoseNode<Handle<UiNode>>;
78pub type StateAction = crate::generic_animation::machine::state::StateAction<Handle<UiNode>>;
80pub type StateActionWrapper =
82 crate::generic_animation::machine::state::StateActionWrapper<Handle<UiNode>>;
83pub type LogicNode = crate::generic_animation::machine::transition::LogicNode<Handle<UiNode>>;
85pub type AndNode = crate::generic_animation::machine::transition::AndNode<Handle<UiNode>>;
87pub type XorNode = crate::generic_animation::machine::transition::XorNode<Handle<UiNode>>;
89pub type OrNode = crate::generic_animation::machine::transition::OrNode<Handle<UiNode>>;
91pub type NotNode = crate::generic_animation::machine::transition::NotNode<Handle<UiNode>>;
93pub type LayerAnimationEventsCollection =
95 crate::generic_animation::machine::layer::LayerAnimationEventsCollection<Handle<UiNode>>;
96pub type AnimationEventsSource =
98 crate::generic_animation::machine::layer::AnimationEventsSource<Handle<UiNode>>;
99
100pub mod prelude {
102 pub use super::{
103 AndNode, AnimationBlendingStateMachine, AnimationBlendingStateMachineBuilder,
104 AnimationEventsSource, BasePoseNode, BlendAnimations, BlendAnimationsByIndex, BlendPose,
105 BlendSpace, BlendSpacePoint, Event, IndexedBlendInput, LayerAnimationEventsCollection,
106 LayerMask, LogicNode, Machine, MachineLayer, NotNode, OrNode, PlayAnimation, PoseNode,
107 RootMotionSettings, State, StateAction, StateActionWrapper, Transition, XorNode,
108 };
109 pub use crate::generic_animation::machine::{
110 node::AnimationEventCollectionStrategy,
111 parameter::{Parameter, ParameterContainer, ParameterDefinition, PoseWeight},
112 };
113}
114
115#[derive(Visit, Reflect, Clone, Debug, Default, ComponentProvider, TypeUuidProvider)]
125#[type_uuid(id = "4b08c753-2a10-41e3-8fb2-4fd0517e86bc")]
126#[reflect(derived_type = "UiNode")]
127pub struct AnimationBlendingStateMachine {
128 widget: Widget,
129 #[component(include)]
130 machine: InheritableVariable<Machine>,
131 #[component(include)]
132 animation_player: InheritableVariable<Handle<UiNode>>,
133}
134
135impl ConstructorProvider<UiNode, UserInterface> for AnimationBlendingStateMachine {
136 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
137 GraphNodeConstructor::new::<Self>()
138 .with_variant("Animation Blending State Machine", |ui| {
139 AnimationBlendingStateMachineBuilder::new(
140 WidgetBuilder::new().with_name("Animation Blending State Machine"),
141 )
142 .build(&mut ui.build_ctx())
143 .to_base()
144 .into()
145 })
146 .with_group("Animation")
147 }
148}
149
150impl AnimationBlendingStateMachine {
151 pub fn set_machine(&mut self, machine: Machine) {
153 self.machine.set_value_and_mark_modified(machine);
154 }
155
156 pub fn machine(&self) -> &InheritableVariable<Machine> {
158 &self.machine
159 }
160
161 pub fn machine_mut(&mut self) -> &mut InheritableVariable<Machine> {
163 &mut self.machine
164 }
165
166 pub fn set_animation_player(&mut self, animation_player: Handle<UiNode>) {
169 self.animation_player
170 .set_value_and_mark_modified(animation_player);
171 }
172
173 pub fn animation_player(&self) -> Handle<UiNode> {
175 *self.animation_player
176 }
177}
178
179define_widget_deref!(AnimationBlendingStateMachine);
180
181impl Control for AnimationBlendingStateMachine {
182 fn update(&mut self, dt: f32, ui: &mut UserInterface) {
183 if let Ok(animation_player) = ui
184 .nodes
185 .try_get_component_of_type_mut::<AnimationPlayer>(*self.animation_player)
186 {
187 animation_player.set_auto_apply(false);
190
191 let pose = self
192 .machine
193 .get_value_mut_silent()
194 .evaluate_pose(animation_player.animations.get_value_mut_silent(), dt);
195
196 pose.apply(ui);
197 }
198 }
199
200 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
201 self.widget.handle_routed_message(ui, message)
202 }
203}
204
205pub struct AnimationBlendingStateMachineBuilder {
207 widget_builder: WidgetBuilder,
208 machine: Machine,
209 animation_player: Handle<UiNode>,
210}
211
212impl AnimationBlendingStateMachineBuilder {
213 pub fn new(widget_builder: WidgetBuilder) -> Self {
215 Self {
216 widget_builder,
217 machine: Default::default(),
218 animation_player: Default::default(),
219 }
220 }
221
222 pub fn with_machine(mut self, machine: Machine) -> Self {
224 self.machine = machine;
225 self
226 }
227
228 pub fn with_animation_player(mut self, animation_player: Handle<UiNode>) -> Self {
230 self.animation_player = animation_player;
231 self
232 }
233
234 pub fn build_absm(self, ctx: &BuildContext) -> AnimationBlendingStateMachine {
236 AnimationBlendingStateMachine {
237 widget: self.widget_builder.with_need_update(true).build(ctx),
238 machine: self.machine.into(),
239 animation_player: self.animation_player.into(),
240 }
241 }
242
243 pub fn build_node(self, ctx: &BuildContext) -> UiNode {
245 UiNode::new(self.build_absm(ctx))
246 }
247
248 pub fn build(self, ctx: &mut BuildContext) -> Handle<AnimationBlendingStateMachine> {
250 ctx.add(self.build_absm(ctx))
251 }
252}
253
254#[derive(
255 Visit,
256 Reflect,
257 Clone,
258 Debug,
259 Default,
260 PartialEq,
261 TypeUuidProvider,
262 AsRefStr,
263 EnumString,
264 VariantNames,
265)]
266#[type_uuid(id = "291e8734-47df-408e-8b2c-57bfb941d8ec")]
267pub enum EventKind {
268 #[default]
269 MouseEnter,
270 MouseLeave,
271 MouseMove,
272 MouseDown(MouseButton),
273 MouseUp(MouseButton),
274 MouseWheel,
275 KeyDown(KeyCode),
276 KeyUp(KeyCode),
277 Focus,
278 Unfocus,
279 TouchStarted,
280 TouchEnded,
281 TouchMoved,
282 TouchCancelled,
283 DoubleTap,
284}
285
286#[derive(Visit, Reflect, Clone, Debug, Default, PartialEq, TypeUuidProvider)]
287#[type_uuid(id = "15f306b8-3bb8-4b35-87bd-6e9e5d748454")]
288pub struct EventAction {
289 kind: EventKind,
290 parameter_name: String,
291 parameter_value: Parameter,
292}
293
294#[derive(Visit, Reflect, Clone, Debug, Default, ComponentProvider, TypeUuidProvider)]
296#[type_uuid(id = "15f306b8-3bb8-4b35-87bd-6e9e5d748455")]
297#[reflect(derived_type = "UiNode")]
298pub struct AbsmEventProvider {
299 widget: Widget,
300 actions: InheritableVariable<Vec<EventAction>>,
301 absm: InheritableVariable<Handle<UiNode>>,
302}
303
304impl ConstructorProvider<UiNode, UserInterface> for AbsmEventProvider {
305 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
306 GraphNodeConstructor::new::<Self>()
307 .with_variant("Absm Event Provider", |ui| {
308 AbsmEventProviderBuilder::new(WidgetBuilder::new().with_name("Absm Event Provider"))
309 .build(&mut ui.build_ctx())
310 .to_base()
311 .into()
312 })
313 .with_group("Animation")
314 }
315}
316
317define_widget_deref!(AbsmEventProvider);
318
319impl AbsmEventProvider {
320 fn on_event(&self, ui: &mut UserInterface, kind: EventKind) {
321 let Some(action) = self.actions.iter().find(|a| a.kind == kind) else {
322 return;
323 };
324
325 let Ok(absm) = ui.try_get_mut_of_type::<AnimationBlendingStateMachine>(*self.absm) else {
326 return;
327 };
328
329 absm.machine_mut()
330 .set_parameter(&action.parameter_name, action.parameter_value);
331 }
332}
333
334impl Control for AbsmEventProvider {
335 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
336 self.widget.handle_routed_message(ui, message);
337
338 let Some(msg) = message.data::<WidgetMessage>() else {
339 return;
340 };
341
342 use WidgetMessage as Msg;
343 match msg {
344 Msg::MouseDown { button, .. } => self.on_event(ui, EventKind::MouseDown(*button)),
345 Msg::MouseUp { button, .. } => self.on_event(ui, EventKind::MouseUp(*button)),
346 Msg::MouseMove { .. } => self.on_event(ui, EventKind::MouseMove),
347 Msg::MouseWheel { .. } => self.on_event(ui, EventKind::MouseWheel),
348 Msg::MouseLeave => self.on_event(ui, EventKind::MouseLeave),
349 Msg::MouseEnter => self.on_event(ui, EventKind::MouseEnter),
350 Msg::Focus => self.on_event(ui, EventKind::Focus),
351 Msg::Unfocus => self.on_event(ui, EventKind::Unfocus),
352 Msg::TouchStarted { .. } => self.on_event(ui, EventKind::TouchStarted),
353 Msg::TouchEnded { .. } => self.on_event(ui, EventKind::TouchEnded),
354 Msg::TouchMoved { .. } => self.on_event(ui, EventKind::TouchMoved),
355 Msg::TouchCancelled { .. } => self.on_event(ui, EventKind::TouchCancelled),
356 Msg::DoubleTap { .. } => self.on_event(ui, EventKind::DoubleTap),
357 Msg::KeyUp(key) => self.on_event(ui, EventKind::KeyUp(*key)),
358 Msg::KeyDown(key) => self.on_event(ui, EventKind::KeyDown(*key)),
359 _ => (),
360 }
361 }
362}
363
364pub struct AbsmEventProviderBuilder {
365 widget_builder: WidgetBuilder,
366 actions: Vec<EventAction>,
367 absm: Handle<UiNode>,
368}
369
370impl AbsmEventProviderBuilder {
371 pub fn new(widget_builder: WidgetBuilder) -> Self {
372 Self {
373 widget_builder,
374 actions: Default::default(),
375 absm: Default::default(),
376 }
377 }
378
379 pub fn with_actions(mut self, actions: Vec<EventAction>) -> Self {
380 self.actions = actions;
381 self
382 }
383
384 pub fn with_absm(mut self, absm: Handle<UiNode>) -> Self {
385 self.absm = absm;
386 self
387 }
388
389 pub fn build(self, ctx: &mut BuildContext) -> Handle<AbsmEventProvider> {
390 let provider = AbsmEventProvider {
391 widget: self.widget_builder.build(ctx),
392 actions: self.actions.into(),
393 absm: self.absm.into(),
394 };
395
396 ctx.add(provider)
397 }
398}
399
400#[cfg(test)]
401mod test {
402 use crate::absm::AnimationBlendingStateMachineBuilder;
403 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
404
405 #[test]
406 fn test_deletion() {
407 test_widget_deletion(|ctx| {
408 AnimationBlendingStateMachineBuilder::new(WidgetBuilder::new()).build(ctx)
409 });
410 }
411}