From 2c7c814a60ac37e99fc5a36f4b193f5688c0df41 Mon Sep 17 00:00:00 2001 From: Adam Johnston Date: Mon, 3 Feb 2025 15:50:06 -0800 Subject: [PATCH] Add node_notify signal for animation trees --- doc/classes/AnimationNode.xml | 6 ++++++ doc/classes/AnimationTree.xml | 7 +++++++ scene/animation/animation_blend_tree.cpp | 20 ++++++++++++------- scene/animation/animation_blend_tree.h | 1 + .../animation_node_state_machine.cpp | 9 +++++++++ scene/animation/animation_tree.cpp | 14 +++++++++++++ scene/animation/animation_tree.h | 8 ++++++++ scene/scene_string_names.h | 1 + 8 files changed, 59 insertions(+), 7 deletions(-) diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml index 5298727b0f7..fb755813e66 100644 --- a/doc/classes/AnimationNode.xml +++ b/doc/classes/AnimationNode.xml @@ -244,5 +244,11 @@ Paths matching the filter will be blended (by the blend value). + + The node has started playback. + + + The node has finished playback. + diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 4a63b35ba05..af25395a0ba 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -45,6 +45,13 @@ Emitted when the [member anim_player] is changed. + + + + + Emitted when the [param node] state changes during processing or playback. + + diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 019a14e556c..a2b4b03abce 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -696,6 +696,10 @@ AnimationNode::NodeTimeInfo AnimationNodeOneShot::_process(const AnimationMixer: set_parameter(fade_in_remaining, cur_fade_in_remaining); set_parameter(fade_out_remaining, cur_fade_out_remaining); + if (cur_active != (bool)get_parameter(active)) { + _notify_tree(cur_active ? NOTIFY_FINISHED : NOTIFY_STARTED); + } + return cur_internal_active ? os_nti : main_nti; } @@ -1451,6 +1455,7 @@ AnimationNodeOutput::AnimationNodeOutput() { void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref p_node, const Vector2 &p_position) { ERR_FAIL_COND(nodes.has(p_name)); ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(node_to_name.has(*p_node)); ERR_FAIL_COND(p_name == SceneStringName(output)); ERR_FAIL_COND(String(p_name).contains_char('/')); @@ -1459,6 +1464,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Refget_input_count()); nodes[p_name] = n; + node_to_name[*p_node] = p_name; emit_changed(); emit_signal(SNAME("tree_changed")); @@ -1476,12 +1482,10 @@ Ref AnimationNodeBlendTree::get_node(const StringName &p_name) co } StringName AnimationNodeBlendTree::get_node_name(const Ref &p_node) const { - for (const KeyValue &E : nodes) { - if (E.value.node == p_node) { - return E.key; - } + AHashMap::ConstIterator E = node_to_name.find(*p_node); + if (E) { + return E->value; } - ERR_FAIL_V(StringName()); } @@ -1525,6 +1529,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) { { Ref node = nodes[p_name].node; + node_to_name.erase(*node); node->disconnect(SNAME("tree_changed"), callable_mp(this, &AnimationNodeBlendTree::_tree_changed)); node->disconnect(SNAME("animation_node_renamed"), callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed)); node->disconnect(SNAME("animation_node_removed"), callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed)); @@ -1554,8 +1559,8 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN ERR_FAIL_COND(p_new_name == SceneStringName(output)); nodes[p_name].node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed)); - nodes[p_new_name] = nodes[p_name]; + node_to_name[*(nodes[p_new_name].node)] = p_new_name; nodes.erase(p_name); // Rename connections. @@ -1853,7 +1858,8 @@ void AnimationNodeBlendTree::_initialize_node_tree() { n.node = output; n.position = Vector2(300, 150); n.connections.resize(1); - nodes["output"] = n; + nodes[SceneStringName(output)] = n; + node_to_name[*n.node] = SceneStringName(output); } AnimationNodeBlendTree::AnimationNodeBlendTree() { diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 1b3d7f236e3..ec95aef9107 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -411,6 +411,7 @@ class AnimationNodeBlendTree : public AnimationRootNode { }; AHashMap nodes; + AHashMap node_to_name; Vector2 graph_offset; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 19acf8c1b16..0c7ba4c7871 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -194,6 +194,7 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine *p_state_machine, const StringName &p_state) { current = p_state; + if (current == StringName()) { group_start_transition = Ref(); group_end_transition = Ref(); @@ -490,6 +491,7 @@ void AnimationNodeStateMachinePlayback::_start(AnimationNodeStateMachine *p_stat teleport_request = true; stop_request = false; start_request = StringName(); + p_state_machine->_notify_tree(AnimationNode::NOTIFY_STARTED); } bool AnimationNodeStateMachinePlayback::_travel(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_is_allow_transition_to_self, bool p_test_only) { @@ -719,6 +721,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St travel_request = StringName(); path.clear(); playing = false; + p_state_machine->_notify_tree(AnimationNode::NOTIFY_FINISHED); return AnimationNode::NodeTimeInfo(); } @@ -883,8 +886,14 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St if (will_end || ((p_state_machine->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_NESTED) && !p_state_machine->has_transition_from(current))) { // There is no next transition. if (fading_from != StringName()) { + if (MAX(current_nti.get_remain(), fadeing_from_nti.get_remain()) <= 0) { + p_state_machine->_notify_tree(AnimationNode::NOTIFY_FINISHED); + } return Animation::is_greater_approx(current_nti.get_remain(), fadeing_from_nti.get_remain()) ? current_nti : fadeing_from_nti; } + if (current_nti.get_remain() <= 0) { + p_state_machine->_notify_tree(AnimationNode::NOTIFY_FINISHED); + } return current_nti; } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index f1c01b1e807..712a940ae16 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -587,6 +587,19 @@ void AnimationNode::_bind_methods() { BIND_ENUM_CONSTANT(FILTER_PASS); BIND_ENUM_CONSTANT(FILTER_STOP); BIND_ENUM_CONSTANT(FILTER_BLEND); + + BIND_ENUM_CONSTANT(NOTIFY_STARTED); + BIND_ENUM_CONSTANT(NOTIFY_FINISHED); +} + +void AnimationNode::_notify_tree(AnimationNode::NotifyReason p_reason) { + AnimationNodeBlendTree *blend_tree = Object::cast_to(node_state.parent); + if (blend_tree) { + StringName node_name = blend_tree->get_node_name(this); + if (!node_name.is_empty()) { + process_state->tree->emit_signal(SceneStringName(node_notify), node_name, p_reason); + } + } } AnimationNode::AnimationNode() { @@ -991,6 +1004,7 @@ void AnimationTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed"))); + ADD_SIGNAL(MethodInfo(SceneStringName(node_notify), PropertyInfo(Variant::STRING_NAME, "node"), PropertyInfo(Variant::INT, "reason"))); } AnimationTree::AnimationTree() { diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index d5b1d4ea460..7200ddcd737 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -54,6 +54,11 @@ public: FILTER_BLEND }; + enum NotifyReason { + NOTIFY_STARTED, + NOTIFY_FINISHED + }; + struct Input { String name; }; @@ -181,6 +186,8 @@ protected: void _validate_property(PropertyInfo &p_property) const; + void _notify_tree(AnimationNode::NotifyReason p_reason); + GDVIRTUAL0RC(Dictionary, _get_child_nodes) GDVIRTUAL0RC(Array, _get_parameter_list) GDVIRTUAL1RC(Ref, _get_child_by_name, StringName) @@ -243,6 +250,7 @@ public: }; VARIANT_ENUM_CAST(AnimationNode::FilterAction) +VARIANT_ENUM_CAST(AnimationNode::NotifyReason) // Root node does not allow inputs. class AnimationRootNode : public AnimationNode { diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 068487e4578..5475f982f78 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -85,6 +85,7 @@ public: const StringName sort_children = StaticCString::create("sort_children"); const StringName finished = StaticCString::create("finished"); + const StringName node_notify = StaticCString::create("node_notify"); const StringName animation_finished = StaticCString::create("animation_finished"); const StringName animation_changed = StaticCString::create("animation_changed"); const StringName animation_started = StaticCString::create("animation_started");