diff options
Diffstat (limited to 'scene/animation/animation_player.cpp')
-rw-r--r-- | scene/animation/animation_player.cpp | 460 |
1 files changed, 261 insertions, 199 deletions
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 073f61bfdd..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" @@ -109,7 +109,7 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { Ref<AnimationLibrary> lib = d[lib_name]; add_animation_library(lib_name, lib); } - + emit_signal("animation_libraries_updated"); } else if (name.begins_with("next/")) { String which = name.get_slicec('/', 1); animation_set_next(which, p_value); @@ -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; @@ -156,7 +156,7 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } else if (name == "blend_times") { Vector<BlendKey> keys; - for (const KeyValue<BlendKey, float> &E : blend_times) { + for (const KeyValue<BlendKey, double> &E : blend_times) { keys.ordered_insert(E.key); } @@ -216,7 +216,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } -void AnimationPlayer::advance(float p_time) { +void AnimationPlayer::advance(double p_time) { _animation_process(p_time); } @@ -323,7 +323,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov #endif // _3D_DISABLED if (!child->is_connected("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed))) { - child->connect("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed).bind(child), CONNECT_ONESHOT); + child->connect("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed).bind(child), CONNECT_ONE_SHOT); } TrackNodeCacheKey key; @@ -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()); @@ -650,22 +650,21 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double double c = Math::ease(p_time / first_key_time, transition); Variant first_value = a->track_get_key_value(i, first_key); first_value = _post_process_key_value(a, i, first_value, nc->node); - Variant interp_value; - Variant::interpolate(pa->capture, first_value, c, interp_value); + Variant interp_value = Animation::interpolate_variant(pa->capture, first_value, c); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); cache_update_prop[cache_update_prop_size++] = pa; pa->value_accum = interp_value; pa->accum_pass = accum_pass; } else { - Variant::interpolate(pa->value_accum, interp_value, p_interp, pa->value_accum); + pa->value_accum = Animation::interpolate_variant(pa->value_accum, interp_value, p_interp); } continue; //handled } } - 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()) { @@ -679,12 +678,26 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double pa->value_accum = value; pa->accum_pass = accum_pass; } else { - Variant::interpolate(pa->value_accum, value, p_interp, pa->value_accum); + 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); @@ -732,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) { @@ -744,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); @@ -785,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; } @@ -834,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(); @@ -889,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) { @@ -929,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 { @@ -941,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(); @@ -953,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); } @@ -969,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: { @@ -980,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) { @@ -1045,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() { @@ -1102,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]; @@ -1165,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; @@ -1224,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); @@ -1238,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; @@ -1270,6 +1315,8 @@ void AnimationPlayer::_animation_set_cache_update() { // If something was modified or removed, caches need to be cleared clear_caches(); } + + emit_signal(SNAME("animation_list_changed")); } void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) { @@ -1282,11 +1329,12 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN if (!animation_set.has(name)) { return; // No need to update because not the one from the library being used. } + _animation_set_cache_update(); // Erase blends if needed List<BlendKey> to_erase; - for (const KeyValue<BlendKey, float> &E : blend_times) { + for (const KeyValue<BlendKey, double> &E : blend_times) { BlendKey bk = E.key; if (bk.from == name || bk.to == name) { to_erase.push_back(bk); @@ -1302,8 +1350,8 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) { // Rename autoplay or blends if needed. List<BlendKey> to_erase; - HashMap<BlendKey, float, BlendKey> to_insert; - for (const KeyValue<BlendKey, float> &E : blend_times) { + HashMap<BlendKey, double, BlendKey> to_insert; + for (const KeyValue<BlendKey, double> &E : blend_times) { BlendKey bk = E.key; BlendKey new_bk = bk; bool erase = false; @@ -1357,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; } @@ -1375,8 +1423,9 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref animation_libraries.insert(insert_pos, ald); 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_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)); + ald.library->connect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed)); _animation_set_cache_update(); @@ -1398,29 +1447,18 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) { ERR_FAIL_COND(at_pos == -1); 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_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; @@ -1430,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_added)); - 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_added).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); @@ -1464,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; } } @@ -1474,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>()); @@ -1484,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); } } @@ -1503,9 +1541,9 @@ bool AnimationPlayer::has_animation(const StringName &p_name) const { Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const { ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: \"%s\".", p_name)); - const AnimationData &data = animation_set[p_name]; + const AnimationData &anim_data = animation_set[p_name]; - return data.animation; + return anim_data.animation; } void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const { @@ -1522,7 +1560,7 @@ void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const { } } -void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time) { +void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, double p_time) { ERR_FAIL_COND_MSG(!animation_set.has(p_animation1), vformat("Animation not found: %s.", p_animation1)); ERR_FAIL_COND_MSG(!animation_set.has(p_animation2), vformat("Animation not found: %s.", p_animation2)); ERR_FAIL_COND_MSG(p_time < 0, "Blend time cannot be smaller than 0."); @@ -1537,7 +1575,7 @@ void AnimationPlayer::set_blend_time(const StringName &p_animation1, const Strin } } -float AnimationPlayer::get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const { +double AnimationPlayer::get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const { BlendKey bk; bk.from = p_animation1; bk.to = p_animation2; @@ -1570,11 +1608,11 @@ void AnimationPlayer::clear_queue() { queued.clear(); } -void AnimationPlayer::play_backwards(const StringName &p_name, float p_custom_blend) { +void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_blend) { play(p_name, p_custom_blend, -1, true); } -void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float p_custom_scale, bool p_from_end) { +void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) { StringName name = p_name; if (String(name) == "") { @@ -1586,7 +1624,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float Playback &c = playback; if (c.current.from) { - float blend_time = 0.0; + double blend_time = 0.0; // find if it can blend BlendKey bk; bk.from = c.current.from->name; @@ -1618,11 +1656,13 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float 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]; @@ -1695,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) { @@ -1740,7 +1774,7 @@ void AnimationPlayer::seek(double p_time, bool p_update) { } } -void AnimationPlayer::seek_delta(double p_time, float p_delta) { +void AnimationPlayer::seek_delta(double p_time, double p_delta) { if (!playback.current.from) { if (playback.assigned) { ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned)); @@ -1761,25 +1795,24 @@ bool AnimationPlayer::is_valid() const { return (playback.current.from); } -float AnimationPlayer::get_current_animation_position() const { +double AnimationPlayer::get_current_animation_position() const { ERR_FAIL_COND_V_MSG(!playback.current.from, 0, "AnimationPlayer has no current animation"); return playback.current.pos; } -float AnimationPlayer::get_current_animation_length() const { +double AnimationPlayer::get_current_animation_length() const { ERR_FAIL_COND_V_MSG(!playback.current.from, 0, "AnimationPlayer has no current animation"); 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")); @@ -1789,7 +1822,12 @@ void AnimationPlayer::_stop_playing_caches() { if (!player) { continue; } - player->stop(); + + if (p_reset) { + player->stop(); + } else { + player->pause(); + } } } @@ -1801,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(); @@ -1812,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) { @@ -1920,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; @@ -1932,11 +1992,11 @@ StringName AnimationPlayer::animation_get_next(const StringName &p_animation) co return animation_set[p_animation].next; } -void AnimationPlayer::set_default_blend_time(float p_default) { +void AnimationPlayer::set_default_blend_time(double p_default) { default_blend_time = p_default; } -float AnimationPlayer::get_default_blend_time() const { +double AnimationPlayer::get_default_blend_time() const { return default_blend_time; } @@ -2036,17 +2096,16 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { aux_player->add_animation_library("", al); aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET); // Forcing the use of the original root because the scene where original player belongs may be not the active one - Node *root = get_node(get_root()); - Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(root); + 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(); @@ -2083,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); @@ -2131,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"); @@ -2150,6 +2210,8 @@ void AnimationPlayer::_bind_methods() { ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name"))); ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name"))); + ADD_SIGNAL(MethodInfo("animation_list_changed")); + ADD_SIGNAL(MethodInfo("animation_libraries_updated")); ADD_SIGNAL(MethodInfo("caches_cleared")); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); |