Переглянути джерело

Add node started/finished signals for animation state machines

Adam Johnston 5 місяців тому
батько
коміт
d72006119d

+ 15 - 0
doc/classes/AnimationNodeStateMachinePlayback.xml

@@ -93,4 +93,19 @@
 	<members>
 		<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="true" />
 	</members>
+	<signals>
+		<signal name="state_finished">
+			<param index="0" name="state" type="StringName" />
+			<description>
+				Emitted when the [param state] finishes playback. If [param state] is a state machine set to grouped mode, its signals are passed through with its name prefixed.
+				If there is a crossfade, this will be fired when the influence of the [method get_fading_from_node] animation is no longer present.
+			</description>
+		</signal>
+		<signal name="state_started">
+			<param index="0" name="state" type="StringName" />
+			<description>
+				Emitted when the [param state] starts playback. If [param state] is a state machine set to grouped mode, its signals are passed through with its name prefixed.
+			</description>
+		</signal>
+	</signals>
 </class>

+ 41 - 15
scene/animation/animation_node_state_machine.cpp

@@ -200,10 +200,12 @@ void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine *
 		return;
 	}
 
+	AnimationTree *tree = p_state_machine->process_state ? p_state_machine->process_state->tree : nullptr;
 	Ref<AnimationNodeStateMachine> anodesm = p_state_machine->find_node_by_path(current);
 	if (anodesm.is_null()) {
 		group_start_transition = Ref<AnimationNodeStateMachineTransition>();
 		group_end_transition = Ref<AnimationNodeStateMachineTransition>();
+		_signal_state_change(tree, current, true);
 		return;
 	}
 
@@ -247,6 +249,8 @@ void AnimationNodeStateMachinePlayback::_set_current(AnimationNodeStateMachine *
 		if (anodesm_end_size != group_end_size) {
 			ERR_PRINT_ED("There is a mismatch in the number of end transitions in and out of the Grouped AnimationNodeStateMachine on AnimationNodeStateMachine: " + base_path + current + ".");
 		}
+	} else {
+		_signal_state_change(tree, current, true);
 	}
 }
 
@@ -346,12 +350,35 @@ float AnimationNodeStateMachinePlayback::get_fading_pos() const {
 	return fading_pos;
 }
 
+bool _is_grouped_state_machine(const Ref<AnimationNodeStateMachine> p_node) {
+	return p_node.is_valid() && p_node->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED;
+}
+
+void AnimationNodeStateMachinePlayback::_clear_fading(AnimationNodeStateMachine *p_state_machine, const StringName &p_state) {
+	if (!p_state.is_empty() && !_is_grouped_state_machine(p_state_machine->get_node(p_state))) {
+		_signal_state_change(p_state_machine->get_animation_tree(), p_state, false);
+	}
+	fading_from = StringName();
+	fadeing_from_nti = AnimationNode::NodeTimeInfo();
+}
+
+void AnimationNodeStateMachinePlayback::_signal_state_change(AnimationTree *p_animation_tree, const StringName &p_state, bool p_started) {
+	if (is_grouped && p_animation_tree && p_state != SceneStringName(Start) && p_state != SceneStringName(End)) {
+		AnimationNodeStateMachinePlayback *parent_playback = *_get_parent_playback(p_animation_tree);
+		if (parent_playback) {
+			String prefix = base_path.substr(base_path.rfind_char('/', base_path.length() - 2) + 1);
+			parent_playback->_signal_state_change(p_animation_tree, prefix + p_state, p_started);
+		}
+	}
+	emit_signal(p_started ? SceneStringName(state_started) : SceneStringName(state_finished), p_state);
+}
+
 void AnimationNodeStateMachinePlayback::_clear_path_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, bool p_test_only) {
 	List<AnimationNode::ChildNode> child_nodes;
 	p_state_machine->get_child_nodes(&child_nodes);
 	for (const AnimationNode::ChildNode &child_node : child_nodes) {
 		Ref<AnimationNodeStateMachine> anodesm = child_node.node;
-		if (anodesm.is_valid() && anodesm->get_state_machine_type() == AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
+		if (_is_grouped_state_machine(anodesm)) {
 			Ref<AnimationNodeStateMachinePlayback> playback = p_tree->get(base_path + child_node.name + "/playback");
 			ERR_FAIL_COND(playback.is_null());
 			playback->_set_base_path(base_path + child_node.name + "/");
@@ -678,8 +705,8 @@ bool AnimationNodeStateMachinePlayback::_make_travel_path(AnimationTree *p_tree,
 	}
 }
 
-AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
-	AnimationNode::NodeTimeInfo nti = _process(p_base_path, p_state_machine, p_playback_info, p_test_only);
+AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
+	AnimationNode::NodeTimeInfo nti = _process(p_state_machine, p_playback_info, p_test_only);
 	start_request = StringName();
 	next_request = false;
 	stop_request = false;
@@ -687,9 +714,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::process(const Str
 	return nti;
 }
 
-AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
-	_set_base_path(p_base_path);
-
+AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
 	AnimationTree *tree = p_state_machine->process_state->tree;
 
 	double p_time = p_playback_info.time;
@@ -789,7 +814,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St
 
 	if (teleport_request) {
 		teleport_request = false;
-		// Clear fadeing on teleport.
+		// Clear fading on teleport.
 		fading_from = StringName();
 		fadeing_from_nti = AnimationNode::NodeTimeInfo();
 		fading_pos = 0;
@@ -871,8 +896,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachinePlayback::_process(const St
 
 		if (Animation::is_greater_or_equal_approx(fading_pos, fading_time)) {
 			// Finish fading.
-			fading_from = StringName();
-			fadeing_from_nti = AnimationNode::NodeTimeInfo();
+			_clear_fading(p_state_machine, fading_from);
 		}
 	}
 
@@ -932,8 +956,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
 				pi.weight = 0;
 				p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only);
 			}
-			fading_from = StringName();
-			fadeing_from_nti = AnimationNode::NodeTimeInfo();
+			_clear_fading(p_state_machine, current);
 			fading_time = 0;
 			fading_pos = 0;
 		}
@@ -998,9 +1021,8 @@ bool AnimationNodeStateMachinePlayback::_can_transition_to_next(AnimationTree *p
 				playback = playback->duplicate();
 			}
 			playback->_next_main();
-			// Then, fadeing should be end.
-			fading_from = StringName();
-			fadeing_from_nti = AnimationNode::NodeTimeInfo();
+			// Then, fading should end.
+			_clear_fading(p_state_machine, fading_from);
 			fading_pos = 0;
 		} else {
 			return true;
@@ -1194,6 +1216,9 @@ void AnimationNodeStateMachinePlayback::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_current_length"), &AnimationNodeStateMachinePlayback::get_current_length);
 	ClassDB::bind_method(D_METHOD("get_fading_from_node"), &AnimationNodeStateMachinePlayback::get_fading_from_node);
 	ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::_get_travel_path);
+
+	ADD_SIGNAL(MethodInfo(SceneStringName(state_started), PropertyInfo(Variant::STRING_NAME, "state")));
+	ADD_SIGNAL(MethodInfo(SceneStringName(state_finished), PropertyInfo(Variant::STRING_NAME, "state")));
 }
 
 AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() {
@@ -1611,12 +1636,13 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
 AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
 	Ref<AnimationNodeStateMachinePlayback> playback_new = get_parameter(playback);
 	ERR_FAIL_COND_V(playback_new.is_null(), AnimationNode::NodeTimeInfo());
+	playback_new->_set_base_path(node_state.get_base_path());
 	playback_new->_set_grouped(state_machine_type == STATE_MACHINE_TYPE_GROUPED);
 	if (p_test_only) {
 		playback_new = playback_new->duplicate(); // Don't process original when testing.
 	}
 
-	return playback_new->process(node_state.get_base_path(), this, p_playback_info, p_test_only);
+	return playback_new->process(this, p_playback_info, p_test_only);
 }
 
 String AnimationNodeStateMachine::get_caption() const {

+ 4 - 2
scene/animation/animation_node_state_machine.h

@@ -288,6 +288,8 @@ class AnimationNodeStateMachinePlayback : public Resource {
 
 	bool is_grouped = false;
 
+	void _clear_fading(AnimationNodeStateMachine *p_state_machine, const StringName &p_state);
+	void _signal_state_change(AnimationTree *p_animation_tree, const StringName &p_state, bool p_started);
 	void _travel_main(const StringName &p_state, bool p_reset_on_teleport = true);
 	void _start_main(const StringName &p_state, bool p_reset = true);
 	void _next_main();
@@ -302,8 +304,8 @@ class AnimationNodeStateMachinePlayback : public Resource {
 	bool _travel_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_is_allow_transition_to_self, bool p_is_parent_same_state, bool p_test_only);
 	void _start_children(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, const String &p_path, bool p_test_only);
 
-	AnimationNode::NodeTimeInfo process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
-	AnimationNode::NodeTimeInfo _process(const String &p_base_path, AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
+	AnimationNode::NodeTimeInfo process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
+	AnimationNode::NodeTimeInfo _process(AnimationNodeStateMachine *p_state_machine, const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only);
 
 	bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
 	bool _transition_to_next_recursive(AnimationTree *p_tree, AnimationNodeStateMachine *p_state_machine, double p_delta, bool p_test_only);

+ 2 - 0
scene/scene_string_names.h

@@ -153,6 +153,8 @@ public:
 
 	const StringName Start = "Start";
 	const StringName End = "End";
+	const StringName state_started = "state_started";
+	const StringName state_finished = "state_finished";
 
 	const StringName FlatButton = "FlatButton";
 };