| 1 | /**************************************************************************/ |
| 2 | /* animation_node_state_machine.h */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #ifndef ANIMATION_NODE_STATE_MACHINE_H |
| 32 | #define ANIMATION_NODE_STATE_MACHINE_H |
| 33 | |
| 34 | #include "core/math/expression.h" |
| 35 | #include "scene/animation/animation_tree.h" |
| 36 | |
| 37 | class AnimationNodeStateMachineTransition : public Resource { |
| 38 | GDCLASS(AnimationNodeStateMachineTransition, Resource); |
| 39 | |
| 40 | public: |
| 41 | enum SwitchMode { |
| 42 | SWITCH_MODE_IMMEDIATE, |
| 43 | SWITCH_MODE_SYNC, |
| 44 | SWITCH_MODE_AT_END, |
| 45 | }; |
| 46 | |
| 47 | enum AdvanceMode { |
| 48 | ADVANCE_MODE_DISABLED, |
| 49 | ADVANCE_MODE_ENABLED, |
| 50 | ADVANCE_MODE_AUTO, |
| 51 | }; |
| 52 | |
| 53 | private: |
| 54 | SwitchMode switch_mode = SWITCH_MODE_IMMEDIATE; |
| 55 | AdvanceMode advance_mode = ADVANCE_MODE_ENABLED; |
| 56 | StringName advance_condition; |
| 57 | StringName advance_condition_name; |
| 58 | float xfade_time = 0.0; |
| 59 | Ref<Curve> xfade_curve; |
| 60 | bool reset = true; |
| 61 | int priority = 1; |
| 62 | String advance_expression; |
| 63 | |
| 64 | friend class AnimationNodeStateMachinePlayback; |
| 65 | Ref<Expression> expression; |
| 66 | |
| 67 | protected: |
| 68 | static void _bind_methods(); |
| 69 | |
| 70 | public: |
| 71 | void set_switch_mode(SwitchMode p_mode); |
| 72 | SwitchMode get_switch_mode() const; |
| 73 | |
| 74 | void set_advance_mode(AdvanceMode p_mode); |
| 75 | AdvanceMode get_advance_mode() const; |
| 76 | |
| 77 | void set_advance_condition(const StringName &p_condition); |
| 78 | StringName get_advance_condition() const; |
| 79 | |
| 80 | StringName get_advance_condition_name() const; |
| 81 | |
| 82 | void set_advance_expression(const String &p_expression); |
| 83 | String get_advance_expression() const; |
| 84 | |
| 85 | void set_xfade_time(float p_xfade); |
| 86 | float get_xfade_time() const; |
| 87 | |
| 88 | void set_reset(bool p_reset); |
| 89 | bool is_reset() const; |
| 90 | |
| 91 | void set_xfade_curve(const Ref<Curve> &p_curve); |
| 92 | Ref<Curve> get_xfade_curve() const; |
| 93 | |
| 94 | void set_priority(int p_priority); |
| 95 | int get_priority() const; |
| 96 | |
| 97 | AnimationNodeStateMachineTransition(); |
| 98 | }; |
| 99 | |
| 100 | VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode) |
| 101 | VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::AdvanceMode) |
| 102 | |
| 103 | class AnimationNodeStateMachinePlayback; |
| 104 | |
| 105 | class AnimationNodeStateMachine : public AnimationRootNode { |
| 106 | GDCLASS(AnimationNodeStateMachine, AnimationRootNode); |
| 107 | |
| 108 | public: |
| 109 | enum StateMachineType { |
| 110 | STATE_MACHINE_TYPE_ROOT, |
| 111 | STATE_MACHINE_TYPE_NESTED, |
| 112 | STATE_MACHINE_TYPE_GROUPED, |
| 113 | }; |
| 114 | |
| 115 | private: |
| 116 | friend class AnimationNodeStateMachinePlayback; |
| 117 | |
| 118 | StateMachineType state_machine_type = STATE_MACHINE_TYPE_ROOT; |
| 119 | |
| 120 | struct State { |
| 121 | Ref<AnimationRootNode> node; |
| 122 | Vector2 position; |
| 123 | }; |
| 124 | |
| 125 | HashMap<StringName, State> states; |
| 126 | bool allow_transition_to_self = false; |
| 127 | bool reset_ends = false; |
| 128 | |
| 129 | struct Transition { |
| 130 | StringName from; |
| 131 | StringName to; |
| 132 | Ref<AnimationNodeStateMachineTransition> transition; |
| 133 | }; |
| 134 | |
| 135 | Vector<Transition> transitions; |
| 136 | |
| 137 | StringName playback = "playback" ; |
| 138 | bool updating_transitions = false; |
| 139 | |
| 140 | Vector2 graph_offset; |
| 141 | |
| 142 | void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition); |
| 143 | void _rename_transitions(const StringName &p_name, const StringName &p_new_name); |
| 144 | bool _can_connect(const StringName &p_name); |
| 145 | |
| 146 | protected: |
| 147 | static void _bind_methods(); |
| 148 | |
| 149 | bool _set(const StringName &p_name, const Variant &p_value); |
| 150 | bool _get(const StringName &p_name, Variant &r_ret) const; |
| 151 | void _get_property_list(List<PropertyInfo> *p_list) const; |
| 152 | void _validate_property(PropertyInfo &p_property) const; |
| 153 | |
| 154 | bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const; |
| 155 | |
| 156 | virtual void _tree_changed() override; |
| 157 | virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override; |
| 158 | virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override; |
| 159 | |
| 160 | virtual void reset_state() override; |
| 161 | |
| 162 | public: |
| 163 | StringName start_node = "Start" ; |
| 164 | StringName end_node = "End" ; |
| 165 | |
| 166 | virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; |
| 167 | virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; |
| 168 | virtual bool is_parameter_read_only(const StringName &p_parameter) const override; |
| 169 | |
| 170 | void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); |
| 171 | void replace_node(const StringName &p_name, Ref<AnimationNode> p_node); |
| 172 | Ref<AnimationNode> get_node(const StringName &p_name) const; |
| 173 | void remove_node(const StringName &p_name); |
| 174 | void rename_node(const StringName &p_name, const StringName &p_new_name); |
| 175 | bool has_node(const StringName &p_name) const; |
| 176 | StringName get_node_name(const Ref<AnimationNode> &p_node) const; |
| 177 | void get_node_list(List<StringName> *r_nodes) const; |
| 178 | |
| 179 | void set_node_position(const StringName &p_name, const Vector2 &p_position); |
| 180 | Vector2 get_node_position(const StringName &p_name) const; |
| 181 | |
| 182 | virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override; |
| 183 | |
| 184 | bool has_transition(const StringName &p_from, const StringName &p_to) const; |
| 185 | bool has_transition_from(const StringName &p_from) const; |
| 186 | bool has_transition_to(const StringName &p_to) const; |
| 187 | int find_transition(const StringName &p_from, const StringName &p_to) const; |
| 188 | Vector<int> find_transition_from(const StringName &p_from) const; |
| 189 | Vector<int> find_transition_to(const StringName &p_to) const; |
| 190 | void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition); |
| 191 | Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const; |
| 192 | StringName get_transition_from(int p_transition) const; |
| 193 | StringName get_transition_to(int p_transition) const; |
| 194 | int get_transition_count() const; |
| 195 | bool is_transition_across_group(int p_transition) const; |
| 196 | void remove_transition_by_index(const int p_transition); |
| 197 | void remove_transition(const StringName &p_from, const StringName &p_to); |
| 198 | |
| 199 | void set_state_machine_type(StateMachineType p_state_machine_type); |
| 200 | StateMachineType get_state_machine_type() const; |
| 201 | |
| 202 | void set_allow_transition_to_self(bool p_enable); |
| 203 | bool is_allow_transition_to_self() const; |
| 204 | |
| 205 | void set_reset_ends(bool p_enable); |
| 206 | bool are_ends_reset() const; |
| 207 | |
| 208 | bool can_edit_node(const StringName &p_name) const; |
| 209 | |
| 210 | void set_graph_offset(const Vector2 &p_offset); |
| 211 | Vector2 get_graph_offset() const; |
| 212 | |
| 213 | virtual double _process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only = false) override; |
| 214 | virtual String get_caption() const override; |
| 215 | |
| 216 | virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) const override; |
| 217 | |
| 218 | AnimationNodeStateMachine(); |
| 219 | }; |
| 220 | |
| 221 | VARIANT_ENUM_CAST(AnimationNodeStateMachine::StateMachineType); |
| 222 | |
| 223 | class AnimationNodeStateMachinePlayback : public Resource { |
| 224 | GDCLASS(AnimationNodeStateMachinePlayback, Resource); |
| 225 | |
| 226 | friend class AnimationNodeStateMachine; |
| 227 | |
| 228 | struct AStarCost { |
| 229 | float distance = 0.0; |
| 230 | StringName prev; |
| 231 | }; |
| 232 | |
| 233 | struct TransitionInfo { |
| 234 | StringName from; |
| 235 | StringName to; |
| 236 | StringName next; |
| 237 | }; |
| 238 | |
| 239 | struct NextInfo { |
| 240 | StringName node; |
| 241 | double xfade; |
| 242 | Ref<Curve> curve; |
| 243 | AnimationNodeStateMachineTransition::SwitchMode switch_mode; |
| 244 | bool is_reset; |
| 245 | }; |
| 246 | |
| 247 | struct ChildStateMachineInfo { |
| 248 | Ref<AnimationNodeStateMachinePlayback> playback; |
| 249 | Vector<StringName> path; |
| 250 | bool is_reset = false; |
| 251 | }; |
| 252 | |
| 253 | Ref<AnimationNodeStateMachineTransition> default_transition; |
| 254 | String base_path; |
| 255 | |
| 256 | double len_fade_from = 0.0; |
| 257 | double pos_fade_from = 0.0; |
| 258 | |
| 259 | double len_current = 0.0; |
| 260 | double pos_current = 0.0; |
| 261 | |
| 262 | StringName current; |
| 263 | Ref<Curve> current_curve; |
| 264 | |
| 265 | Ref<AnimationNodeStateMachineTransition> group_start_transition; |
| 266 | Ref<AnimationNodeStateMachineTransition> group_end_transition; |
| 267 | |
| 268 | StringName fading_from; |
| 269 | float fading_time = 0.0; |
| 270 | float fading_pos = 0.0; |
| 271 | |
| 272 | Vector<StringName> path; |
| 273 | bool playing = false; |
| 274 | |
| 275 | StringName start_request; |
| 276 | StringName travel_request; |
| 277 | bool reset_request = false; |
| 278 | bool reset_request_on_teleport = false; |
| 279 | bool _reset_request_for_fading_from = false; |
| 280 | bool next_request = false; |
| 281 | bool stop_request = false; |
| 282 | bool teleport_request = false; |
| 283 | |
| 284 | bool is_grouped = false; |
| 285 | |
| 286 | void _travel_main(const StringName &p_state, bool p_reset_on_teleport = true); |
| 287 | void _start_main(const StringName &p_state, bool p_reset = true); |
| 288 | void _next_main(); |
| 289 | void _stop_main(); |
| 290 | |
| 291 | bool _make_travel_path(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, Vector<StringName> &r_path, bool p_test_only); |
| 292 | String _validate_path(AnimationNodeStateMachine *p_state_machine, const String &p_path); |
| 293 | bool _travel(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only); |
| 294 | void _start(AnimationNodeStateMachine *p_state_machine); |
| 295 | |
| 296 | void _clear_path_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only); |
| 297 | bool _travel_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only); |
| 298 | void _start_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_test_only); |
| 299 | |
| 300 | double process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only); |
| 301 | double _process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only); |
| 302 | |
| 303 | bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const; |
| 304 | bool _transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only); |
| 305 | NextInfo _find_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine) const; |
| 306 | Ref<AnimationNodeStateMachineTransition> _check_group_transition(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const AnimationNodeStateMachine::Transition &p_transition, Ref<AnimationNodeStateMachine> &r_state_machine, bool &r_bypass) const; |
| 307 | bool _can_transition_to_next(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, NextInfo p_next, bool p_test_only); |
| 308 | |
| 309 | void _set_current(AnimationNodeStateMachine *p_state_machine, const StringName &p_state); |
| 310 | void _set_grouped(bool p_is_grouped); |
| 311 | void _set_base_path(const String &p_base_path); |
| 312 | Ref<AnimationNodeStateMachinePlayback> _get_parent_playback(AnimationTree *p_tree) const; |
| 313 | Ref<AnimationNodeStateMachine> _get_parent_state_machine(AnimationTree *p_tree) const; |
| 314 | Ref<AnimationNodeStateMachineTransition> _get_group_start_transition() const; |
| 315 | Ref<AnimationNodeStateMachineTransition> _get_group_end_transition() const; |
| 316 | |
| 317 | TypedArray<StringName> _get_travel_path() const; |
| 318 | |
| 319 | protected: |
| 320 | static void _bind_methods(); |
| 321 | |
| 322 | public: |
| 323 | void travel(const StringName &p_state, bool p_reset_on_teleport = true); |
| 324 | void start(const StringName &p_state, bool p_reset = true); |
| 325 | void next(); |
| 326 | void stop(); |
| 327 | bool is_playing() const; |
| 328 | bool is_end() const; |
| 329 | StringName get_current_node() const; |
| 330 | StringName get_fading_from_node() const; |
| 331 | Vector<StringName> get_travel_path() const; |
| 332 | float get_current_play_pos() const; |
| 333 | float get_current_length() const; |
| 334 | |
| 335 | float get_fade_from_play_pos() const; |
| 336 | float get_fade_from_length() const; |
| 337 | |
| 338 | float get_fading_time() const; |
| 339 | float get_fading_pos() const; |
| 340 | |
| 341 | void clear_path(); |
| 342 | void push_path(const StringName &p_state); |
| 343 | |
| 344 | AnimationNodeStateMachinePlayback(); |
| 345 | }; |
| 346 | |
| 347 | #endif // ANIMATION_NODE_STATE_MACHINE_H |
| 348 | |