From a6fe6f0993e68c7f6e0f4de161496fd890d88c36 Mon Sep 17 00:00:00 2001 From: Adam Johnston Date: Sun, 19 Apr 2026 20:27:52 -0700 Subject: [PATCH] Add AnimationNodeObservers to notify animation events --- doc/classes/AnimationNodeBlendSpace1D.xml | 12 ++++ doc/classes/AnimationNodeBlendSpace2D.xml | 12 ++++ doc/classes/AnimationNodeObserver.xml | 13 +++++ .../AnimationNodeObserverBlendSpace.xml | 18 ++++++ doc/classes/AnimationNodeObserverOneShot.xml | 32 ++++++++++ .../AnimationNodeObserverTransition.xml | 24 ++++++++ doc/classes/AnimationNodeOneShot.xml | 11 ++++ doc/classes/AnimationNodeTransition.xml | 11 ++++ scene/animation/animation_blend_space_1d.cpp | 8 +++ scene/animation/animation_blend_space_2d.cpp | 8 +++ scene/animation/animation_blend_tree.cpp | 44 ++++++++++++++ scene/animation/animation_blend_tree.h | 26 +++++++++ scene/animation/animation_tree.cpp | 3 + scene/animation/animation_tree.h | 58 +++++++++++++------ scene/register_scene_types.cpp | 4 ++ 15 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 doc/classes/AnimationNodeObserver.xml create mode 100644 doc/classes/AnimationNodeObserverBlendSpace.xml create mode 100644 doc/classes/AnimationNodeObserverOneShot.xml create mode 100644 doc/classes/AnimationNodeObserverTransition.xml diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml index 64afe791b5a9..44ab6d503bfc 100644 --- a/doc/classes/AnimationNodeBlendSpace1D.xml +++ b/doc/classes/AnimationNodeBlendSpace1D.xml @@ -7,6 +7,18 @@ A resource used by [AnimationNodeBlendTree]. [AnimationNodeBlendSpace1D] represents a virtual axis on which any type of [AnimationRootNode]s can be added using [method add_blend_point]. Outputs the linear blend of the two [AnimationRootNode]s adjacent to the current value. You can set the extents of the axis with [member min_space] and [member max_space]. + + Optionally, signals can be used by setting an [AnimationNodeObserverBlendSpace] through [AnimationTree]. + [codeblocks] + [gdscript] + var observer = tree.get("parameters/BlendSpace/observer") + observer.closest_point_changed.connect(on_closest_point_changed) + [/gdscript] + [csharp] + var observer = animationTree.Get("parameters/BlendSpace/observer").As<AnimationNodeObserverBlendSpace>(); + observer.ClosestPointChanged += OnClosestPointChanged; + [/csharp] + [/codeblocks] $DOCS_URL/tutorials/animation/animation_tree.html diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index 51ee5f7eed1f..ce3b6b0153e5 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -7,6 +7,18 @@ A resource used by [AnimationNodeBlendTree]. [AnimationNodeBlendSpace2D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value. You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to triangulate the blend space by hand. + + Optionally, signals can be used by setting an [AnimationNodeObserverBlendSpace] through [AnimationTree]. + [codeblocks] + [gdscript] + var observer = tree.get("parameters/BlendSpace/observer") + observer.closest_point_changed.connect(on_closest_point_changed) + [/gdscript] + [csharp] + var observer = animationTree.Get("parameters/BlendSpace/observer").As<AnimationNodeObserverBlendSpace>(); + observer.ClosestPointChanged += OnClosestPointChanged; + [/csharp] + [/codeblocks] $DOCS_URL/tutorials/animation/animation_tree.html diff --git a/doc/classes/AnimationNodeObserver.xml b/doc/classes/AnimationNodeObserver.xml new file mode 100644 index 000000000000..4a51575e7c5c --- /dev/null +++ b/doc/classes/AnimationNodeObserver.xml @@ -0,0 +1,13 @@ + + + + Base class for observer types which relay [AnimationNode] events for a particular [AnimationTree] node. + + + + + + + + + diff --git a/doc/classes/AnimationNodeObserverBlendSpace.xml b/doc/classes/AnimationNodeObserverBlendSpace.xml new file mode 100644 index 000000000000..2b50db01afb6 --- /dev/null +++ b/doc/classes/AnimationNodeObserverBlendSpace.xml @@ -0,0 +1,18 @@ + + + + Observer for events from [AnimationNodeBlendSpace1D] and [AnimationNodeBlendSpace2D] instances. + + + + + + + + + + Emitted when the closest point to the current blend position changes. + + + + diff --git a/doc/classes/AnimationNodeObserverOneShot.xml b/doc/classes/AnimationNodeObserverOneShot.xml new file mode 100644 index 000000000000..e4b1b82d80bb --- /dev/null +++ b/doc/classes/AnimationNodeObserverOneShot.xml @@ -0,0 +1,32 @@ + + + + Observer for events from [AnimationNodeOneShot] instances. + + + + + + + + + Emitted when the one-shot has finished fading in. Only emitted if fade-in time is greater than zero. + + + + + Emitted when the one-shot has started fading out. Only emitted if fade-out time is greater than zero. + + + + + Emitted when the one-shot has finished. + + + + + Emitted when the one-shot has started. + + + + diff --git a/doc/classes/AnimationNodeObserverTransition.xml b/doc/classes/AnimationNodeObserverTransition.xml new file mode 100644 index 000000000000..c5e7d124401f --- /dev/null +++ b/doc/classes/AnimationNodeObserverTransition.xml @@ -0,0 +1,24 @@ + + + + Observer for events from [AnimationNodeTransition] instances. + + + + + + + + + + Emitted when transitioning out of a state. If crossfade time is greater than zero, emits when the crossfade finishes. Otherwise, emits immediately before [signal state_started] emits for the new state. + + + + + + Emitted when a transition to a new state starts. + + + + diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml index 57bb75a5fb08..52fef6fe5ee6 100644 --- a/doc/classes/AnimationNodeOneShot.xml +++ b/doc/classes/AnimationNodeOneShot.xml @@ -50,6 +50,17 @@ animationTree.Get("parameters/OneShot/internal_active"); [/csharp] [/codeblocks] + Optionally, signals can be used by setting an [AnimationNodeObserverOneShot] through [AnimationTree]. + [codeblocks] + [gdscript] + var observer = tree.get("parameters/OneShot/observer") + observer.started.connect(on_one_shot_started) + [/gdscript] + [csharp] + var observer = animationTree.Get("parameters/OneShot/observer").As<AnimationNodeObserverOneShot>(); + observer.Started += OnOneShotStarted; + [/csharp] + [/codeblocks] $DOCS_URL/tutorials/animation/animation_tree.html diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml index ee230b6c0ac0..f1cb0323e860 100644 --- a/doc/classes/AnimationNodeTransition.xml +++ b/doc/classes/AnimationNodeTransition.xml @@ -35,6 +35,17 @@ animationTree.Get("parameters/Transition/current_index"); [/csharp] [/codeblocks] + Optionally, signals can be used by setting an [AnimationNodeObserverTransition] through [AnimationTree]. + [codeblocks] + [gdscript] + var observer = animation_tree.get("parameters/Transition/observer") + observer.state_started.connect(on_state_started) + [/gdscript] + [csharp] + var observer = animationTree.Get("parameters/Transition/observer").As<AnimationNodeObserverTransition>(); + observer.StateStarted += OnStateStarted; + [/csharp] + [/codeblocks] $DOCS_URL/tutorials/animation/animation_tree.html diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index cbcf63a63839..af0046aeda24 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -37,6 +37,7 @@ void AnimationNodeBlendSpace1D::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position)); + r_list->push_back(PropertyInfo(Variant::OBJECT, observer, PROPERTY_HINT_RESOURCE_TYPE, AnimationNodeObserverBlendSpace::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE)); r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } @@ -595,6 +596,13 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(ProcessState &p_ } } + if (!p_test_only && new_closest != cur_closest && new_closest != -1) { + Ref observer_ref = p_instance.get_parameter_observer(); + if (observer_ref.is_valid()) { + observer_ref->emit_signal("closest_point_changed", get_blend_point_name(new_closest)); + } + } + p_instance.set_parameter_closest(new_closest, p_process_state.is_testing); return mind; } diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 95da8b8b0af9..7378596b1af8 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -39,6 +39,7 @@ void AnimationNodeBlendSpace2D::get_parameter_list(LocalVector *r_list) const { AnimationNode::get_parameter_list(r_list); r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position)); + r_list->push_back(PropertyInfo(Variant::OBJECT, observer, PROPERTY_HINT_RESOURCE_TYPE, AnimationNodeObserverBlendSpace::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE)); r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } @@ -755,6 +756,13 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(ProcessState &p_ } } + if (!p_test_only && new_closest != cur_closest && new_closest != -1) { + Ref observer_ref = p_instance.get_parameter_observer(); + if (observer_ref.is_valid()) { + observer_ref->emit_signal("closest_point_changed", get_blend_point_name(new_closest)); + } + } + p_instance.set_parameter_closest(new_closest, p_process_state.is_testing); return mind; } diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 83fef83d1c35..386654891f70 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -439,6 +439,7 @@ void AnimationNodeOneShot::get_parameter_list(LocalVector *r_list) r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); r_list->push_back(PropertyInfo(Variant::BOOL, internal_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort,Fade Out")); + r_list->push_back(PropertyInfo(Variant::OBJECT, observer, PROPERTY_HINT_RESOURCE_TYPE, AnimationNodeObserverOneShot::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE)); r_list->push_back(PropertyInfo(Variant::FLOAT, fade_in_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, fade_out_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); @@ -560,6 +561,21 @@ bool AnimationNodeOneShot::has_filter() const { return true; } +void AnimationNodeOneShot::_check_and_notify_state_changes(AnimationNodeInstance &p_instance, bool p_prev_active, bool p_prev_internal_active, double p_prev_fade_in_remaining) { + Ref observer_ref = p_instance.get_parameter_observer(); + if (observer_ref.is_valid()) { + if (!p_prev_active && p_instance.get_parameter_active()) { + observer_ref->emit_signal("started"); + } else if (p_prev_active && !p_instance.get_parameter_active()) { + observer_ref->emit_signal(SceneStringName(finished)); + } else if (Animation::is_greater_approx(p_prev_fade_in_remaining, 0) && Math::is_zero_approx(p_instance.get_parameter_fade_in_remaining())) { + observer_ref->emit_signal("fade_in_finished"); + } else if (get_fade_out_time() > 0 && p_prev_internal_active && !p_instance.get_parameter_internal_active()) { + observer_ref->emit_signal("fade_out_started"); + } + } +} + AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { OneShotRequest cur_request = static_cast(p_instance.get_parameter_request()); bool cur_active = p_instance.get_parameter_active(); @@ -579,6 +595,7 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(ProcessState &p_proce double abs_delta = Math::abs(p_delta); bool p_seek = p_playback_info.seeked; bool p_is_external_seeking = p_playback_info.is_external_seeking; + double prev_fade_in_remaining = cur_fade_in_remaining; bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE; @@ -628,6 +645,9 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(ProcessState &p_proce bool os_seek = p_seek; if (!is_shooting) { + if (!p_test_only) { + _check_and_notify_state_changes(p_instance, cur_active, cur_internal_active, prev_fade_in_remaining); + } AnimationMixer::PlaybackInfo pi = p_playback_info; pi.weight = 1.0; return blend_input(p_process_state, p_instance, 0, pi, FILTER_IGNORE, sync, p_test_only); @@ -728,6 +748,9 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(ProcessState &p_proce p_instance.set_parameter_fade_in_remaining(cur_fade_in_remaining, p_process_state.is_testing); p_instance.set_parameter_fade_out_remaining(cur_fade_out_remaining, p_process_state.is_testing); + if (!p_test_only) { + _check_and_notify_state_changes(p_instance, cur_active, cur_internal_active, prev_fade_in_remaining); + } return cur_internal_active ? os_nti : main_nti; } @@ -1165,6 +1188,7 @@ void AnimationNodeTransition::get_parameter_list(LocalVector *r_li r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally. r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::OBJECT, observer, PROPERTY_HINT_RESOURCE_TYPE, AnimationNodeObserverTransition::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE)); } Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { @@ -1287,6 +1311,16 @@ bool AnimationNodeTransition::is_allow_transition_to_self() const { return allow_transition_to_self; } +void AnimationNodeTransition::_signal_state_change(const AnimationNodeInstance &p_instance, bool p_starting, int p_state, bool p_test_only) { + if (p_test_only) { + return; + } + Ref observer_ref = p_instance.get_parameter_observer(); + if (observer_ref.is_valid()) { + observer_ref->emit_signal(p_starting ? SceneStringName(state_started) : SceneStringName(state_finished), get_input_name(p_state)); + } +} + AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only) { const String &cur_transition_request = p_instance.get_parameter_transition_request(); int cur_current_index = p_instance.get_parameter_current_index(); @@ -1333,6 +1367,10 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(ProcessState &p_pr } } else { switched = true; + if (xfade_time == 0) { + _signal_state_change(p_instance, false, cur_current_index, p_test_only); + } + _signal_state_change(p_instance, true, new_idx, p_test_only); cur_prev_index = cur_current_index; p_instance.set_parameter_prev_index(cur_current_index, p_process_state.is_testing); cur_current_index = new_idx; @@ -1346,6 +1384,9 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(ProcessState &p_pr } if (clear_remaining_fade) { + if (xfade_time > 0 && Animation::is_greater_approx(cur_prev_xfading, 0.0)) { + _signal_state_change(p_instance, false, cur_prev_index, p_test_only); + } cur_prev_xfading = 0; p_instance.set_parameter_prev_xfading(0, p_process_state.is_testing); cur_prev_index = -1; @@ -1416,6 +1457,9 @@ AnimationNode::NodeTimeInfo AnimationNodeTransition::_process(ProcessState &p_pr blend_input(p_process_state, p_instance, cur_prev_index, pi, FILTER_IGNORE, true, p_test_only); if (!p_seek) { if (Animation::is_less_or_equal_approx(cur_prev_xfading, 0)) { + if (xfade_time > 0) { + _signal_state_change(p_instance, false, cur_prev_index, p_test_only); + } p_instance.set_parameter_prev_index(-1, p_process_state.is_testing); } cur_prev_xfading -= Math::abs(p_playback_info.delta); diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 7a2c3835fa40..1e42fc1c9dc8 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -124,6 +124,18 @@ class AnimationNodeSync : public AnimationNode { AnimationNodeSync(); }; +class AnimationNodeObserverOneShot : public AnimationNodeObserver { + GDCLASS(AnimationNodeObserverOneShot, AnimationNodeObserver); + +protected: + static void _bind_methods() { + ADD_SIGNAL(MethodInfo("started")); + ADD_SIGNAL(MethodInfo("fade_in_finished")); + ADD_SIGNAL(MethodInfo("fade_out_started")); + ADD_SIGNAL(MethodInfo(SceneStringName(finished))); + } +}; + class AnimationNodeOneShot : public AnimationNodeSync { GDCLASS(AnimationNodeOneShot, AnimationNodeSync); @@ -160,6 +172,8 @@ class AnimationNodeOneShot : public AnimationNodeSync { StringName fade_out_remaining = "fade_out_remaining"; StringName time_to_restart = "time_to_restart"; + void _check_and_notify_state_changes(AnimationNodeInstance &p_instance, bool p_prev_active, bool p_prev_internal_active, double p_prev_fade_in_remaining); + protected: static void _bind_methods(); @@ -329,6 +343,16 @@ class AnimationNodeTimeSeek : public AnimationNode { AnimationNodeTimeSeek(); }; +class AnimationNodeObserverTransition : public AnimationNodeObserver { + GDCLASS(AnimationNodeObserverTransition, AnimationNodeObserver); + +protected: + static void _bind_methods() { + ADD_SIGNAL(MethodInfo(SceneStringName(state_started), PropertyInfo(Variant::STRING, "state"))); + ADD_SIGNAL(MethodInfo(SceneStringName(state_finished), PropertyInfo(Variant::STRING, "state"))); + } +}; + class AnimationNodeTransition : public AnimationNodeSync { GDCLASS(AnimationNodeTransition, AnimationNodeSync); @@ -351,6 +375,8 @@ class AnimationNodeTransition : public AnimationNodeSync { bool pending_update = false; + void _signal_state_change(const AnimationNodeInstance &p_instance, bool p_starting, int p_state, bool p_test_only); + protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 41ff9965d212..212eb8eab31f 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -61,6 +61,9 @@ Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter if (p_parameter == current_length || p_parameter == current_position || p_parameter == current_delta) { return 0.0; } + if (p_parameter == observer) { + return Ref(); + } GDVIRTUAL_CALL(_get_parameter_default_value, p_parameter, ret); return ret; } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index a57f9ca9695f..6125f9e1a54e 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -45,6 +45,24 @@ class AnimationNodeEndState; class AnimationTree; struct AnimationNodeInstance; +class AnimationNodeObserver : public Resource { + GDCLASS(AnimationNodeObserver, Resource); + +public: + AnimationNodeObserver() { + set_local_to_scene(true); + } +}; + +class AnimationNodeObserverBlendSpace : public AnimationNodeObserver { + GDCLASS(AnimationNodeObserverBlendSpace, AnimationNodeObserver); + +protected: + static void _bind_methods() { + ADD_SIGNAL(MethodInfo("closest_point_changed", PropertyInfo(Variant::STRING_NAME, "closest_point_name"))); + } +}; + class AnimationNode : public Resource { GDCLASS(AnimationNode, Resource); @@ -146,6 +164,7 @@ class AnimationNode : public Resource { StringName current_length = "current_length"; StringName current_position = "current_position"; StringName current_delta = "current_delta"; + StringName observer = "observer"; NodeTimeInfo process(ProcessState &p_process_state, AnimationNodeInstance &p_instance, const AnimationMixer::PlaybackInfo &p_playback_info, bool p_test_only = false); // Virtualizing for especially AnimationNodeAnimation needs to take "backward" into account. @@ -302,40 +321,41 @@ struct AnimationNodeInstance { X(0, CURRENT_LENGTH, current_length, Variant::FLOAT, double) \ X(1, CURRENT_POSITION, current_position, Variant::FLOAT, double) \ X(2, CURRENT_DELTA, current_delta, Variant::FLOAT, double) \ + X(3, OBSERVER, observer, Variant::OBJECT, Ref) \ /* AnimationNodeTimeScale. */ \ - X(3, TIME_SCALE, scale, Variant::FLOAT, double) \ + X(4, TIME_SCALE, scale, Variant::FLOAT, double) \ /* AnimationNodeTimeSeek. */ \ - X(3, SEEK_REQUEST, seek_request, Variant::FLOAT, double) \ + X(4, SEEK_REQUEST, seek_request, Variant::FLOAT, double) \ /* AnimationNodeAdd2, AnimationNodeAdd3. */ \ - X(3, ADD_AMOUNT, add_amount, Variant::FLOAT, double) \ + X(4, ADD_AMOUNT, add_amount, Variant::FLOAT, double) \ /* AnimationNodeBlend2, AnimationNodeBlend3. */ \ - X(3, BLEND_AMOUNT, blend_amount, Variant::FLOAT, double) \ + X(4, BLEND_AMOUNT, blend_amount, Variant::FLOAT, double) \ /* AnimationNodeSub2. */ \ - X(3, SUB_AMOUNT, sub_amount, Variant::FLOAT, double) \ + X(4, SUB_AMOUNT, sub_amount, Variant::FLOAT, double) \ /* AnimationNodeOneShot. */ \ - X(3, ONESHOT_TIME_TO_RESTART, time_to_restart, Variant::FLOAT, double) \ - X(4, ONESHOT_FADE_IN_REMAINING, fade_in_remaining, Variant::FLOAT, double) \ - X(5, ONESHOT_FADE_OUT_REMAINING, fade_out_remaining, Variant::FLOAT, double) \ - X(6, ONESHOT_ACTIVE, active, Variant::BOOL, bool) \ - X(7, ONESHOT_INTERNAL_ACTIVE, internal_active, Variant::BOOL, bool) \ - X(8, ONESHOT_REQUEST, request, Variant::INT, int) \ + X(4, ONESHOT_TIME_TO_RESTART, time_to_restart, Variant::FLOAT, double) \ + X(5, ONESHOT_FADE_IN_REMAINING, fade_in_remaining, Variant::FLOAT, double) \ + X(6, ONESHOT_FADE_OUT_REMAINING, fade_out_remaining, Variant::FLOAT, double) \ + X(7, ONESHOT_ACTIVE, active, Variant::BOOL, bool) \ + X(8, ONESHOT_INTERNAL_ACTIVE, internal_active, Variant::BOOL, bool) \ + X(9, ONESHOT_REQUEST, request, Variant::INT, int) \ /* AnimationNodeAnimation */ \ - X(3, ANIMATION_BACKWARD, backward, Variant::BOOL, bool) \ + X(4, ANIMATION_BACKWARD, backward, Variant::BOOL, bool) \ /* AnimationNodeTransition TODO: Make ref & */ \ - X(3, TRANSITION_REQUEST, transition_request, Variant::STRING, String) \ - X(4, CURRENT_INDEX, current_index, Variant::INT, int) \ - X(5, PREV_INDEX, prev_index, Variant::INT, int) \ - X(6, PREV_XFADING, prev_xfading, Variant::FLOAT, double) \ - X(7, CURRENT_STATE, current_state, Variant::STRING, String) \ + X(4, TRANSITION_REQUEST, transition_request, Variant::STRING, String) \ + X(5, CURRENT_INDEX, current_index, Variant::INT, int) \ + X(6, PREV_INDEX, prev_index, Variant::INT, int) \ + X(7, PREV_XFADING, prev_xfading, Variant::FLOAT, double) \ + X(8, CURRENT_STATE, current_state, Variant::STRING, String) \ /* AnimationNodeBlendSpace1D and AnimationNodeBlendSpace2D, \ We currently cannot do blend_position, due to type same name but different type */ \ - X(3, CLOSEST, closest, Variant::INT, int) + X(4, CLOSEST, closest, Variant::INT, int) enum Slot : uint8_t { #define SLOT_ENUM(index, e, _member, _variant_type, _native_type) SLOT_##e = index, ANIM_SLOT_LIST(SLOT_ENUM) #undef SLOT_ENUM - SLOT_MAX = 9 + SLOT_MAX = 10 }; void maybe_bind_slot_property(const StringName &p_name, Variant *p_property) { diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index ffd748c04627..733e2315fc05 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -593,10 +593,12 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationPlayer); GDREGISTER_CLASS(AnimationTree); GDREGISTER_CLASS(AnimationNode); + GDREGISTER_ABSTRACT_CLASS(AnimationNodeObserver); GDREGISTER_CLASS(AnimationRootNode); GDREGISTER_CLASS(AnimationNodeBlendTree); GDREGISTER_CLASS(AnimationNodeBlendSpace1D); GDREGISTER_CLASS(AnimationNodeBlendSpace2D); + GDREGISTER_CLASS(AnimationNodeObserverBlendSpace); GDREGISTER_CLASS(AnimationNodeStateMachine); GDREGISTER_CLASS(AnimationNodeStateMachinePlayback); GDREGISTER_VIRTUAL_CLASS(AnimationNodeExtension); @@ -605,6 +607,7 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationNodeStateMachineTransition); GDREGISTER_CLASS(AnimationNodeOutput); GDREGISTER_CLASS(AnimationNodeOneShot); + GDREGISTER_CLASS(AnimationNodeObserverOneShot); GDREGISTER_CLASS(AnimationNodeAnimation); GDREGISTER_CLASS(AnimationNodeAdd2); GDREGISTER_CLASS(AnimationNodeAdd3); @@ -614,6 +617,7 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationNodeTimeScale); GDREGISTER_CLASS(AnimationNodeTimeSeek); GDREGISTER_CLASS(AnimationNodeTransition); + GDREGISTER_CLASS(AnimationNodeObserverTransition); GDREGISTER_CLASS(ShaderGlobalsOverride); // can be used in any shader