Path: blob/master/scene/animation/animation_player.cpp
20960 views
/**************************************************************************/1/* animation_player.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "animation_player.h"31#include "animation_player.compat.inc"3233#include "core/config/engine.h"3435bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) {36String name = p_name;37if (name.begins_with("playback/play")) { // For backward compatibility.38set_current_animation(p_value);39} else if (name.begins_with("next/")) {40String which = name.get_slicec('/', 1);41animation_set_next(which, p_value);42} else if (p_name == SceneStringName(blend_times)) {43Array array = p_value;44int len = array.size();45ERR_FAIL_COND_V(len % 3, false);4647for (int i = 0; i < len / 3; i++) {48StringName from = array[i * 3 + 0];49StringName to = array[i * 3 + 1];50float time = array[i * 3 + 2];51set_blend_time(from, to, time);52}53#ifndef DISABLE_DEPRECATED54} else if (p_name == "method_call_mode") {55set_callback_mode_method(static_cast<AnimationCallbackModeMethod>((int)p_value));56} else if (p_name == "playback_process_mode") {57set_callback_mode_process(static_cast<AnimationCallbackModeProcess>((int)p_value));58} else if (p_name == "playback_active") {59set_active(p_value);60#endif // DISABLE_DEPRECATED61} else {62return false;63}64return true;65}6667bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {68String name = p_name;6970if (name == "playback/play") { // For backward compatibility.7172r_ret = get_current_animation();7374} else if (name.begins_with("next/")) {75String which = name.get_slicec('/', 1);76r_ret = animation_get_next(which);7778} else if (p_name == SceneStringName(blend_times)) {79Vector<BlendKey> keys;80for (const KeyValue<BlendKey, double> &E : blend_times) {81keys.ordered_insert(E.key);82}8384Array array;85for (int i = 0; i < keys.size(); i++) {86array.push_back(keys[i].from);87array.push_back(keys[i].to);88array.push_back(blend_times.get(keys[i]));89}9091r_ret = array;92#ifndef DISABLE_DEPRECATED93} else if (name == "method_call_mode") {94r_ret = get_callback_mode_method();95} else if (name == "playback_process_mode") {96r_ret = get_callback_mode_process();97} else if (name == "playback_active") {98r_ret = is_active();99#endif // DISABLE_DEPRECATED100} else {101return false;102}103104return true;105}106107void AnimationPlayer::_validate_property(PropertyInfo &p_property) const {108if (Engine::get_singleton()->is_editor_hint() && p_property.name == "current_animation") {109List<String> names;110111for (const KeyValue<StringName, AnimationData> &E : animation_set) {112names.push_back(E.key);113}114names.push_front("[stop]");115String hint;116for (List<String>::Element *E = names.front(); E; E = E->next()) {117if (E != names.front()) {118hint += ",";119}120hint += E->get();121}122123p_property.hint_string = hint;124} else if (!auto_capture && p_property.name.begins_with("playback_auto_capture_")) {125p_property.usage = PROPERTY_USAGE_NONE;126}127}128129void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {130List<PropertyInfo> anim_names;131132for (const KeyValue<StringName, AnimationData> &E : animation_set) {133AHashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);134if (F && F->value != StringName()) {135anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));136}137}138139for (const PropertyInfo &E : anim_names) {140p_list->push_back(E);141}142143p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));144}145146void AnimationPlayer::_notification(int p_what) {147switch (p_what) {148case NOTIFICATION_READY: {149if (!Engine::get_singleton()->is_editor_hint() && animation_set.has(autoplay)) {150set_active(active);151play(autoplay);152_check_immediately_after_start();153}154} break;155}156}157158void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_internal_seeked, bool p_started, bool p_is_current) {159double speed = speed_scale * cd.speed_scale;160bool backwards = std::signbit(speed); // Negative zero means playing backwards too.161double delta = p_started ? 0 : p_delta * speed;162double next_pos = cd.pos + delta;163164double start = cd.get_start_time();165double end = cd.get_end_time();166167Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;168169switch (cd.from->animation->get_loop_mode()) {170case Animation::LOOP_NONE: {171if (Animation::is_less_approx(next_pos, start)) {172next_pos = start;173} else if (Animation::is_greater_approx(next_pos, end)) {174next_pos = end;175}176delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here).177} break;178179case Animation::LOOP_LINEAR: {180if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {181looped_flag = Animation::LOOPED_FLAG_START;182}183if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {184looped_flag = Animation::LOOPED_FLAG_END;185}186next_pos = Math::fposmod(next_pos - start, end - start) + start;187} break;188189case Animation::LOOP_PINGPONG: {190if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {191cd.speed_scale *= -1.0;192looped_flag = Animation::LOOPED_FLAG_START;193}194if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {195cd.speed_scale *= -1.0;196looped_flag = Animation::LOOPED_FLAG_END;197}198next_pos = Math::pingpong(next_pos - start, end - start) + start;199} break;200201default:202break;203}204205double prev_pos = cd.pos; // The animation may be changed during process, so it is safer that the state is changed before process.206207// End detection.208if (p_is_current) {209if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) {210if (!backwards && Animation::is_less_or_equal_approx(prev_pos, end) && Math::is_equal_approx(next_pos, end)) {211// Playback finished.212next_pos = end; // Snap to the edge.213end_reached = true;214end_notify = Animation::is_less_approx(prev_pos, end); // Notify only if not already at the end.215p_blend = 1.0;216}217if (backwards && Animation::is_greater_or_equal_approx(prev_pos, start) && Math::is_equal_approx(next_pos, start)) {218// Playback finished.219next_pos = start; // Snap to the edge.220end_reached = true;221end_notify = Animation::is_greater_approx(prev_pos, start); // Notify only if not already at the beginning.222p_blend = 1.0;223}224}225}226227cd.pos = next_pos;228229PlaybackInfo pi;230if (p_started) {231pi.time = prev_pos;232pi.delta = 0;233pi.start = start;234pi.end = end;235pi.seeked = true;236} else {237pi.time = next_pos;238pi.delta = delta;239pi.start = start;240pi.end = end;241pi.seeked = p_seeked;242}243if (Math::is_zero_approx(pi.delta) && backwards) {244pi.delta = -0.0; // Sign is needed to handle converted Continuous track from Discrete track correctly.245}246// Immediately after playback, discrete keys should be retrieved with EXACT mode since behind keys must be ignored at that time.247pi.is_external_seeking = !p_internal_seeked && !p_started;248pi.looped_flag = looped_flag;249pi.weight = p_blend;250make_animation_instance(cd.from->name, pi);251}252253float AnimationPlayer::get_current_blend_amount() {254Playback &c = playback;255float blend = 1.0;256for (const Blend &b : c.blend) {257blend = blend - b.blend_left;258}259return MAX(0, blend);260}261262void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) {263Playback &c = playback;264265bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process.266bool internal_seeked = c.internal_seeked;267268if (!Math::is_zero_approx(p_delta)) {269c.seeked = false;270c.internal_seeked = false;271}272273// Second, process current animation to check if the animation end reached.274_process_playback_data(c.current, p_delta, get_current_blend_amount(), seeked, internal_seeked, p_started, true);275276// Finally, if not end the animation, do blending.277if (end_reached) {278playback.blend.clear();279if (end_notify) {280finished_anim = playback.assigned;281}282return;283}284List<List<Blend>::Element *> to_erase;285for (List<Blend>::Element *E = c.blend.front(); E; E = E->next()) {286Blend &b = E->get();287b.blend_left = MAX(0, b.blend_left - Math::abs(speed_scale * p_delta) / b.blend_time);288if (Animation::is_less_or_equal_approx(b.blend_left, 0)) {289to_erase.push_back(E);290b.blend_left = CMP_EPSILON; // May want to play last frame.291}292// Note: There may be issues if an animation event triggers an animation change while this blend is active,293// so it is best to use "deferred" calls instead of "immediate" for animation events that can trigger new animations.294_process_playback_data(b.data, p_delta, b.blend_left, false, false, false);295}296for (List<Blend>::Element *&E : to_erase) {297c.blend.erase(E);298}299}300301bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {302if (!playback.current.from) {303_set_process(false);304return false;305}306307tmp_from = playback.current.from->animation->get_instance_id();308end_reached = false;309end_notify = false;310311finished_anim = StringName();312313bool started = playback.started; // The animation may be changed during process, so it is safer that the state is changed before process.314if (playback.started) {315playback.started = false;316}317318AnimationData *prev_from = playback.current.from;319_blend_playback_data(p_delta, started);320321if (prev_from != playback.current.from) {322return false; // Animation has been changed in the process (may be caused by method track), abort process.323}324325return true;326}327328void AnimationPlayer::_blend_capture(double p_delta) {329blend_capture(p_delta * Math::abs(speed_scale));330}331332void AnimationPlayer::_blend_post_process() {333if (end_reached) {334// If the method track changes current animation, the animation is not finished.335if (tmp_from == playback.current.from->animation->get_instance_id()) {336if (playback_queue.size()) {337if (!finished_anim.is_empty()) {338emit_signal(SceneStringName(animation_finished), finished_anim);339}340String old = playback.assigned;341play(playback_queue.front()->get());342String new_name = playback.assigned;343playback_queue.pop_front();344if (end_notify) {345emit_signal(SceneStringName(animation_changed), old, new_name);346}347} else {348_clear_caches();349playing = false;350_set_process(false);351if (end_notify) {352if (!finished_anim.is_empty()) {353emit_signal(SceneStringName(animation_finished), finished_anim);354}355emit_signal(SNAME("current_animation_changed"), "");356if (movie_quit_on_finish && OS::get_singleton()->has_feature("movie")) {357print_line(vformat("Movie Maker mode is enabled. Quitting on animation finish as requested by: %s", get_path()));358get_tree()->quit();359}360}361}362}363end_reached = false;364end_notify = false;365}366tmp_from = ObjectID();367}368369void AnimationPlayer::queue(const StringName &p_name) {370if (!is_playing()) {371play(p_name);372} else {373playback_queue.push_back(p_name);374}375}376377TypedArray<StringName> AnimationPlayer::get_queue() {378TypedArray<StringName> ret;379for (const StringName &E : playback_queue) {380ret.push_back(E);381}382383return ret;384}385386void AnimationPlayer::clear_queue() {387playback_queue.clear();388}389390void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_blend) {391play(p_name, p_custom_blend, -1, true);392}393394void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend) {395play_section_with_markers(p_name, p_start_marker, p_end_marker, p_custom_blend, -1, true);396}397398void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) {399play_section(p_name, p_start_time, p_end_time, p_custom_blend, -1, true);400}401402void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {403if (auto_capture) {404play_with_capture(p_name, auto_capture_duration, p_custom_blend, p_custom_scale, p_from_end, auto_capture_transition_type, auto_capture_ease_type);405} else {406_play(p_name, p_custom_blend, p_custom_scale, p_from_end);407}408}409410void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {411play_section_with_markers(p_name, StringName(), StringName(), p_custom_blend, p_custom_scale, p_from_end);412}413414void AnimationPlayer::play_section_with_markers(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend, float p_custom_scale, bool p_from_end) {415StringName name = p_name;416417if (name == StringName()) {418name = playback.assigned;419}420421ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));422423Ref<Animation> animation = animation_set[name].animation;424425ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));426ERR_FAIL_COND_MSG(p_start_marker && !animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, name));427ERR_FAIL_COND_MSG(p_end_marker && !animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, name));428429double start_time = p_start_marker ? animation->get_marker_time(p_start_marker) : -1;430double end_time = p_end_marker ? animation->get_marker_time(p_end_marker) : -1;431432ERR_FAIL_COND_MSG(p_start_marker && p_end_marker && Animation::is_greater_approx(start_time, end_time), vformat("End marker %s is placed earlier than start marker %s in animation: %s.", p_end_marker, p_start_marker, name));433434if (p_start_marker && Animation::is_less_approx(start_time, 0)) {435WARN_PRINT_ED(vformat("Negative time start marker: %s is invalid in the section, so the start of the animation: %s is used instead.", p_start_marker, playback.current.from->animation->get_name()));436}437if (p_end_marker && Animation::is_less_approx(end_time, 0)) {438WARN_PRINT_ED(vformat("Negative time end marker: %s is invalid in the section, so the end of the animation: %s is used instead.", p_end_marker, playback.current.from->animation->get_name()));439}440441play_section(name, start_time, end_time, p_custom_blend, p_custom_scale, p_from_end);442}443444void AnimationPlayer::play_section(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend, float p_custom_scale, bool p_from_end) {445StringName name = p_name;446447if (name == StringName()) {448name = playback.assigned;449}450451ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));452ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Math::is_equal_approx(p_start_time, p_end_time), "Start time and end time must not equal to each other.");453ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Animation::is_greater_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));454455Playback &c = playback;456457if (c.current.from) {458double blend_time = 0.0;459// Find if it can blend.460BlendKey bk;461bk.from = c.current.from->name;462bk.to = name;463464if (Animation::is_greater_or_equal_approx(p_custom_blend, 0)) {465blend_time = p_custom_blend;466} else if (blend_times.has(bk)) {467blend_time = blend_times[bk];468} else {469bk.from = "*";470if (blend_times.has(bk)) {471blend_time = blend_times[bk];472} else {473bk.from = c.current.from->name;474bk.to = "*";475476if (blend_times.has(bk)) {477blend_time = blend_times[bk];478}479}480}481482if (Animation::is_less_approx(p_custom_blend, 0) && Math::is_zero_approx(blend_time) && default_blend_time) {483blend_time = default_blend_time;484}485if (Animation::is_greater_approx(blend_time, 0)) {486Blend b;487b.data = c.current;488b.blend_left = get_current_blend_amount();489b.blend_time = blend_time;490c.blend.push_back(b);491} else {492c.blend.clear();493}494}495496if (get_current_animation() != p_name) {497_clear_playing_caches();498}499500c.current.from = &animation_set[name];501c.current.speed_scale = p_custom_scale;502c.current.start_time = p_start_time;503c.current.end_time = p_end_time;504505double start = playback.current.get_start_time();506double end = playback.current.get_end_time();507508if (!end_reached) {509playback_queue.clear();510}511512if (c.assigned != name) { // Reset.513c.current.pos = p_from_end ? end : start;514c.assigned = name;515emit_signal(SNAME("current_animation_changed"), c.assigned);516} else {517if (p_from_end && Animation::is_less_or_equal_approx(c.current.pos, start)) {518// Animation reset but played backwards, set position to the end.519seek_internal(end, true, true, true);520} else if (!p_from_end && Animation::is_greater_or_equal_approx(c.current.pos, end)) {521// Animation resumed but already ended, set position to the beginning.522seek_internal(start, true, true, true);523} else if (playing) {524return;525}526}527528c.seeked = false;529c.started = true;530531_set_process(true); // Always process when starting an animation.532playing = true;533534emit_signal(SceneStringName(animation_started), c.assigned);535536if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {537return; // No next in this case.538}539540StringName next = animation_get_next(p_name);541if (next != StringName() && animation_set.has(next)) {542queue(next);543}544}545546void AnimationPlayer::_capture(const StringName &p_name, bool p_from_end, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {547StringName name = p_name;548if (name == StringName()) {549name = playback.assigned;550}551552Ref<Animation> anim = get_animation(name);553if (anim.is_null() || !anim->is_capture_included()) {554return;555}556if (std::signbit(p_duration)) {557double max_dur = 0;558double current_pos = playback.current.pos;559if (playback.assigned != name) {560current_pos = p_from_end ? anim->get_length() : 0;561}562for (int i = 0; i < anim->get_track_count(); i++) {563if (anim->track_get_type(i) != Animation::TYPE_VALUE) {564continue;565}566if (anim->value_track_get_update_mode(i) != Animation::UPDATE_CAPTURE) {567continue;568}569if (anim->track_get_key_count(i) == 0) {570continue;571}572max_dur = MAX(max_dur, p_from_end ? current_pos - anim->track_get_key_time(i, anim->track_get_key_count(i) - 1) : anim->track_get_key_time(i, 0) - current_pos);573}574p_duration = max_dur;575}576if (Math::is_zero_approx(p_duration)) {577return;578}579capture(name, p_duration, p_trans_type, p_ease_type);580}581582void AnimationPlayer::play_with_capture(const StringName &p_name, double p_duration, double p_custom_blend, float p_custom_scale, bool p_from_end, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {583_capture(p_name, p_from_end, p_duration, p_trans_type, p_ease_type);584_play(p_name, p_custom_blend, p_custom_scale, p_from_end);585}586587bool AnimationPlayer::is_playing() const {588return playing;589}590591void AnimationPlayer::set_current_animation(const StringName &p_animation) {592if (p_animation == SNAME("[stop]") || p_animation.is_empty()) {593stop();594} else if (!is_playing()) {595play(p_animation);596} else if (playback.assigned != p_animation) {597float speed = playback.current.speed_scale;598play(p_animation, -1.0, speed, std::signbit(speed));599} else {600// Same animation, do not replay from start.601}602}603604StringName AnimationPlayer::get_current_animation() const {605return (is_playing() ? playback.assigned : StringName());606}607608void AnimationPlayer::set_assigned_animation(const StringName &p_animation) {609if (is_playing()) {610float speed = playback.current.speed_scale;611play(p_animation, -1.0, speed, std::signbit(speed));612} else {613ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation.operator String()));614playback.current.pos = 0;615playback.current.from = &animation_set[p_animation];616playback.current.start_time = -1;617playback.current.end_time = -1;618playback.assigned = p_animation;619emit_signal(SNAME("current_animation_changed"), playback.assigned);620}621}622623StringName AnimationPlayer::get_assigned_animation() const {624return playback.assigned;625}626627void AnimationPlayer::pause() {628_stop_internal(false, false);629}630631void AnimationPlayer::stop(bool p_keep_state) {632_stop_internal(true, p_keep_state);633}634635void AnimationPlayer::set_speed_scale(float p_speed) {636speed_scale = p_speed;637}638639float AnimationPlayer::get_speed_scale() const {640return speed_scale;641}642643float AnimationPlayer::get_playing_speed() const {644if (!playing) {645return 0;646}647return speed_scale * playback.current.speed_scale;648}649650void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_only, bool p_is_internal_seek) {651if (!active) {652return;653}654655bool is_backward = Animation::is_less_approx(p_time, playback.current.pos);656657_check_immediately_after_start();658659playback.current.pos = p_time;660if (!playback.current.from) {661if (playback.assigned) {662ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned));663playback.current.from = &animation_set[playback.assigned];664}665if (!playback.current.from) {666return; // There is no animation.667}668}669670double start = playback.current.get_start_time();671double end = playback.current.get_end_time();672673// Clamp the seek position.674p_time = CLAMP(p_time, start, end);675676playback.seeked = true;677playback.internal_seeked = p_is_internal_seek;678679if (p_update) {680_process_animation(is_backward ? -0.0 : 0.0, p_update_only);681playback.seeked = false; // If animation was proceeded here, no more seek in internal process.682}683}684685void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) {686seek_internal(p_time, p_update, p_update_only);687}688689void AnimationPlayer::advance(double p_time) {690_check_immediately_after_start();691AnimationMixer::advance(p_time);692}693694void AnimationPlayer::_check_immediately_after_start() {695if (playback.started) {696_process_animation(0); // Force process current key for Discrete/Method/Audio/AnimationPlayback. Then, started flag is cleared.697}698}699700bool AnimationPlayer::is_valid() const {701return (playback.current.from);702}703704double AnimationPlayer::get_current_animation_position() const {705ERR_FAIL_NULL_V_MSG(playback.current.from, 0, "AnimationPlayer has no current animation.");706return playback.current.pos;707}708709double AnimationPlayer::get_current_animation_length() const {710ERR_FAIL_NULL_V_MSG(playback.current.from, 0, "AnimationPlayer has no current animation.");711return playback.current.from->animation->get_length();712}713714void AnimationPlayer::set_section_with_markers(const StringName &p_start_marker, const StringName &p_end_marker) {715ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");716ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));717ERR_FAIL_COND_MSG(p_start_marker && !playback.current.from->animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));718ERR_FAIL_COND_MSG(p_end_marker && !playback.current.from->animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));719double start_time = p_start_marker ? playback.current.from->animation->get_marker_time(p_start_marker) : -1;720double end_time = p_end_marker ? playback.current.from->animation->get_marker_time(p_end_marker) : -1;721if (p_start_marker && Animation::is_less_approx(start_time, 0)) {722WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));723}724if (p_end_marker && Animation::is_less_approx(end_time, 0)) {725WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));726}727set_section(start_time, end_time);728}729730void AnimationPlayer::set_section(double p_start_time, double p_end_time) {731ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");732ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));733playback.current.start_time = p_start_time;734playback.current.end_time = p_end_time;735playback.current.pos = CLAMP(playback.current.pos, playback.current.get_start_time(), playback.current.get_end_time());736}737738void AnimationPlayer::reset_section() {739playback.current.start_time = -1;740playback.current.end_time = -1;741}742743double AnimationPlayer::get_section_start_time() const {744ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation.");745return playback.current.get_start_time();746}747748double AnimationPlayer::get_section_end_time() const {749ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation.");750return playback.current.get_end_time();751}752753bool AnimationPlayer::has_section() const {754return Animation::is_greater_or_equal_approx(playback.current.start_time, 0) || Animation::is_greater_or_equal_approx(playback.current.end_time, 0);755}756757void AnimationPlayer::set_autoplay(const StringName &p_name) {758if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {759WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect.");760}761762autoplay = p_name;763}764765StringName AnimationPlayer::get_autoplay() const {766return autoplay;767}768769void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {770movie_quit_on_finish = p_enabled;771}772773bool AnimationPlayer::is_movie_quit_on_finish_enabled() const {774return movie_quit_on_finish;775}776777void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {778_clear_caches();779Playback &c = playback;780// c.blend.clear();781double start = c.current.from ? playback.current.get_start_time() : 0;782if (p_reset) {783c.blend.clear();784if (p_keep_state) {785c.current.pos = start;786} else {787is_stopping = true;788seek_internal(start, true, true, true);789is_stopping = false;790}791c.current.from = nullptr;792c.current.speed_scale = 1;793emit_signal(SNAME("current_animation_changed"), "");794}795_set_process(false);796playback_queue.clear();797playing = false;798}799800void AnimationPlayer::animation_set_next(const StringName &p_animation, const StringName &p_next) {801ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));802animation_next_set[p_animation] = p_next;803}804805StringName AnimationPlayer::animation_get_next(const StringName &p_animation) const {806if (!animation_next_set.has(p_animation)) {807return StringName();808}809return animation_next_set[p_animation];810}811812void AnimationPlayer::set_default_blend_time(double p_default) {813default_blend_time = p_default;814}815816double AnimationPlayer::get_default_blend_time() const {817return default_blend_time;818}819820void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, double p_time) {821ERR_FAIL_COND_MSG(!animation_set.has(p_animation1), vformat("Animation not found: %s.", p_animation1));822ERR_FAIL_COND_MSG(!animation_set.has(p_animation2), vformat("Animation not found: %s.", p_animation2));823ERR_FAIL_COND_MSG(p_time < 0, "Blend time cannot be smaller than 0.");824825BlendKey bk;826bk.from = p_animation1;827bk.to = p_animation2;828if (Math::is_zero_approx(p_time)) {829blend_times.erase(bk);830} else {831blend_times[bk] = p_time;832}833}834835double AnimationPlayer::get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const {836BlendKey bk;837bk.from = p_animation1;838bk.to = p_animation2;839840if (blend_times.has(bk)) {841return blend_times[bk];842} else {843return 0;844}845}846847void AnimationPlayer::set_auto_capture(bool p_auto_capture) {848auto_capture = p_auto_capture;849notify_property_list_changed();850}851852bool AnimationPlayer::is_auto_capture() const {853return auto_capture;854}855856void AnimationPlayer::set_auto_capture_duration(double p_auto_capture_duration) {857auto_capture_duration = p_auto_capture_duration;858}859860double AnimationPlayer::get_auto_capture_duration() const {861return auto_capture_duration;862}863864void AnimationPlayer::set_auto_capture_transition_type(Tween::TransitionType p_auto_capture_transition_type) {865auto_capture_transition_type = p_auto_capture_transition_type;866}867868Tween::TransitionType AnimationPlayer::get_auto_capture_transition_type() const {869return auto_capture_transition_type;870}871872void AnimationPlayer::set_auto_capture_ease_type(Tween::EaseType p_auto_capture_ease_type) {873auto_capture_ease_type = p_auto_capture_ease_type;874}875876Tween::EaseType AnimationPlayer::get_auto_capture_ease_type() const {877return auto_capture_ease_type;878}879880#ifdef TOOLS_ENABLED881void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {882const String pf = p_function;883if (p_idx == 0 && (pf == "play" || pf == "play_backwards" || pf == "has_animation" || pf == "queue")) {884List<StringName> al;885get_animation_list(&al);886for (const StringName &name : al) {887r_options->push_back(String(name).quote());888}889}890AnimationMixer::get_argument_options(p_function, p_idx, r_options);891}892#endif893894void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {895AnimationMixer::_animation_removed(p_name, p_library);896897StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name));898899if (!animation_set.has(name)) {900return; // No need to update because not the one from the library being used.901}902903_animation_set_cache_update();904905// Erase blends if needed906List<BlendKey> to_erase;907for (const KeyValue<BlendKey, double> &E : blend_times) {908BlendKey bk = E.key;909if (bk.from == name || bk.to == name) {910to_erase.push_back(bk);911}912}913914while (to_erase.size()) {915blend_times.erase(to_erase.front()->get());916to_erase.pop_front();917}918}919920void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) {921AnimationMixer::_rename_animation(p_from_name, p_to_name);922923// Rename autoplay or blends if needed.924List<BlendKey> to_erase;925HashMap<BlendKey, double, BlendKey> to_insert;926for (const KeyValue<BlendKey, double> &E : blend_times) {927BlendKey bk = E.key;928BlendKey new_bk = bk;929bool erase = false;930if (bk.from == p_from_name) {931new_bk.from = p_to_name;932erase = true;933}934if (bk.to == p_from_name) {935new_bk.to = p_to_name;936erase = true;937}938939if (erase) {940to_erase.push_back(bk);941to_insert[new_bk] = E.value;942}943}944945while (to_erase.size()) {946blend_times.erase(to_erase.front()->get());947to_erase.pop_front();948}949950while (to_insert.size()) {951blend_times[to_insert.begin()->key] = to_insert.begin()->value;952to_insert.remove(to_insert.begin());953}954955if (autoplay == p_from_name) {956autoplay = p_to_name;957}958}959960void AnimationPlayer::_bind_methods() {961ClassDB::bind_method(D_METHOD("animation_set_next", "animation_from", "animation_to"), &AnimationPlayer::animation_set_next);962ClassDB::bind_method(D_METHOD("animation_get_next", "animation_from"), &AnimationPlayer::animation_get_next);963964ClassDB::bind_method(D_METHOD("set_blend_time", "animation_from", "animation_to", "sec"), &AnimationPlayer::set_blend_time);965ClassDB::bind_method(D_METHOD("get_blend_time", "animation_from", "animation_to"), &AnimationPlayer::get_blend_time);966967ClassDB::bind_method(D_METHOD("set_default_blend_time", "sec"), &AnimationPlayer::set_default_blend_time);968ClassDB::bind_method(D_METHOD("get_default_blend_time"), &AnimationPlayer::get_default_blend_time);969970ClassDB::bind_method(D_METHOD("set_auto_capture", "auto_capture"), &AnimationPlayer::set_auto_capture);971ClassDB::bind_method(D_METHOD("is_auto_capture"), &AnimationPlayer::is_auto_capture);972ClassDB::bind_method(D_METHOD("set_auto_capture_duration", "auto_capture_duration"), &AnimationPlayer::set_auto_capture_duration);973ClassDB::bind_method(D_METHOD("get_auto_capture_duration"), &AnimationPlayer::get_auto_capture_duration);974ClassDB::bind_method(D_METHOD("set_auto_capture_transition_type", "auto_capture_transition_type"), &AnimationPlayer::set_auto_capture_transition_type);975ClassDB::bind_method(D_METHOD("get_auto_capture_transition_type"), &AnimationPlayer::get_auto_capture_transition_type);976ClassDB::bind_method(D_METHOD("set_auto_capture_ease_type", "auto_capture_ease_type"), &AnimationPlayer::set_auto_capture_ease_type);977ClassDB::bind_method(D_METHOD("get_auto_capture_ease_type"), &AnimationPlayer::get_auto_capture_ease_type);978979ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));980ClassDB::bind_method(D_METHOD("play_section_with_markers", "name", "start_marker", "end_marker", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));981ClassDB::bind_method(D_METHOD("play_section", "name", "start_time", "end_time", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));982ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));983ClassDB::bind_method(D_METHOD("play_section_with_markers_backwards", "name", "start_marker", "end_marker", "custom_blend"), &AnimationPlayer::play_section_with_markers_backwards, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1));984ClassDB::bind_method(D_METHOD("play_section_backwards", "name", "start_time", "end_time", "custom_blend"), &AnimationPlayer::play_section_backwards, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1));985ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(StringName()), DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));986ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);987ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));988ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing);989ClassDB::bind_method(D_METHOD("is_animation_active"), &AnimationPlayer::is_valid);990991ClassDB::bind_method(D_METHOD("set_current_animation", "animation"), &AnimationPlayer::set_current_animation);992ClassDB::bind_method(D_METHOD("get_current_animation"), &AnimationPlayer::get_current_animation);993ClassDB::bind_method(D_METHOD("set_assigned_animation", "animation"), &AnimationPlayer::set_assigned_animation);994ClassDB::bind_method(D_METHOD("get_assigned_animation"), &AnimationPlayer::get_assigned_animation);995ClassDB::bind_method(D_METHOD("queue", "name"), &AnimationPlayer::queue);996ClassDB::bind_method(D_METHOD("get_queue"), &AnimationPlayer::get_queue);997ClassDB::bind_method(D_METHOD("clear_queue"), &AnimationPlayer::clear_queue);998999ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &AnimationPlayer::set_speed_scale);1000ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimationPlayer::get_speed_scale);1001ClassDB::bind_method(D_METHOD("get_playing_speed"), &AnimationPlayer::get_playing_speed);10021003ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay);1004ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay);10051006ClassDB::bind_method(D_METHOD("find_animation", "animation"), &AnimationPlayer::find_animation);1007ClassDB::bind_method(D_METHOD("find_animation_library", "animation"), &AnimationPlayer::find_animation_library);10081009ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);1010ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);10111012ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);1013ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);10141015ClassDB::bind_method(D_METHOD("set_section_with_markers", "start_marker", "end_marker"), &AnimationPlayer::set_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()));1016ClassDB::bind_method(D_METHOD("set_section", "start_time", "end_time"), &AnimationPlayer::set_section, DEFVAL(-1), DEFVAL(-1));1017ClassDB::bind_method(D_METHOD("reset_section"), &AnimationPlayer::reset_section);10181019ClassDB::bind_method(D_METHOD("get_section_start_time"), &AnimationPlayer::get_section_start_time);1020ClassDB::bind_method(D_METHOD("get_section_end_time"), &AnimationPlayer::get_section_end_time);1021ClassDB::bind_method(D_METHOD("has_section"), &AnimationPlayer::has_section);10221023ClassDB::bind_method(D_METHOD("seek", "seconds", "update", "update_only"), &AnimationPlayer::seek, DEFVAL(false), DEFVAL(false));10241025ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation");1026ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation");1027ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay");1028ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_length");1029ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_position");10301031ADD_GROUP("Playback Options", "playback_");1032ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_auto_capture"), "set_auto_capture", "is_auto_capture");1033ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_auto_capture_duration", PROPERTY_HINT_NONE, "suffix:s"), "set_auto_capture_duration", "get_auto_capture_duration");1034ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Quad,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_auto_capture_transition_type", "get_auto_capture_transition_type");1035ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_auto_capture_ease_type", PROPERTY_HINT_ENUM, "In,Out,InOut,OutIn"), "set_auto_capture_ease_type", "get_auto_capture_ease_type");1036ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time");10371038ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-4,4,0.001,or_less,or_greater"), "set_speed_scale", "get_speed_scale");1039ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");10401041ADD_SIGNAL(MethodInfo(SNAME("current_animation_changed"), PropertyInfo(Variant::STRING_NAME, "name")));1042ADD_SIGNAL(MethodInfo(SNAME("animation_changed"), PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name")));1043}10441045AnimationPlayer::AnimationPlayer() {1046}10471048AnimationPlayer::~AnimationPlayer() {1049}105010511052