diff options
Diffstat (limited to 'scene/animation')
-rw-r--r-- | scene/animation/animation_blend_tree.cpp | 164 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.h | 42 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 139 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.h | 18 | ||||
-rw-r--r-- | scene/animation/animation_player.cpp | 128 | ||||
-rw-r--r-- | scene/animation/animation_player.h | 7 | ||||
-rw-r--r-- | scene/animation/animation_tree.cpp | 25 | ||||
-rw-r--r-- | scene/animation/animation_tree.h | 4 | ||||
-rw-r--r-- | scene/animation/tween.cpp | 44 | ||||
-rw-r--r-- | scene/animation/tween.h | 5 |
10 files changed, 383 insertions, 193 deletions
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 1ef0774828..a00b1d8ee1 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -229,15 +229,17 @@ AnimationNodeSync::AnimationNodeSync() { //////////////////////////////////////////////////////// void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { - r_list->push_back(PropertyInfo(Variant::BOOL, active)); - r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); + r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort")); r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const { - if (p_parameter == active || p_parameter == prev_active) { + if (p_parameter == request) { + return ONE_SHOT_REQUEST_NONE; + } else if (p_parameter == active) { return false; } else if (p_parameter == time_to_restart) { return -1; @@ -246,6 +248,13 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa } } +bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const { + if (p_parameter == active) { + return true; + } + return false; +} + void AnimationNodeOneShot::set_fadein_time(double p_time) { fade_in = p_time; } @@ -303,41 +312,42 @@ bool AnimationNodeOneShot::has_filter() const { } double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_external_seeking) { + OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request)); bool cur_active = get_parameter(active); - bool cur_prev_active = get_parameter(prev_active); double cur_time = get_parameter(time); double cur_remaining = get_parameter(remaining); double cur_time_to_restart = get_parameter(time_to_restart); - if (!cur_active) { - //make it as if this node doesn't exist, pass input 0 by. - if (cur_prev_active) { - set_parameter(prev_active, false); - } + set_parameter(request, ONE_SHOT_REQUEST_NONE); + + bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE; + if (cur_request == ONE_SHOT_REQUEST_ABORT) { + set_parameter(active, false); + set_parameter(time_to_restart, -1); + return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + } else if (!do_start && !cur_active) { if (cur_time_to_restart >= 0.0 && !p_seek) { cur_time_to_restart -= p_time; if (cur_time_to_restart < 0) { - //restart - set_parameter(active, true); - cur_active = true; + do_start = true; // Restart. } set_parameter(time_to_restart, cur_time_to_restart); } - - return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + if (!do_start) { + return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + } } bool os_seek = p_seek; - if (p_seek) { cur_time = p_time; } - bool do_start = !cur_prev_active; if (do_start) { cur_time = 0; os_seek = true; - set_parameter(prev_active, true); + set_parameter(request, ONE_SHOT_REQUEST_NONE); + set_parameter(active, true); } real_t blend; @@ -375,7 +385,6 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter cur_remaining = os_rem; if (cur_remaining <= 0) { set_parameter(active, false); - set_parameter(prev_active, false); if (autorestart) { double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay; set_parameter(time_to_restart, restart_sec); @@ -419,6 +428,10 @@ void AnimationNodeOneShot::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay"); + BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE); + BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE); + BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT); + BIND_ENUM_CONSTANT(MIX_MODE_BLEND); BIND_ENUM_CONSTANT(MIX_MODE_ADD); } @@ -640,9 +653,10 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con anims += inputs[i].name; } - r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims)); - r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); - r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::STRING, current_state, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); // For interface. + r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately. + r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally. + r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } @@ -650,13 +664,22 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { if (p_parameter == time || p_parameter == prev_xfading) { return 0.0; - } else if (p_parameter == prev || p_parameter == prev_current) { + } else if (p_parameter == prev_index) { return -1; + } else if (p_parameter == transition_request || p_parameter == current_state) { + return String(); } else { return 0; } } +bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const { + if (p_parameter == current_state || p_parameter == current_index) { + return true; + } + return false; +} + String AnimationNodeTransition::get_caption() const { return "Transition"; } @@ -702,6 +725,17 @@ String AnimationNodeTransition::get_input_caption(int p_input) const { return inputs[p_input].name; } +int AnimationNodeTransition::find_input_caption(const String &p_name) const { + int idx = -1; + for (int i = 0; i < MAX_INPUTS; i++) { + if (inputs[i].name == p_name) { + idx = i; + break; + } + } + return idx; +} + void AnimationNodeTransition::set_xfade_time(double p_fade) { xfade_time = p_fade; } @@ -718,35 +752,62 @@ Ref<Curve> AnimationNodeTransition::get_xfade_curve() const { return xfade_curve; } -void AnimationNodeTransition::set_from_start(bool p_from_start) { - from_start = p_from_start; +void AnimationNodeTransition::set_reset(bool p_reset) { + reset = p_reset; } -bool AnimationNodeTransition::is_from_start() const { - return from_start; +bool AnimationNodeTransition::is_reset() const { + return reset; } double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_external_seeking) { - int cur_current = get_parameter(current); - int cur_prev = get_parameter(prev); - int cur_prev_current = get_parameter(prev_current); + String cur_transition_request = get_parameter(transition_request); + int cur_current_index = get_parameter(current_index); + int cur_prev_index = get_parameter(prev_index); double cur_time = get_parameter(time); double cur_prev_xfading = get_parameter(prev_xfading); - bool switched = cur_current != cur_prev_current; + bool switched = false; + bool restart = false; + + if (!cur_transition_request.is_empty()) { + int new_idx = find_input_caption(cur_transition_request); + if (new_idx >= 0) { + if (cur_current_index == new_idx) { + // Transition to same state. + restart = reset; + cur_prev_xfading = 0; + set_parameter(prev_xfading, 0); + cur_prev_index = -1; + set_parameter(prev_index, -1); + } else { + switched = true; + cur_prev_index = cur_current_index; + set_parameter(prev_index, cur_current_index); + } + cur_current_index = new_idx; + set_parameter(current_index, cur_current_index); + set_parameter(current_state, cur_transition_request); + } else { + ERR_PRINT("No such input: '" + cur_transition_request + "'"); + } + cur_transition_request = String(); + set_parameter(transition_request, cur_transition_request); + } - if (switched) { - set_parameter(prev_current, cur_current); - set_parameter(prev, cur_prev_current); + // Special case for restart. + if (restart) { + set_parameter(time, 0); + return blend_input(cur_current_index, 0, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + } - cur_prev = cur_prev_current; + if (switched) { cur_prev_xfading = xfade_time; cur_time = 0; - switched = true; } - if (cur_current < 0 || cur_current >= enabled_inputs || cur_prev >= enabled_inputs) { + if (cur_current_index < 0 || cur_current_index >= enabled_inputs || cur_prev_index >= enabled_inputs) { return 0; } @@ -754,15 +815,15 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex if (sync) { for (int i = 0; i < enabled_inputs; i++) { - if (i != cur_current && i != cur_prev) { + if (i != cur_current_index && i != cur_prev_index) { blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); } } } - if (cur_prev < 0) { // process current animation, check for transition + if (cur_prev_index < 0) { // process current animation, check for transition - rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); if (p_seek) { cur_time = p_time; @@ -770,8 +831,8 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex cur_time += p_time; } - if (inputs[cur_current].auto_advance && rem <= xfade_time) { - set_parameter(current, (cur_current + 1) % enabled_inputs); + if (inputs[cur_current_index].auto_advance && rem <= xfade_time) { + set_parameter(transition_request, get_input_caption((cur_current_index + 1) % enabled_inputs)); } } else { // cross-fading from prev to current @@ -783,21 +844,21 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex // Blend values must be more than CMP_EPSILON to process discrete keys in edge. real_t blend_inv = 1.0 - blend; - if (from_start && !p_seek && switched) { //just switched, seek to start of current - rem = blend_input(cur_current, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); + if (reset && !p_seek && switched) { //just switched, seek to start of current + rem = blend_input(cur_current_index, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } else { - rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); + rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); } if (p_seek) { - blend_input(cur_prev, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); + blend_input(cur_prev_index, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); cur_time = p_time; } else { - blend_input(cur_prev, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); + blend_input(cur_prev_index, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true); cur_time += p_time; cur_prev_xfading -= p_time; if (cur_prev_xfading < 0) { - set_parameter(prev, -1); + set_parameter(prev_index, -1); } } } @@ -829,6 +890,7 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption); ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption); + ClassDB::bind_method(D_METHOD("find_input_caption", "caption"), &AnimationNodeTransition::find_input_caption); ClassDB::bind_method(D_METHOD("set_xfade_time", "time"), &AnimationNodeTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeTransition::get_xfade_time); @@ -836,13 +898,13 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeTransition::set_xfade_curve); ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeTransition::get_xfade_curve); - ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start); - ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start); + ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeTransition::set_reset); + ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeTransition::is_reset); - ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,31,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset"); for (int i = 0; i < MAX_INPUTS; i++) { ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_caption", "get_input_caption", i); diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index e72471202e..a1969bb621 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -96,6 +96,12 @@ class AnimationNodeOneShot : public AnimationNodeSync { GDCLASS(AnimationNodeOneShot, AnimationNodeSync); public: + enum OneShotRequest { + ONE_SHOT_REQUEST_NONE, + ONE_SHOT_REQUEST_FIRE, + ONE_SHOT_REQUEST_ABORT, + }; + enum MixMode { MIX_MODE_BLEND, MIX_MODE_ADD @@ -110,13 +116,8 @@ private: double autorestart_random_delay = 0.0; MixMode mix = MIX_MODE_BLEND; - /* bool active; - bool do_start; - double time; - double remaining;*/ - + StringName request = PNAME("request"); StringName active = PNAME("active"); - StringName prev_active = "prev_active"; StringName time = "time"; StringName remaining = "remaining"; StringName time_to_restart = "time_to_restart"; @@ -127,6 +128,7 @@ protected: public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; + virtual bool is_parameter_read_only(const StringName &p_parameter) const override; virtual String get_caption() const override; @@ -153,6 +155,7 @@ public: AnimationNodeOneShot(); }; +VARIANT_ENUM_CAST(AnimationNodeOneShot::OneShotRequest) VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) class AnimationNodeAdd2 : public AnimationNodeSync { @@ -284,22 +287,19 @@ class AnimationNodeTransition : public AnimationNodeSync { InputData inputs[MAX_INPUTS]; int enabled_inputs = 0; - /* - double prev_xfading; - int prev; - double time; - int current; - int prev_current; */ - - StringName prev_xfading = "prev_xfading"; - StringName prev = "prev"; StringName time = "time"; - StringName current = PNAME("current"); - StringName prev_current = "prev_current"; + StringName prev_xfading = "prev_xfading"; + StringName prev_index = "prev_index"; + StringName current_index = PNAME("current_index"); + StringName current_state = PNAME("current_state"); + StringName transition_request = PNAME("transition_request"); + + StringName prev_frame_current = "pf_current"; + StringName prev_frame_current_idx = "pf_current_idx"; double xfade_time = 0.0; Ref<Curve> xfade_curve; - bool from_start = true; + bool reset = true; void _update_inputs(); @@ -310,6 +310,7 @@ protected: public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; + virtual bool is_parameter_read_only(const StringName &p_parameter) const override; virtual String get_caption() const override; @@ -321,6 +322,7 @@ public: void set_input_caption(int p_input, const String &p_name); String get_input_caption(int p_input) const; + int find_input_caption(const String &p_name) const; void set_xfade_time(double p_fade); double get_xfade_time() const; @@ -328,8 +330,8 @@ public: void set_xfade_curve(const Ref<Curve> &p_curve); Ref<Curve> get_xfade_curve() const; - void set_from_start(bool p_from_start); - bool is_from_start() const; + void set_reset(bool p_reset); + bool is_reset() const; double process(double p_time, bool p_seek, bool p_is_external_seeking) override; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f5df64dbdd..02f1e9f9a6 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -107,6 +107,15 @@ Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const { return xfade_curve; } +void AnimationNodeStateMachineTransition::set_reset(bool p_reset) { + reset = p_reset; + emit_changed(); +} + +bool AnimationNodeStateMachineTransition::is_reset() const { + return reset; +} + void AnimationNodeStateMachineTransition::set_priority(int p_priority) { priority = p_priority; emit_changed(); @@ -132,6 +141,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve); ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve); + ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeStateMachineTransition::set_reset); + ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeStateMachineTransition::is_reset); + ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority); @@ -140,6 +152,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_GROUP("Switch", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode"); @@ -164,18 +179,27 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { //////////////////////////////////////////////////////// -void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { - start_request_travel = true; - start_request = p_state; +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) { + travel_request = p_state; + reset_request_on_teleport = p_reset_on_teleport; stop_request = false; } -void AnimationNodeStateMachinePlayback::start(const StringName &p_state) { - start_request_travel = false; +void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) { + travel_request = StringName(); + reset_request = p_reset; + _start(p_state); +} + +void AnimationNodeStateMachinePlayback::_start(const StringName &p_state) { start_request = p_state; stop_request = false; } +void AnimationNodeStateMachinePlayback::next() { + next_request = true; +} + void AnimationNodeStateMachinePlayback::stop() { stop_request = true; } @@ -188,7 +212,7 @@ StringName AnimationNodeStateMachinePlayback::get_current_node() const { return current; } -StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const { +StringName AnimationNodeStateMachinePlayback::get_fading_from_node() const { return fading_from; } @@ -212,7 +236,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta path.clear(); //a new one will be needed if (current == p_travel) { - return true; //nothing to do + return false; // Will teleport oneself (restart). } Vector2 current_pos = p_state_machine->states[current].position; @@ -323,6 +347,15 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta } double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) { + double rem = _process(p_state_machine, p_time, p_seek, p_is_external_seeking); + start_request = StringName(); + next_request = false; + stop_request = false; + reset_request_on_teleport = false; + return rem; +} + +double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) { if (p_time == -1) { Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; if (anodesm.is_valid()) { @@ -335,14 +368,13 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s //if not playing and it can restart, then restart if (!playing && start_request == StringName()) { if (!stop_request && p_state_machine->start_node) { - start(p_state_machine->start_node); + _start(p_state_machine->start_node); } else { return 0; } } if (playing && stop_request) { - stop_request = false; playing = false; return 0; } @@ -350,42 +382,47 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s bool play_start = false; if (start_request != StringName()) { - if (start_request_travel) { - if (!playing) { - if (!stop_request && p_state_machine->start_node) { - // can restart, just postpone traveling - path.clear(); - current = p_state_machine->start_node; - playing = true; - play_start = true; - } else { - // stopped, invalid state - String node_name = start_request; - start_request = StringName(); //clear start request - ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?"); - } - } else { - if (!_travel(p_state_machine, start_request)) { - // can't travel, then teleport - path.clear(); - current = start_request; - play_start = true; - } - start_request = StringName(); //clear start request - } + // teleport to start + if (p_state_machine->states.has(start_request)) { + path.clear(); + current = start_request; + playing = true; + play_start = true; } else { - // teleport to start - if (p_state_machine->states.has(start_request)) { + StringName node = start_request; + ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + } + } else if (travel_request != StringName()) { + if (!playing) { + if (!stop_request && p_state_machine->start_node) { + // can restart, just postpone traveling path.clear(); - current = start_request; + current = p_state_machine->start_node; playing = true; play_start = true; - start_request = StringName(); //clear start request } else { - StringName node = start_request; - start_request = StringName(); //clear start request - ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + // stopped, invalid state + String node_name = travel_request; + travel_request = StringName(); + ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?"); } + } else { + if (!_travel(p_state_machine, travel_request)) { + // can't travel, then teleport + if (p_state_machine->states.has(travel_request)) { + path.clear(); + if (current != travel_request || reset_request_on_teleport) { + current = travel_request; + play_start = true; + reset_request = reset_request_on_teleport; + } + } else { + StringName node = travel_request; + travel_request = StringName(); + ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + } + } + travel_request = StringName(); } } @@ -396,8 +433,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current = p_state_machine->start_node; } - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true); - pos_current = 0; + if (reset_request) { + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true); + pos_current = 0; + reset_request = false; + } } if (!p_state_machine->states.has(current)) { @@ -421,7 +461,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (current_curve.is_valid()) { fade_blend = current_curve->sample(fade_blend); } - double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + + double rem = do_start ? len_current : p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (fading_from != StringName()) { double fade_blend_inv = 1.0 - fade_blend; @@ -457,6 +498,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s next_xfade = p_state_machine->transitions[i].transition->get_xfade_time(); current_curve = p_state_machine->transitions[i].transition->get_xfade_curve(); switch_mode = p_state_machine->transitions[i].transition->get_switch_mode(); + reset_request = p_state_machine->transitions[i].transition->is_reset(); next = path[0]; } } @@ -513,6 +555,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current_curve = p_state_machine->transitions[auto_advance_to].transition->get_xfade_curve(); next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time(); switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode(); + reset_request = p_state_machine->transitions[auto_advance_to].transition->is_reset(); } } @@ -567,7 +610,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s goto_next = fading_from == StringName(); } - if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped + if (next_request || goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped if (next_xfade) { //time to fade, baby fading_from = current; @@ -591,7 +634,9 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current = next; - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here. + if (reset_request) { + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here. + } if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { pos_current = MIN(pos_current, len_current); p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true); @@ -652,13 +697,15 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima } void AnimationNodeStateMachinePlayback::_bind_methods() { - ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); - ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); + ClassDB::bind_method(D_METHOD("travel", "to_node", "reset_on_teleport"), &AnimationNodeStateMachinePlayback::travel, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("start", "node", "reset"), &AnimationNodeStateMachinePlayback::start, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("next"), &AnimationNodeStateMachinePlayback::next); ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); ClassDB::bind_method(D_METHOD("get_current_play_position"), &AnimationNodeStateMachinePlayback::get_current_play_pos); ClassDB::bind_method(D_METHOD("get_current_length"), &AnimationNodeStateMachinePlayback::get_current_length); + ClassDB::bind_method(D_METHOD("get_fading_from_node"), &AnimationNodeStateMachinePlayback::get_fading_from_node); ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 116589eb2f..1b4e010a06 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -57,6 +57,7 @@ private: StringName advance_condition_name; float xfade_time = 0.0; Ref<Curve> xfade_curve; + bool reset = true; int priority = 1; String advance_expression; @@ -84,6 +85,9 @@ public: void set_xfade_time(float p_xfade); float get_xfade_time() const; + void set_reset(bool p_reset); + bool is_reset() const; + void set_xfade_curve(const Ref<Curve> &p_curve); Ref<Curve> get_xfade_curve() const; @@ -131,10 +135,15 @@ class AnimationNodeStateMachinePlayback : public Resource { bool playing = false; StringName start_request; - bool start_request_travel = false; + StringName travel_request; + bool reset_request = false; + bool reset_request_on_teleport = false; + bool next_request = false; bool stop_request = false; bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel); + void _start(const StringName &p_state); + double _process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking); double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking); @@ -144,12 +153,13 @@ protected: static void _bind_methods(); public: - void travel(const StringName &p_state); - void start(const StringName &p_state); + void travel(const StringName &p_state, bool p_reset_on_teleport = true); + void start(const StringName &p_state, bool p_reset = true); + void next(); void stop(); bool is_playing() const; StringName get_current_node() const; - StringName get_blend_from_node() const; + StringName get_fading_from_node() const; Vector<StringName> get_travel_path() const; float get_current_play_pos() const; float get_current_length() const; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 7f42c8fac3..047997ca09 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -143,8 +143,8 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } else if (name.begins_with("libraries")) { Dictionary d; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - d[animation_libraries[i].name] = animation_libraries[i].library; + for (const AnimationLibraryData &lib : animation_libraries) { + d[lib.name] = lib.library; } r_ret = d; @@ -745,7 +745,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_METHOD: { - if (!nc->node) { + if (!nc->node || is_stopping) { continue; } if (!p_is_current) { @@ -808,7 +808,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_AUDIO: { - if (!nc->node) { + if (!nc->node || is_stopping) { continue; } @@ -915,6 +915,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_ANIMATION: { + if (is_stopping) { + continue; + } + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(nc->node); if (!player) { continue; @@ -1265,13 +1269,13 @@ void AnimationPlayer::_animation_set_cache_update() { bool clear_cache_needed = false; // Update changed and add otherwise - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { - StringName key = animation_libraries[i].name == StringName() ? K.key : StringName(String(animation_libraries[i].name) + "/" + String(K.key)); + for (const AnimationLibraryData &lib : animation_libraries) { + for (const KeyValue<StringName, Ref<Animation>> &K : lib.library->animations) { + StringName key = lib.name == StringName() ? K.key : StringName(String(lib.name) + "/" + String(K.key)); if (!animation_set.has(key)) { AnimationData ad; ad.animation = K.value; - ad.animation_library = animation_libraries[i].name; + ad.animation_library = lib.name; ad.name = key; ad.last_update = animation_set_update_pass; animation_set.insert(ad.name, ad); @@ -1279,11 +1283,11 @@ void AnimationPlayer::_animation_set_cache_update() { AnimationData &ad = animation_set[key]; if (ad.last_update != animation_set_update_pass) { // Was not updated, update. If the animation is duplicated, the second one will be ignored. - if (ad.animation != K.value || ad.animation_library != animation_libraries[i].name) { + if (ad.animation != K.value || ad.animation_library != lib.name) { // Animation changed, update and clear caches. clear_cache_needed = true; ad.animation = K.value; - ad.animation_library = animation_libraries[i].name; + ad.animation_library = lib.name; } ad.last_update = animation_set_update_pass; @@ -1401,11 +1405,11 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref int insert_pos = 0; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - ERR_FAIL_COND_V_MSG(animation_libraries[i].name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name)); - ERR_FAIL_COND_V_MSG(animation_libraries[i].library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + animation_libraries[i].name.operator String() + "'."); + for (const AnimationLibraryData &lib : animation_libraries) { + ERR_FAIL_COND_V_MSG(lib.name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name)); + ERR_FAIL_COND_V_MSG(lib.library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + lib.name.operator String() + "'."); - if (animation_libraries[i].name.operator String() >= p_name.operator String()) { + if (lib.name.operator String() >= p_name.operator String()) { break; } @@ -1464,21 +1468,21 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S #endif bool found = false; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - ERR_FAIL_COND_MSG(animation_libraries[i].name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name)); - if (animation_libraries[i].name == p_name) { + for (AnimationLibraryData &lib : animation_libraries) { + ERR_FAIL_COND_MSG(lib.name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name)); + if (lib.name == p_name) { found = true; - animation_libraries[i].name = p_new_name; + lib.name = p_new_name; // rename connections - animation_libraries[i].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); - animation_libraries[i].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); - animation_libraries[i].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); + lib.library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); + lib.library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); + lib.library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); - animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name)); + lib.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); + lib.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name)); + lib.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name)); - for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { + for (const KeyValue<StringName, Ref<Animation>> &K : lib.library->animations) { StringName old_name = p_name == StringName() ? K.key : StringName(String(p_name) + "/" + String(K.key)); StringName new_name = p_new_name == StringName() ? K.key : StringName(String(p_new_name) + "/" + String(K.key)); _rename_animation(old_name, new_name); @@ -1498,8 +1502,8 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S } bool AnimationPlayer::has_animation_library(const StringName &p_name) const { - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - if (animation_libraries[i].name == p_name) { + for (const AnimationLibraryData &lib : animation_libraries) { + if (lib.name == p_name) { return true; } } @@ -1508,9 +1512,9 @@ bool AnimationPlayer::has_animation_library(const StringName &p_name) const { } Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p_name) const { - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - if (animation_libraries[i].name == p_name) { - return animation_libraries[i].library; + for (const AnimationLibraryData &lib : animation_libraries) { + if (lib.name == p_name) { + return lib.library; } } ERR_FAIL_V(Ref<AnimationLibrary>()); @@ -1518,15 +1522,15 @@ Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p TypedArray<StringName> AnimationPlayer::_get_animation_library_list() const { TypedArray<StringName> ret; - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - ret.push_back(animation_libraries[i].name); + for (const AnimationLibraryData &lib : animation_libraries) { + ret.push_back(lib.name); } return ret; } void AnimationPlayer::get_animation_library_list(List<StringName> *p_libraries) const { - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - p_libraries->push_back(animation_libraries[i].name); + for (const AnimationLibraryData &lib : animation_libraries) { + p_libraries->push_back(lib.name); } } @@ -1658,7 +1662,7 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa } if (get_current_animation() != p_name) { - _stop_playing_caches(); + _stop_playing_caches(false); } c.current.from = &animation_set[name]; @@ -1731,18 +1735,12 @@ String AnimationPlayer::get_assigned_animation() const { return playback.assigned; } -void AnimationPlayer::stop(bool p_reset) { - _stop_playing_caches(); - Playback &c = playback; - c.blend.clear(); - if (p_reset) { - c.current.from = nullptr; - c.current.speed_scale = 1; - c.current.pos = 0; - } - _set_process(false); - queued.clear(); - playing = false; +void AnimationPlayer::pause() { + _stop_internal(false, false); +} + +void AnimationPlayer::stop(bool p_keep_state) { + _stop_internal(true, p_keep_state); } void AnimationPlayer::set_speed_scale(float p_speed) { @@ -1814,7 +1812,7 @@ void AnimationPlayer::_animation_changed(const StringName &p_name) { } } -void AnimationPlayer::_stop_playing_caches() { +void AnimationPlayer::_stop_playing_caches(bool p_reset) { for (TrackNodeCache *E : playing_caches) { if (E->node && E->audio_playing) { E->node->call(SNAME("stop")); @@ -1824,7 +1822,12 @@ void AnimationPlayer::_stop_playing_caches() { if (!player) { continue; } - player->stop(); + + if (p_reset) { + player->stop(); + } else { + player->pause(); + } } } @@ -1836,7 +1839,7 @@ void AnimationPlayer::_node_removed(Node *p_node) { } void AnimationPlayer::clear_caches() { - _stop_playing_caches(); + _stop_playing_caches(true); node_cache_map.clear(); @@ -1957,6 +1960,26 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) { processing = p_process; } +void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) { + _stop_playing_caches(p_reset); + Playback &c = playback; + c.blend.clear(); + if (p_reset) { + if (p_keep_state) { + c.current.pos = 0; + } else { + is_stopping = true; + seek(0, true); + is_stopping = false; + } + c.current.from = nullptr; + c.current.speed_scale = 1; + } + _set_process(false); + queued.clear(); + playing = false; +} + void AnimationPlayer::animation_set_next(const StringName &p_animation, const StringName &p_next) { ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation)); animation_set[p_animation].next = p_next; @@ -2081,7 +2104,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values(); old_values->restore(); - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Animation Apply Reset")); ur->add_do_method(new_values.ptr(), "restore"); ur->add_undo_method(old_values.ptr(), "restore"); @@ -2119,7 +2142,8 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(""), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("stop", "reset"), &AnimationPlayer::stop, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause); + ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing); ClassDB::bind_method(D_METHOD("set_current_animation", "anim"), &AnimationPlayer::set_current_animation); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index f431253876..7e7d12f982 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -192,6 +192,7 @@ private: uint64_t accum_pass = 1; float speed_scale = 1.0; double default_blend_time = 0.0; + bool is_stopping = false; struct AnimationData { String name; @@ -277,7 +278,7 @@ private: void _animation_process(double p_delta); void _node_removed(Node *p_node); - void _stop_playing_caches(); + void _stop_playing_caches(bool p_reset); // bind helpers Vector<String> _get_animation_list() const { @@ -294,6 +295,7 @@ private: void _animation_changed(const StringName &p_name); void _set_process(bool p_process, bool p_force = false); + void _stop_internal(bool p_reset, bool p_keep_state); bool playing = false; @@ -346,7 +348,8 @@ public: void queue(const StringName &p_name); Vector<String> get_queue(); void clear_queue(); - void stop(bool p_reset = true); + void pause(); + void stop(bool p_keep_state = false); bool is_playing() const; String get_current_animation() const; void set_current_animation(const String &p_anim); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index ab341797c7..077a5696bb 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -54,13 +54,19 @@ Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter return ret; } +bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const { + bool ret = false; + GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret); + return ret; +} + void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND(!state); ERR_FAIL_COND(!state->tree->property_parent_map.has(base_path)); ERR_FAIL_COND(!state->tree->property_parent_map[base_path].has(p_name)); StringName path = state->tree->property_parent_map[base_path][p_name]; - state->tree->property_map[path] = p_value; + state->tree->property_map[path].first = p_value; } Variant AnimationNode::get_parameter(const StringName &p_name) const { @@ -69,7 +75,7 @@ Variant AnimationNode::get_parameter(const StringName &p_name) const { ERR_FAIL_COND_V(!state->tree->property_parent_map[base_path].has(p_name), Variant()); StringName path = state->tree->property_parent_map[base_path][p_name]; - return state->tree->property_map[path]; + return state->tree->property_map[path].first; } void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) { @@ -427,6 +433,7 @@ void AnimationNode::_bind_methods() { GDVIRTUAL_BIND(_get_parameter_list); GDVIRTUAL_BIND(_get_child_by_name, "name"); GDVIRTUAL_BIND(_get_parameter_default_value, "parameter"); + GDVIRTUAL_BIND(_is_parameter_read_only, "parameter"); GDVIRTUAL_BIND(_process, "time", "seek", "is_external_seeking"); GDVIRTUAL_BIND(_get_caption); GDVIRTUAL_BIND(_has_filter); @@ -747,7 +754,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { if (has_reset_anim) { int rt = reset_anim->find_track(path, track_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - track_bezier->init_value = reset_anim->track_get_key_value(rt, 0); + track_bezier->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; } } } break; @@ -1889,7 +1896,10 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A StringName key = pinfo.name; if (!property_map.has(p_base_path + key)) { - property_map[p_base_path + key] = node->get_parameter_default_value(key); + Pair<Variant, bool> param; + param.first = node->get_parameter_default_value(key); + param.second = node->is_parameter_read_only(key); + property_map[p_base_path + key] = param; } property_parent_map[p_base_path][key] = p_base_path + key; @@ -1931,7 +1941,10 @@ bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) { } if (property_map.has(p_name)) { - property_map[p_name] = p_value; + if (is_inside_tree() && property_map[p_name].second) { + return false; // Prevent to set property by user. + } + property_map[p_name].first = p_value; return true; } @@ -1944,7 +1957,7 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const { } if (property_map.has(p_name)) { - r_ret = property_map[p_name]; + r_ret = property_map[p_name].first; return true; } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 2c1be6199c..52d3e1bd41 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -117,6 +117,7 @@ protected: GDVIRTUAL0RC(Array, _get_parameter_list) GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName) GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName) + GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName) GDVIRTUAL3RC(double, _process, double, bool, bool) GDVIRTUAL0RC(String, _get_caption) GDVIRTUAL0RC(bool, _has_filter) @@ -124,6 +125,7 @@ protected: public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const; virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual bool is_parameter_read_only(const StringName &p_parameter) const; void set_parameter(const StringName &p_name, const Variant &p_value); Variant get_parameter(const StringName &p_name) const; @@ -304,7 +306,7 @@ private: void _update_properties(); List<PropertyInfo> properties; HashMap<StringName, HashMap<StringName, StringName>> property_parent_map; - HashMap<StringName, Variant> property_map; + HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag. struct Activity { uint64_t last_pass = 0; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index b88695427e..39d1793368 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -60,7 +60,7 @@ void Tweener::_bind_methods() { ADD_SIGNAL(MethodInfo("finished")); } -void Tween::start_tweeners() { +void Tween::_start_tweeners() { if (tweeners.is_empty()) { dead = true; ERR_FAIL_MSG("Tween without commands, aborting."); @@ -71,6 +71,15 @@ void Tween::start_tweeners() { } } +void Tween::_stop_internal(bool p_reset) { + running = false; + if (p_reset) { + started = false; + dead = false; + total_time = 0; + } +} + Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration) { ERR_FAIL_NULL_V(p_target, nullptr); ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); @@ -135,14 +144,11 @@ void Tween::append(Ref<Tweener> p_tweener) { } void Tween::stop() { - started = false; - running = false; - dead = false; - total_time = 0; + _stop_internal(true); } void Tween::pause() { - running = false; + _stop_internal(false); } void Tween::play() { @@ -274,11 +280,20 @@ bool Tween::step(double p_delta) { } if (!started) { - ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners."); + if (tweeners.is_empty()) { + String tween_id; + Node *node = get_bound_node(); + if (node) { + tween_id = vformat("Tween (bound to %s)", node->is_inside_tree() ? (String)node->get_path() : (String)node->get_name()); + } else { + tween_id = to_string(); + } + ERR_FAIL_V_MSG(false, tween_id + ": started with no Tweeners."); + } current_step = 0; loops_done = 0; total_time = 0; - start_tweeners(); + _start_tweeners(); started = true; } @@ -319,7 +334,7 @@ bool Tween::step(double p_delta) { } else { emit_signal(SNAME("loop_finished"), loops_done); current_step = 0; - start_tweeners(); + _start_tweeners(); #ifdef DEBUG_ENABLED if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) { if (!potential_infinite) { @@ -332,7 +347,7 @@ bool Tween::step(double p_delta) { #endif } } else { - start_tweeners(); + _start_tweeners(); } } } @@ -387,6 +402,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, d return ret; } +String Tween::to_string() { + String ret = Object::to_string(); + Node *node = get_bound_node(); + if (node) { + ret += vformat(" (bound to %s)", node->get_name()); + } + return ret; +} + void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property); ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval); diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 8f65416e71..58217db535 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -123,12 +123,15 @@ private: typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d); static interpolater interpolaters[TRANS_MAX][EASE_MAX]; - void start_tweeners(); + void _start_tweeners(); + void _stop_internal(bool p_reset); protected: static void _bind_methods(); public: + virtual String to_string() override; + Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration); Ref<IntervalTweener> tween_interval(double p_time); Ref<CallbackTweener> tween_callback(Callable p_callback); |