diff options
Diffstat (limited to 'scene/animation/animation_player.cpp')
-rw-r--r-- | scene/animation/animation_player.cpp | 429 |
1 files changed, 231 insertions, 198 deletions
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index e306d00a51..047997ca09 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* animation_player.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* animation_player.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "animation_player.h" @@ -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; @@ -468,7 +468,7 @@ Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, i return p_value; } -void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) { +void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, Animation::LoopedFlag p_looped_flag) { _ensure_node_caches(p_anim); ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); @@ -664,7 +664,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } } - if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE || (p_delta == 0 && update_mode == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek + if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { Variant value = a->value_track_interpolate(i, p_time); if (value == Variant()) { @@ -681,9 +681,23 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double pa->value_accum = Animation::interpolate_variant(pa->value_accum, value, p_interp); } - } else if (p_is_current && p_delta != 0) { + } else { List<int> indices; - a->value_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged); + + if (p_seeked) { + int found_key = a->track_find_key(i, p_time); + if (found_key >= 0) { + indices.push_back(found_key); + } + } else { + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); + if (first_key >= 0) { + indices.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag); + } for (int &F : indices) { Variant value = a->track_get_key_value(i, F); @@ -731,10 +745,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_METHOD: { - if (!nc->node) { - continue; - } - if (p_delta == 0) { + if (!nc->node || is_stopping) { continue; } if (!p_is_current) { @@ -743,7 +754,20 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double List<int> indices; - a->method_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged); + if (p_seeked) { + int found_key = a->track_find_key(i, p_time); + if (found_key >= 0) { + indices.push_back(found_key); + } + } else { + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); + if (first_key >= 0) { + indices.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag); + } for (int &E : indices) { StringName method = a->method_track_get_name(i, E); @@ -784,10 +808,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_AUDIO: { - if (!nc->node) { - continue; - } - if (p_delta == 0) { + if (!nc->node || is_stopping) { continue; } @@ -833,7 +854,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } else { //find stuff to play List<int> to_play; - a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged); + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); + if (first_key >= 0) { + to_play.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag); if (to_play.size()) { int idx = to_play.back()->get(); @@ -888,12 +915,16 @@ 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; } - if (p_delta == 0 || p_seeked) { + if (p_seeked) { //seek int idx = a->track_find_key(i, p_time); if (idx < 0) { @@ -928,9 +959,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double break; } - if (player->is_playing() || p_seeked) { - player->play(anim_name); + if (player->is_playing()) { player->seek(at_anim_pos); + player->play(anim_name); nc->animation_playing = true; playing_caches.insert(nc); } else { @@ -940,7 +971,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } else { //find stuff to play List<int> to_play; - a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged); + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); + if (first_key >= 0) { + to_play.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag); if (to_play.size()) { int idx = to_play.back()->get(); @@ -952,8 +989,8 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double nc->animation_playing = false; } } else { + player->seek(0.0); player->play(anim_name); - player->seek(0.0, true); nc->animation_playing = true; playing_caches.insert(nc); } @@ -968,9 +1005,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started) { double delta = p_delta * speed_scale * cd.speed_scale; double next_pos = cd.pos + delta; + bool backwards = signbit(delta); // Negative zero means playing backwards too. real_t len = cd.from->animation->get_length(); - int pingponged = 0; + Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; switch (cd.from->animation->get_loop_mode()) { case Animation::LOOP_NONE: { @@ -979,64 +1017,57 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, } else if (next_pos > len) { next_pos = len; } - - bool backwards = signbit(delta); // Negative zero means playing backwards too - delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here) - - if (&cd == &playback.current) { - if (!backwards && cd.pos <= len && next_pos == len) { - //playback finished - end_reached = true; - end_notify = cd.pos < len; // Notify only if not already at the end - } - - if (backwards && cd.pos >= 0 && next_pos == 0) { - //playback finished - end_reached = true; - end_notify = cd.pos > 0; // Notify only if not already at the beginning - } - } + delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here). } break; case Animation::LOOP_LINEAR: { - double looped_next_pos = Math::fposmod(next_pos, (double)len); - if (looped_next_pos == 0 && next_pos != 0) { - // Loop multiples of the length to it, rather than 0 - // so state at time=length is previewable in the editor - next_pos = len; - } else { - next_pos = looped_next_pos; + if (next_pos < 0 && cd.pos >= 0) { + looped_flag = Animation::LOOPED_FLAG_START; } + if (next_pos > len && cd.pos <= len) { + looped_flag = Animation::LOOPED_FLAG_END; + } + next_pos = Math::fposmod(next_pos, (double)len); } break; case Animation::LOOP_PINGPONG: { - if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) { - if (next_pos < 0 && cd.pos >= 0) { - cd.speed_scale *= -1.0; - pingponged = -1; - } - if (next_pos > len && cd.pos <= len) { - cd.speed_scale *= -1.0; - pingponged = 1; - } + if (next_pos < 0 && cd.pos >= 0) { + cd.speed_scale *= -1.0; + looped_flag = Animation::LOOPED_FLAG_START; } - double looped_next_pos = Math::pingpong(next_pos, (double)len); - if (looped_next_pos == 0 && next_pos != 0) { - // Loop multiples of the length to it, rather than 0 - // so state at time=length is previewable in the editor - next_pos = len; - } else { - next_pos = looped_next_pos; + if (next_pos > len && cd.pos <= len) { + cd.speed_scale *= -1.0; + looped_flag = Animation::LOOPED_FLAG_END; } + next_pos = Math::pingpong(next_pos, (double)len); } break; default: break; } + double prev_pos = cd.pos; // The animation may be changed during process, so it is safer that the state is changed before process. cd.pos = next_pos; - _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged); + AnimationData *prev_from = cd.from; + _animation_process_animation(cd.from, prev_pos, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, looped_flag); + + // End detection. + if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) { + if (prev_from != playback.current.from) { + return; // Animation has been changed in the process (may be caused by method track), abort process. + } + if (!backwards && prev_pos <= len && next_pos == len) { + // Playback finished. + end_reached = true; + end_notify = prev_pos < len; // Notify only if not already at the end. + } + if (backwards && prev_pos >= 0 && next_pos == 0) { + // Playback finished. + end_reached = true; + end_notify = prev_pos > 0; // Notify only if not already at the beginning. + } + } } void AnimationPlayer::_animation_process2(double p_delta, bool p_started) { @@ -1044,24 +1075,35 @@ void AnimationPlayer::_animation_process2(double p_delta, bool p_started) { accum_pass++; - _animation_process_data(c.current, p_delta, 1.0f, c.seeked && p_delta != 0, p_started); + bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process. + if (p_delta != 0) { c.seeked = false; } - List<Blend>::Element *prev = nullptr; - for (List<Blend>::Element *E = c.blend.back(); E; E = prev) { + float blend = 1.0; // First animation we play at 100% blend + + List<Blend>::Element *next = NULL; + for (List<Blend>::Element *E = c.blend.front(); E; E = next) { Blend &b = E->get(); - float blend = b.blend_left / b.blend_time; + // Note: There may be issues if an animation event triggers an animation change while this blend is active, + // so it is best to use "deferred" calls instead of "immediate" for animation events that can trigger new animations. _animation_process_data(b.data, p_delta, blend, false, false); - + blend = 1.0 - b.blend_left / b.blend_time; // This is how much to blend the NEXT animation b.blend_left -= Math::absf(speed_scale * p_delta); - - prev = E->prev(); + next = E->next(); if (b.blend_left < 0) { - c.blend.erase(E); + // If the blend of this has finished, we need to remove ALL the previous blends + List<Blend>::Element *prev; + while (E) { + prev = E->prev(); + c.blend.erase(E); + E = prev; + } } } + + _animation_process_data(c.current, p_delta, blend, seeked, p_started); } void AnimationPlayer::_animation_update_transforms() { @@ -1101,8 +1143,6 @@ void AnimationPlayer::_animation_update_transforms() { } } - cache_update_size = 0; - for (int i = 0; i < cache_update_prop_size; i++) { TrackNodeCache::PropertyAnim *pa = cache_update_prop[i]; @@ -1164,29 +1204,35 @@ void AnimationPlayer::_animation_update_transforms() { } } - cache_update_prop_size = 0; - for (int i = 0; i < cache_update_bezier_size; i++) { TrackNodeCache::BezierAnim *ba = cache_update_bezier[i]; ERR_CONTINUE(ba->accum_pass != accum_pass); ba->object->set_indexed(ba->bezier_property, ba->bezier_accum); } - - cache_update_bezier_size = 0; } void AnimationPlayer::_animation_process(double p_delta) { if (playback.current.from) { end_reached = false; end_notify = false; - _animation_process2(p_delta, playback.started); + bool started = playback.started; // The animation may be changed during process, so it is safer that the state is changed before process. if (playback.started) { playback.started = false; } + cache_update_size = 0; + cache_update_prop_size = 0; + cache_update_bezier_size = 0; + + AnimationData *prev_from = playback.current.from; + _animation_process2(p_delta, started); + if (prev_from != playback.current.from) { + return; // Animation has been changed in the process (may be caused by method track), abort process. + } _animation_update_transforms(); + if (end_reached) { if (queued.size()) { String old = playback.assigned; @@ -1223,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); @@ -1237,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; @@ -1274,23 +1320,6 @@ void AnimationPlayer::_animation_set_cache_update() { } void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) { - { - int at_pos = -1; - - for (uint32_t i = 0; i < animation_libraries.size(); i++) { - if (animation_libraries[i].name == p_library) { - at_pos = i; - break; - } - } - - ERR_FAIL_COND(at_pos == -1); - - ERR_FAIL_COND(!animation_libraries[at_pos].library->animations.has(p_name)); - - _ref_anim(animation_libraries[at_pos].library->animations[p_name]); - } - _animation_set_cache_update(); } @@ -1301,11 +1330,6 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN return; // No need to update because not the one from the library being used. } - AnimationData animation_data = animation_set[name]; - if (animation_data.animation_library == p_library) { - _unref_anim(animation_data.animation); - } - _animation_set_cache_update(); // Erase blends if needed @@ -1381,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; } @@ -1401,10 +1425,7 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name)); ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_name)); ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_name)); - - for (const KeyValue<StringName, Ref<Animation>> &K : ald.library->animations) { - _ref_anim(K.value); - } + ald.library->connect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed)); _animation_set_cache_update(); @@ -1428,27 +1449,16 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) { animation_libraries[at_pos].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); animation_libraries[at_pos].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); + animation_libraries[at_pos].library->disconnect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed)); stop(); - for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[at_pos].library->animations) { - _unref_anim(K.value); - } - animation_libraries.remove_at(at_pos); _animation_set_cache_update(); notify_property_list_changed(); } -void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), CONNECT_REFERENCE_COUNTED); -} - -void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed)); -} - void AnimationPlayer::rename_animation_library(const StringName &p_name, const StringName &p_new_name) { if (p_name == p_new_name) { return; @@ -1458,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); @@ -1492,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; } } @@ -1502,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>()); @@ -1512,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); } } @@ -1646,11 +1656,13 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa b.data = c.current; b.blend_time = b.blend_left = blend_time; c.blend.push_back(b); + } else { + c.blend.clear(); } } if (get_current_animation() != p_name) { - _stop_playing_caches(); + _stop_playing_caches(false); } c.current.from = &animation_set[name]; @@ -1723,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) { @@ -1799,15 +1805,14 @@ double AnimationPlayer::get_current_animation_length() const { return playback.current.from->animation->get_length(); } -void AnimationPlayer::_animation_changed() { +void AnimationPlayer::_animation_changed(const StringName &p_name) { clear_caches(); - emit_signal(SNAME("caches_cleared")); if (is_playing()) { playback.seeked = true; //need to restart stuff, like audio } } -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")); @@ -1817,7 +1822,12 @@ void AnimationPlayer::_stop_playing_caches() { if (!player) { continue; } - player->stop(); + + if (p_reset) { + player->stop(); + } else { + player->pause(); + } } } @@ -1829,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(); @@ -1840,6 +1850,8 @@ void AnimationPlayer::clear_caches() { cache_update_size = 0; cache_update_prop_size = 0; cache_update_bezier_size = 0; + + emit_signal(SNAME("caches_cleared")); } void AnimationPlayer::set_active(bool p_active) { @@ -1948,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; @@ -2066,14 +2098,14 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { // Forcing the use of the original root because the scene where original player belongs may be not the active one Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(get_node(get_root())); aux_player->seek(0.0f, true); - aux_player->queue_delete(); + aux_player->queue_free(); if (p_user_initiated) { Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values(); old_values->restore(); - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur->create_action(TTR("Anim Apply Reset")); + 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"); ur->commit_action(); @@ -2110,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); @@ -2158,7 +2191,7 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); |