Skip to content

Add AnimationNodeObservers for signaling animation events#118789

Open
a-johnston wants to merge 1 commit into
godotengine:masterfrom
a-johnston:animation_node_observers
Open

Add AnimationNodeObservers for signaling animation events#118789
a-johnston wants to merge 1 commit into
godotengine:masterfrom
a-johnston:animation_node_observers

Conversation

@a-johnston
Copy link
Copy Markdown
Contributor

@a-johnston a-johnston commented Apr 20, 2026

This implements godotengine/godot-proposals#13386 For AnimationNodeOneShot, AnimationNodeTransition, AnimationNodeBlendSpace1D, and AnimationNodeBlendSpace2D.

The new parameter matches how it looks in the proposed mockup:

Screenshot 2026-04-20 at 11 28 07 AM

And here's how it appears in the tree itself:

Screenshot 2026-04-20 at 11 27 11 AM

I think this is OK although the view within the AnimationTree tab is a bit confusing without labels or tooltips.

Usage is to manually set the observer in the animation tree's parameter list and then in code like:

var observer := tree.get(observer_path) as AnimationNodeObserverOneShot
if observer:
    observer.started.connect(callback)

The following test project has scenes testing each of the new observer types. Arrow keys to drive the animation node(s) and escape to return to the main menu.

animation_tree_test.zip

@a-johnston a-johnston requested review from a team as code owners April 20, 2026 18:51
Comment thread scene/scene_string_names.h Outdated
@TokageItLab
Copy link
Copy Markdown
Member

TokageItLab commented Apr 22, 2026

The direction should be right, since we agreed on it during the animation meeting.

I suggest creating a base class called AnimationNodeObserver and extending it. After that, it would be useful to adopt this approach in Transition and BlendSpace as well, so that signals can be triggered when the highest-priority port changes; See also #111893 (comment).

At last, it would be better to include the following in the description of the NodeOneShot documentation: If you want to use signals, you can use [AnimationNodeObserverOneShot]. The same applies to each AnimationNode.

Comment thread scene/animation/animation_blend_tree.cpp Outdated
@a-johnston
Copy link
Copy Markdown
Contributor Author

I suggest creating a base class called AnimationNodeObserver and extending it. After that, it would be useful to adopt this approach in Transition and BlendSpace as well, so that signals can be triggered when the highest-priority port changes

Would anything live in that base class or would it stay empty for now? I omitted the shared base class in my first pass since the signal signatures would be different although the usage pattern was very similar.

Comment thread scene/animation/animation_blend_tree.cpp Outdated
@TokageItLab
Copy link
Copy Markdown
Member

Would anything live in that base class or would it stay empty for now? I omitted the shared base class in my first pass since the signal signatures would be different although the usage pattern was very similar.

I think empty class is fine, and you can register it as abstract class.

GDREGISTER_ABSTRACT_CLASS(AnimationNodeObserver);

FYI, the base class of Texture is empty.

class Texture : public Resource {
	GDCLASS(Texture, Resource);
};

@a-johnston
Copy link
Copy Markdown
Contributor Author

The base class turned out to be a good spot for the local to scene override. For the observer classes is the following signal set acceptable:

AnimationNodeOneShotObserver
- started()
- fadein_finished()  # Only fires if fade-in time > 0
- fadeout_started()  # Only fires if fade-out time > 0
- finished()

AnimationNodeBlendSpaceObserver
- closest_changed()

AnimationNodeTransitionObserver
- transition()  # fires immediately before requested animation becomes current, so both are queryable through the tree as `current_state` and `transition_request`
- fadein_finished()  # only fires if xfade_time > 0

In particular, should we pass the closest index through that signal? It would only be useful for fetching the position or associated animation root node, and isn't currently exposed anywhere else. I think it makes sense to just leave that empty and rely on users to track whatever state they need.

@TokageItLab
Copy link
Copy Markdown
Member

TokageItLab commented Apr 23, 2026

For the naming consistency:

  • It should be AnimationNodeObserverXXX
  • fadein should be separated like fade_in
    • We weren’t able to complete the unification of "fadexxx" into "fade_xxx" in time for the 4.0 launch, but it should be unified into it in 5.0; The names of C++'s functions should serve as the standard

Basically, signals should only be emitted in cases where they cannot be tracked state by the user (implicitly occurred by the core), and that is the case I was referring to. I have provided further details below:

AnimationNodeObserverOneShot

  • started()
    • It is useful for knowing that a transition has occurred when Auto Restart is enabled
  • fade_in_finished()
  • fade_out_started()
  • finished()

AnimationNodeObserverBlendSpace

  • state_changed(StringName p_point) or closest_point_changed(StringName p_point)
    • Especially in Discrete Blend mode, only one animation is played, so this is useful after crossing that boundary
    • The user inputs coordinates. Since the state is not updated until _process() is called once after setting the coordinates, simple closest-tracking results in a one-frame delay
    • BlendSpaces now have points with StringNames, see also Display and allow setting name/index of BlendSpace points #110369

AnimationNodeObserverTransition

  • state_changed(StringName p_state) or port_changed(StringName p_state)
    • It is useful for knowing that a transition has occurred when Auto Advance is enabled
  • fade_in_finished()

It's debatable whether we should standardize the naming of "changed" signals to “state_changed“ or whether they should indicate the arguments like "closest_point_changed / port_changed". I'd like to hear others' opinions as well.

@a-johnston a-johnston force-pushed the animation_node_observers branch 3 times, most recently from 2e412fe to ee7399d Compare April 23, 2026 23:56
@a-johnston a-johnston changed the title WIP animation node observers Adds AnimationNodeObservers for signaling animation events Apr 24, 2026
@a-johnston
Copy link
Copy Markdown
Contributor Author

This is more or less ready. I opted to use closest_point_changed for the blend space observer and crossfade_finished for the transition node but these can be renamed to whatever is desired. I also manually tested one shots with Auto Restart enabled and it behaves as expected, although I don't have it turned on in the sample project. The docs are pretty sparse right now, but I think for a somewhat self-explanatory experimental api it's ok. The only other thing I'd call out is that the signal names aren't being cached anywhere currently so it does create a StringName when emitting. I can add the StringNames as fields like the parameter names are on each instance although it felt a bit weird since they're instance members.

@a-johnston
Copy link
Copy Markdown
Contributor Author

CI failed with ERROR: Cannot open resource pack '/tmp/test_project.pck'. at: _setup (core/config/project_settings.cpp:691) which seems unrelated but I don't see a way for me to rerun just that step.

Comment thread scene/animation/animation_blend_tree.h Outdated
@AThousandShips AThousandShips changed the title Adds AnimationNodeObservers for signaling animation events Add AnimationNodeObservers for signaling animation events Apr 24, 2026
Comment thread scene/animation/animation_blend_tree.cpp Outdated
@a-johnston a-johnston force-pushed the animation_node_observers branch from ee7399d to 564a34f Compare April 24, 2026 21:23
Copy link
Copy Markdown
Member

@TokageItLab TokageItLab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be fine for the most part. It should be documented that Observers are available (such as link and sample code) in the extension classes of AnimationNodes that support Observers.

Since we are currently in feature freeze for 4.7, we will review this later at the AnimationMeeting as scope for 4.8.

@a-johnston
Copy link
Copy Markdown
Contributor Author

Ah right I forgot to update those other docs. I'll do that and separately I can make a docs pr for adding it to the animation tree tutorial. Although that would also need to wait for after 4.7 ofc

@a-johnston a-johnston force-pushed the animation_node_observers branch from 564a34f to 46e1c6b Compare April 24, 2026 22:24
Comment thread scene/animation/animation_tree.h Outdated
@a-johnston a-johnston force-pushed the animation_node_observers branch from 46e1c6b to a6fe6f0 Compare May 15, 2026 06:06
jak6jak added a commit to jak6jak-experiment/godot that referenced this pull request May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants