Merge pull request #70278 from TokageItLab/add-animation-started-finished-signal-to-tree

Add `animation_started/finished` signals to `AnimationTree` and fix time accuracy in StateMachine
This commit is contained in:
Rémi Verschelde 2022-12-23 09:08:17 +01:00
commit ecd895a860
No known key found for this signature in database
GPG Key ID: C3336907360768E1
5 changed files with 39 additions and 8 deletions

View File

@ -111,11 +111,25 @@
</member>
</members>
<signals>
<signal name="animation_finished">
<param index="0" name="anim_name" type="StringName" />
<description>
Notifies when an animation finished playing.
[b]Note:[/b] This signal is not emitted if an animation is looping or aborted. Also be aware of the possibility of unseen playback by sync and xfade.
</description>
</signal>
<signal name="animation_player_changed">
<description>
Emitted when the [member anim_player] is changed.
</description>
</signal>
<signal name="animation_started">
<param index="0" name="anim_name" type="StringName" />
<description>
Notifies when an animation starts playing.
[b]Note:[/b] This signal is not emitted if an animation is looping or playbacked from the middle. Also be aware of the possibility of unseen playback by sync and xfade.
</description>
</signal>
</signals>
<constants>
<constant name="ANIMATION_PROCESS_PHYSICS" value="0" enum="AnimationProcessCallback">

View File

@ -144,6 +144,19 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_ext
}
}
}
// Emit start & finish signal. Internally, the detections are the same for backward.
// We should use call_deferred since the track keys are still being prosessed.
if (state->tree) {
// AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
if (p_seek && !p_is_external_seeking && cur_time == 0) {
state->tree->call_deferred(SNAME("emit_signal"), "animation_started", animation);
}
// Finished.
if (prev_time < anim_size && cur_time >= anim_size) {
state->tree->call_deferred(SNAME("emit_signal"), "animation_finished", animation);
}
}
}
if (play_mode == PLAY_MODE_FORWARD) {

View File

@ -369,6 +369,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
// can't travel, then teleport
path.clear();
current = start_request;
play_start = true;
}
start_request = StringName(); //clear start request
}
@ -414,7 +415,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
fading_pos += p_time;
}
fade_blend = MIN(1.0, fading_pos / fading_time);
if (fade_blend >= 1.0) {
if (fade_blend > 1.0) {
fading_from = StringName();
}
}
@ -423,9 +424,9 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (current_curve.is_valid()) {
fade_blend = current_curve->sample(fade_blend);
}
float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
float fade_blend_inv = 1.0 - fade_blend;
double fade_blend_inv = 1.0 - fade_blend;
if (fading_from != StringName()) {
p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
}
@ -436,14 +437,14 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
{ //advance and loop check
float next_pos = len_current - rem;
double next_pos = len_current - rem;
end_loop = next_pos < pos_current;
pos_current = next_pos; //looped
}
//find next
StringName next;
float next_xfade = 0.0;
double next_xfade = 0.0;
AnimationNodeStateMachineTransition::SwitchMode switch_mode = AnimationNodeStateMachineTransition::SWITCH_MODE_IMMEDIATE;
if (path.size()) {

View File

@ -114,8 +114,8 @@ class AnimationNodeStateMachinePlayback : public Resource {
StringName next;
};
float len_current = 0.0;
float pos_current = 0.0;
double len_current = 0.0;
double pos_current = 0.0;
bool end_loop = false;
StringName current;

View File

@ -432,7 +432,6 @@ void AnimationNode::_bind_methods() {
GDVIRTUAL_BIND(_has_filter);
ADD_SIGNAL(MethodInfo("removed_from_graph"));
ADD_SIGNAL(MethodInfo("tree_changed"));
BIND_ENUM_CONSTANT(FILTER_IGNORE);
@ -2037,6 +2036,10 @@ void AnimationTree::_bind_methods() {
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL);
ADD_SIGNAL(MethodInfo("animation_player_changed"));
// Signals from AnimationNodes.
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name")));
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name")));
}
AnimationTree::AnimationTree() {