Add capture() and play_with_capture() as substitute of update capture
This commit is contained in:
parent
4e990cd7e5
commit
85d66cb4e8
|
@ -651,7 +651,7 @@
|
|||
Update at the keyframes.
|
||||
</constant>
|
||||
<constant name="UPDATE_CAPTURE" value="2" enum="UpdateMode">
|
||||
Same as linear interpolation, but also interpolates from the current value (i.e. dynamically at runtime) if the first key isn't at 0 seconds.
|
||||
Same as [constant UPDATE_CONTINUOUS] but works as a flag to capture the value of the current object and perform interpolation in some methods. See also [method AnimationMixer.capture] and [method AnimationPlayer.play_with_capture].
|
||||
</constant>
|
||||
<constant name="LOOP_NONE" value="0" enum="LoopMode">
|
||||
At both ends of the animation, the animation will stop playing.
|
||||
|
|
|
@ -36,6 +36,18 @@
|
|||
Manually advance the animations by the specified time (in seconds).
|
||||
</description>
|
||||
</method>
|
||||
<method name="capture">
|
||||
<return type="void" />
|
||||
<param index="0" name="name" type="StringName" />
|
||||
<param index="1" name="duration" type="float" />
|
||||
<param index="2" name="trans_type" type="int" enum="Tween.TransitionType" default="0" />
|
||||
<param index="3" name="ease_type" type="int" enum="Tween.EaseType" default="0" />
|
||||
<description>
|
||||
If the animation track specified by [param name] has an option [constant Animation.UPDATE_CAPTURE], stores current values of the objects indicated by the track path as a cache. If there is already a captured cache, the old cache is discarded.
|
||||
After this it will interpolate with current animation blending result during the playback process for the time specified by [param duration], working like a crossfade.
|
||||
You can specify [param trans_type] as the curve for the interpolation. For better results, it may be appropriate to specify [constant Tween.TRANS_LINEAR] for cases where the first key of the track begins with a non-zero value or where the key value does not change, and [constant Tween.TRANS_QUAD] for cases where the key value changes linearly.
|
||||
</description>
|
||||
</method>
|
||||
<method name="clear_caches">
|
||||
<return type="void" />
|
||||
<description>
|
||||
|
|
|
@ -110,6 +110,26 @@
|
|||
This method is a shorthand for [method play] with [code]custom_speed = -1.0[/code] and [code]from_end = true[/code], so see its description for more information.
|
||||
</description>
|
||||
</method>
|
||||
<method name="play_with_capture">
|
||||
<return type="void" />
|
||||
<param index="0" name="name" type="StringName" />
|
||||
<param index="1" name="duration" type="float" default="-1.0" />
|
||||
<param index="2" name="custom_blend" type="float" default="-1" />
|
||||
<param index="3" name="custom_speed" type="float" default="1.0" />
|
||||
<param index="4" name="from_end" type="bool" default="false" />
|
||||
<param index="5" name="trans_type" type="int" enum="Tween.TransitionType" default="0" />
|
||||
<param index="6" name="ease_type" type="int" enum="Tween.EaseType" default="0" />
|
||||
<description>
|
||||
See [method AnimationMixer.capture]. It is almost the same as the following:
|
||||
[codeblock]
|
||||
capture(name, duration, trans_type, ease_type)
|
||||
play(name, custom_blend, custom_speed, from_end)
|
||||
[/codeblock]
|
||||
If name is blank, it specifies [member assigned_animation].
|
||||
If [param duration] is a negative value, the duration is set to the interval between the current position and the first key, when [param from_end] is [code]true[/code], uses the interval between the current position and the last key instead.
|
||||
[b]Note:[/b] The [param duration] takes [member speed_scale] into account, but [param custom_speed] does not, because the capture cache is interpolated with the blend result and the result may contain multiple animations.
|
||||
</description>
|
||||
</method>
|
||||
<method name="queue">
|
||||
<return type="void" />
|
||||
<param index="0" name="name" type="StringName" />
|
||||
|
@ -125,7 +145,7 @@
|
|||
<param index="2" name="update_only" type="bool" default="false" />
|
||||
<description>
|
||||
Seeks the animation to the [param seconds] point in time (in seconds). If [param update] is [code]true[/code], the animation updates too, otherwise it updates at process time. Events between the current frame and [param seconds] are skipped.
|
||||
If [param update_only] is true, the method / audio / animation playback tracks will not be processed.
|
||||
If [param update_only] is [code]true[/code], the method / audio / animation playback tracks will not be processed.
|
||||
[b]Note:[/b] Seeking to the end of the animation doesn't emit [signal AnimationMixer.animation_finished]. If you want to skip animation and emit the signal, use [method AnimationMixer.advance].
|
||||
</description>
|
||||
</method>
|
||||
|
|
|
@ -552,6 +552,7 @@ void AnimationMixer::_clear_caches() {
|
|||
}
|
||||
track_cache.clear();
|
||||
cache_valid = false;
|
||||
capture_cache.clear();
|
||||
|
||||
emit_signal(SNAME("caches_cleared"));
|
||||
}
|
||||
|
@ -915,6 +916,7 @@ bool AnimationMixer::_update_caches() {
|
|||
void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
|
||||
_blend_init();
|
||||
if (_blend_pre_process(p_delta, track_count, track_map)) {
|
||||
_blend_capture(p_delta);
|
||||
_blend_calc_total_weight();
|
||||
_blend_process(p_delta, p_update_only);
|
||||
_blend_apply();
|
||||
|
@ -1013,6 +1015,43 @@ void AnimationMixer::_blend_post_process() {
|
|||
//
|
||||
}
|
||||
|
||||
void AnimationMixer::_blend_capture(double p_delta) {
|
||||
blend_capture(p_delta);
|
||||
}
|
||||
|
||||
void AnimationMixer::blend_capture(double p_delta) {
|
||||
if (capture_cache.animation.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
capture_cache.remain -= p_delta * capture_cache.step;
|
||||
if (capture_cache.remain <= 0.0) {
|
||||
capture_cache.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
real_t weight = Tween::run_equation(capture_cache.trans_type, capture_cache.ease_type, capture_cache.remain, 0.0, 1.0, 1.0);
|
||||
|
||||
// Blend with other animations.
|
||||
real_t inv = 1.0 - weight;
|
||||
for (AnimationInstance &ai : animation_instances) {
|
||||
ai.playback_info.weight *= inv;
|
||||
}
|
||||
|
||||
// Build capture animation instance.
|
||||
AnimationData ad;
|
||||
ad.animation = capture_cache.animation;
|
||||
|
||||
PlaybackInfo pi;
|
||||
pi.weight = weight;
|
||||
|
||||
AnimationInstance ai;
|
||||
ai.animation_data = ad;
|
||||
ai.playback_info = pi;
|
||||
|
||||
animation_instances.push_back(ai);
|
||||
}
|
||||
|
||||
void AnimationMixer::_blend_calc_total_weight() {
|
||||
for (const AnimationInstance &ai : animation_instances) {
|
||||
Ref<Animation> a = ai.animation_data.animation;
|
||||
|
@ -1848,6 +1887,10 @@ Vector3 AnimationMixer::get_root_motion_scale_accumulator() const {
|
|||
return root_motion_scale_accumulator;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* -- Reset on save --------------------------- */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
void AnimationMixer::set_reset_on_save_enabled(bool p_enabled) {
|
||||
reset_on_save = p_enabled;
|
||||
}
|
||||
|
@ -2011,6 +2054,50 @@ Ref<AnimatedValuesBackup> AnimationMixer::apply_reset(bool p_user_initiated) {
|
|||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* -- Capture feature ------------------------- */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) {
|
||||
ERR_FAIL_COND(!active);
|
||||
ERR_FAIL_COND(!has_animation(p_name));
|
||||
ERR_FAIL_COND(Math::is_zero_approx(p_duration));
|
||||
Ref<Animation> reference_animation = get_animation(p_name);
|
||||
|
||||
if (!cache_valid) {
|
||||
_update_caches(); // Need to retrieve object id.
|
||||
}
|
||||
|
||||
capture_cache.remain = 1.0;
|
||||
capture_cache.step = 1.0 / p_duration;
|
||||
capture_cache.trans_type = p_trans_type;
|
||||
capture_cache.ease_type = p_ease_type;
|
||||
capture_cache.animation.instantiate();
|
||||
|
||||
bool is_valid = false;
|
||||
for (int i = 0; i < reference_animation->get_track_count(); i++) {
|
||||
if (!reference_animation->track_is_enabled(i)) {
|
||||
continue;
|
||||
}
|
||||
if (reference_animation->track_get_type(i) == Animation::TYPE_VALUE && reference_animation->value_track_get_update_mode(i) == Animation::UPDATE_CAPTURE) {
|
||||
TrackCacheValue *t = static_cast<TrackCacheValue *>(track_cache[reference_animation->track_get_type_hash(i)]);
|
||||
Object *t_obj = ObjectDB::get_instance(t->object_id);
|
||||
if (t_obj) {
|
||||
Variant value = t_obj->get_indexed(t->subpath);
|
||||
int inserted_idx = capture_cache.animation->add_track(Animation::TYPE_VALUE);
|
||||
capture_cache.animation->track_set_path(inserted_idx, reference_animation->track_get_path(i));
|
||||
capture_cache.animation->track_insert_key(inserted_idx, 0, value);
|
||||
capture_cache.animation->value_track_set_update_mode(inserted_idx, Animation::UPDATE_CONTINUOUS);
|
||||
capture_cache.animation->track_set_interpolation_type(inserted_idx, Animation::INTERPOLATION_LINEAR);
|
||||
is_valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_valid) {
|
||||
capture_cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* -- General functions ----------------------- */
|
||||
/* -------------------------------------------- */
|
||||
|
@ -2118,9 +2205,14 @@ void AnimationMixer::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic");
|
||||
|
||||
/* ---- Reset on save ---- */
|
||||
ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationMixer::set_reset_on_save_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationMixer::is_reset_on_save_enabled);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled");
|
||||
|
||||
/* ---- Capture feature ---- */
|
||||
ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("mixer_updated")); // For updating dummy player.
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root_node", "get_root_node");
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#ifndef ANIMATION_MIXER_H
|
||||
#define ANIMATION_MIXER_H
|
||||
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
|
@ -334,12 +335,34 @@ protected:
|
|||
|
||||
void _blend_init();
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map);
|
||||
virtual void _blend_capture(double p_delta);
|
||||
void _blend_calc_total_weight(); // For undeterministic blending.
|
||||
void _blend_process(double p_delta, bool p_update_only = false);
|
||||
void _blend_apply();
|
||||
virtual void _blend_post_process();
|
||||
void _call_object(ObjectID p_object_id, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred);
|
||||
|
||||
/* ---- Capture feature ---- */
|
||||
struct CaptureCache {
|
||||
Ref<Animation> animation;
|
||||
double remain = 0.0;
|
||||
double step = 0.0;
|
||||
Tween::TransitionType trans_type = Tween::TRANS_LINEAR;
|
||||
Tween::EaseType ease_type = Tween::EASE_IN;
|
||||
|
||||
void clear() {
|
||||
animation.unref();
|
||||
remain = 0.0;
|
||||
step = 0.0;
|
||||
}
|
||||
|
||||
CaptureCache() {}
|
||||
~CaptureCache() {
|
||||
clear();
|
||||
}
|
||||
} capture_cache;
|
||||
void blend_capture(double p_delta); // To blend capture track with all other animations.
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1);
|
||||
|
||||
|
@ -400,9 +423,12 @@ public:
|
|||
virtual void advance(double p_time);
|
||||
virtual void clear_caches(); ///< must be called by hand if an animation was modified after added
|
||||
|
||||
/* ---- Capture feature ---- */
|
||||
void capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
|
||||
|
||||
/* ---- Reset on save ---- */
|
||||
void set_reset_on_save_enabled(bool p_enabled);
|
||||
bool is_reset_on_save_enabled() const;
|
||||
|
||||
bool can_apply_reset() const;
|
||||
void _build_backup_track_cache();
|
||||
Ref<AnimatedValuesBackup> make_backup();
|
||||
|
|
|
@ -310,6 +310,10 @@ bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, cons
|
|||
return true;
|
||||
}
|
||||
|
||||
void AnimationPlayer::_blend_capture(double p_delta) {
|
||||
blend_capture(p_delta * Math::abs(speed_scale));
|
||||
}
|
||||
|
||||
void AnimationPlayer::_blend_post_process() {
|
||||
if (end_reached) {
|
||||
// If the method track changes current animation, the animation is not finished.
|
||||
|
@ -366,13 +370,73 @@ void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_b
|
|||
play(p_name, p_custom_blend, -1, true);
|
||||
}
|
||||
|
||||
void 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) {
|
||||
StringName name = p_name;
|
||||
if (name == StringName()) {
|
||||
name = playback.assigned;
|
||||
}
|
||||
|
||||
if (signbit(p_duration)) {
|
||||
double max_dur = 0;
|
||||
Ref<Animation> anim = get_animation(name);
|
||||
if (anim.is_valid()) {
|
||||
double current_pos = playback.current.pos;
|
||||
if (playback.assigned != name) {
|
||||
current_pos = p_from_end ? anim->get_length() : 0;
|
||||
}
|
||||
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||
if (anim->track_get_type(i) != Animation::TYPE_VALUE) {
|
||||
continue;
|
||||
}
|
||||
if (anim->value_track_get_update_mode(i) != Animation::UPDATE_CAPTURE) {
|
||||
continue;
|
||||
}
|
||||
if (anim->track_get_key_count(i) == 0) {
|
||||
continue;
|
||||
}
|
||||
max_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);
|
||||
}
|
||||
}
|
||||
p_duration = max_dur;
|
||||
}
|
||||
|
||||
capture(name, p_duration, p_trans_type, p_ease_type);
|
||||
play(name, p_custom_blend, p_custom_scale, 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) == "") {
|
||||
if (name == StringName()) {
|
||||
name = playback.assigned;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
bool warn_enabled = false;
|
||||
if (capture_cache.animation.is_null()) {
|
||||
Ref<Animation> anim = get_animation(name);
|
||||
if (anim.is_valid()) {
|
||||
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||
if (anim->track_get_type(i) != Animation::TYPE_VALUE) {
|
||||
continue;
|
||||
}
|
||||
if (anim->value_track_get_update_mode(i) != Animation::UPDATE_CAPTURE) {
|
||||
continue;
|
||||
}
|
||||
if (anim->track_get_key_count(i) == 0) {
|
||||
continue;
|
||||
}
|
||||
warn_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (warn_enabled) {
|
||||
WARN_PRINT_ONCE_ED("Capture track found. If you want to interpolate animation with captured frame, you can use play_with_capture() instead of play().");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
|
||||
|
||||
Playback &c = playback;
|
||||
|
@ -417,7 +481,7 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
|
|||
}
|
||||
|
||||
if (get_current_animation() != p_name) {
|
||||
_clear_caches();
|
||||
_clear_playing_caches();
|
||||
}
|
||||
|
||||
c.current.from = &animation_set[name];
|
||||
|
@ -751,6 +815,7 @@ 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("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
|
||||
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);
|
||||
|
|
|
@ -128,6 +128,7 @@ protected:
|
|||
|
||||
// Make animation instances.
|
||||
virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
|
||||
virtual void _blend_capture(double p_delta) override;
|
||||
virtual void _blend_post_process() override;
|
||||
|
||||
virtual void _animation_removed(const StringName &p_name, const StringName &p_library) override;
|
||||
|
@ -157,6 +158,7 @@ public:
|
|||
|
||||
void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
|
||||
void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
|
||||
void play_with_capture(const StringName &p_name, double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
|
||||
void queue(const StringName &p_name);
|
||||
Vector<String> get_queue();
|
||||
void clear_queue();
|
||||
|
|
Loading…
Reference in New Issue