Browse Source

Fix NodeOneShot doesn't respect fade-out when aborting and improvement

Silc Lizard (Tokage) Renew 2 years ago
parent
commit
238bc9fe1f

+ 28 - 3
doc/classes/AnimationNodeOneShot.xml

@@ -18,10 +18,20 @@
 		# Alternative syntax (same result as above).
 		animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT
 
+		# Abort child animation with fading out connected to "shot" port.
+		animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT)
+		# Alternative syntax (same result as above).
+		animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT
+
 		# Get current state (read-only).
-		animation_tree.get("parameters/OneShot/active"))
+		animation_tree.get("parameters/OneShot/active")
 		# Alternative syntax (same result as above).
 		animation_tree["parameters/OneShot/active"]
+
+		# Get current internal state (read-only).
+		animation_tree.get("parameters/OneShot/internal_active")
+		# Alternative syntax (same result as above).
+		animation_tree["parameters/OneShot/internal_active"]
 		[/gdscript]
 		[csharp]
 		// Play child animation connected to "shot" port.
@@ -30,8 +40,14 @@
 		// Abort child animation connected to "shot" port.
 		animationTree.Set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT);
 
+		// Abort child animation with fading out connected to "shot" port.
+		animationTree.Set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT);
+
 		// Get current state (read-only).
 		animationTree.Get("parameters/OneShot/active");
+
+		// Get current internal state (read-only).
+		animationTree.Get("parameters/OneShot/internal_active");
 		[/csharp]
 		[/codeblocks]
 	</description>
@@ -50,11 +66,17 @@
 		<member name="autorestart_random_delay" type="float" setter="set_autorestart_random_delay" getter="get_autorestart_random_delay" default="0.0">
 			If [member autorestart] is [code]true[/code], a random additional delay (in seconds) between 0 and this value will be added to [member autorestart_delay].
 		</member>
+		<member name="fadein_curve" type="Curve" setter="set_fadein_curve" getter="get_fadein_curve">
+			Determines how cross-fading between animations is eased. If empty, the transition will be linear.
+		</member>
 		<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
-			The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a crossfade that starts at 0 second and ends at 1 second during the animation.
+			The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 0 second and ends at 1 second during the animation.
+		</member>
+		<member name="fadeout_curve" type="Curve" setter="set_fadeout_curve" getter="get_fadeout_curve">
+			Determines how cross-fading between animations is eased. If empty, the transition will be linear.
 		</member>
 		<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
-			The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a crossfade that starts at 4 second and ends at 5 second during the animation.
+			The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 4 second and ends at 5 second during the animation.
 		</member>
 		<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
 			The blend type.
@@ -70,6 +92,9 @@
 		<constant name="ONE_SHOT_REQUEST_ABORT" value="2" enum="OneShotRequest">
 			The request to stop the animation connected to "shot" port.
 		</constant>
+		<constant name="ONE_SHOT_REQUEST_FADE_OUT" value="3" enum="OneShotRequest">
+			The request to fade out the animation connected to "shot" port.
+		</constant>
 		<constant name="MIX_MODE_BLEND" value="0" enum="MixMode">
 			Blends two animations. See also [AnimationNodeBlend2].
 		</constant>

+ 120 - 42
scene/animation/animation_blend_tree.cpp

@@ -232,19 +232,20 @@ AnimationNodeSync::AnimationNodeSync() {
 }
 
 ////////////////////////////////////////////////////////
-
 void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
 	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::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort"));
+	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::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
 	r_list->push_back(PropertyInfo(Variant::FLOAT, 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));
 }
 
 Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const {
 	if (p_parameter == request) {
 		return ONE_SHOT_REQUEST_NONE;
-	} else if (p_parameter == active) {
+	} else if (p_parameter == active || p_parameter == internal_active) {
 		return false;
 	} else if (p_parameter == time_to_restart) {
 		return -1;
@@ -254,50 +255,66 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa
 }
 
 bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
-	if (p_parameter == active) {
+	if (p_parameter == active || p_parameter == internal_active) {
 		return true;
 	}
 	return false;
 }
 
-void AnimationNodeOneShot::set_fadein_time(double p_time) {
+void AnimationNodeOneShot::set_fade_in_time(double p_time) {
 	fade_in = p_time;
 }
 
-void AnimationNodeOneShot::set_fadeout_time(double p_time) {
-	fade_out = p_time;
+double AnimationNodeOneShot::get_fade_in_time() const {
+	return fade_in;
 }
 
-double AnimationNodeOneShot::get_fadein_time() const {
-	return fade_in;
+void AnimationNodeOneShot::set_fade_out_time(double p_time) {
+	fade_out = p_time;
 }
 
-double AnimationNodeOneShot::get_fadeout_time() const {
+double AnimationNodeOneShot::get_fade_out_time() const {
 	return fade_out;
 }
 
-void AnimationNodeOneShot::set_autorestart(bool p_active) {
-	autorestart = p_active;
+void AnimationNodeOneShot::set_fade_in_curve(const Ref<Curve> &p_curve) {
+	fade_in_curve = p_curve;
+}
+
+Ref<Curve> AnimationNodeOneShot::get_fade_in_curve() const {
+	return fade_in_curve;
+}
+
+void AnimationNodeOneShot::set_fade_out_curve(const Ref<Curve> &p_curve) {
+	fade_out_curve = p_curve;
+}
+
+Ref<Curve> AnimationNodeOneShot::get_fade_out_curve() const {
+	return fade_out_curve;
 }
 
-void AnimationNodeOneShot::set_autorestart_delay(double p_time) {
-	autorestart_delay = p_time;
+void AnimationNodeOneShot::set_auto_restart_enabled(bool p_enabled) {
+	auto_restart = p_enabled;
 }
 
-void AnimationNodeOneShot::set_autorestart_random_delay(double p_time) {
-	autorestart_random_delay = p_time;
+void AnimationNodeOneShot::set_auto_restart_delay(double p_time) {
+	auto_restart_delay = p_time;
 }
 
-bool AnimationNodeOneShot::has_autorestart() const {
-	return autorestart;
+void AnimationNodeOneShot::set_auto_restart_random_delay(double p_time) {
+	auto_restart_random_delay = p_time;
 }
 
-double AnimationNodeOneShot::get_autorestart_delay() const {
-	return autorestart_delay;
+bool AnimationNodeOneShot::is_auto_restart_enabled() const {
+	return auto_restart;
 }
 
-double AnimationNodeOneShot::get_autorestart_random_delay() const {
-	return autorestart_random_delay;
+double AnimationNodeOneShot::get_auto_restart_delay() const {
+	return auto_restart_delay;
+}
+
+double AnimationNodeOneShot::get_auto_restart_random_delay() const {
+	return auto_restart_random_delay;
 }
 
 void AnimationNodeOneShot::set_mix_mode(MixMode p_mix) {
@@ -319,17 +336,39 @@ bool AnimationNodeOneShot::has_filter() const {
 double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
 	OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
 	bool cur_active = get_parameter(active);
+	bool cur_internal_active = get_parameter(internal_active);
 	double cur_time = get_parameter(time);
 	double cur_remaining = get_parameter(remaining);
+	double cur_fade_out_remaining = get_parameter(fade_out_remaining);
 	double cur_time_to_restart = get_parameter(time_to_restart);
 
 	set_parameter(request, ONE_SHOT_REQUEST_NONE);
 
+	bool is_shooting = true;
+	bool clear_remaining_fade = false;
+	bool is_fading_out = cur_active == true && cur_internal_active == false;
+
+	if (p_time == 0 && p_seek && !p_is_external_seeking) {
+		clear_remaining_fade = true; // Reset occurs.
+	}
+
 	bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE;
 	if (cur_request == ONE_SHOT_REQUEST_ABORT) {
+		set_parameter(internal_active, false);
 		set_parameter(active, false);
 		set_parameter(time_to_restart, -1);
-		return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
+		is_shooting = false;
+	} else if (cur_request == ONE_SHOT_REQUEST_FADE_OUT && !is_fading_out) { // If fading, keep current fade.
+		if (cur_active) {
+			// Request fading.
+			is_fading_out = true;
+			cur_fade_out_remaining = fade_out;
+		} else {
+			// Shot is ended, do nothing.
+			is_shooting = false;
+		}
+		set_parameter(internal_active, false);
+		set_parameter(time_to_restart, -1);
 	} else if (!do_start && !cur_active) {
 		if (cur_time_to_restart >= 0.0 && !p_seek) {
 			cur_time_to_restart -= p_time;
@@ -339,19 +378,32 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
 			set_parameter(time_to_restart, cur_time_to_restart);
 		}
 		if (!do_start) {
-			return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
+			is_shooting = false;
 		}
 	}
 
 	bool os_seek = p_seek;
-	if (p_seek) {
-		cur_time = p_time;
+
+	if (clear_remaining_fade) {
+		os_seek = false;
+		cur_fade_out_remaining = 0;
+		set_parameter(fade_out_remaining, 0);
+		if (is_fading_out) {
+			is_fading_out = false;
+			set_parameter(internal_active, false);
+			set_parameter(active, false);
+		}
+	}
+
+	if (!is_shooting) {
+		return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
 	}
 
 	if (do_start) {
 		cur_time = 0;
 		os_seek = true;
 		set_parameter(request, ONE_SHOT_REQUEST_NONE);
+		set_parameter(internal_active, true);
 		set_parameter(active, true);
 	}
 
@@ -361,13 +413,25 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
 		if (fade_in > 0) {
 			use_blend = true;
 			blend = cur_time / fade_in;
+			if (fade_in_curve.is_valid()) {
+				blend = fade_in_curve->sample(blend);
+			}
 		} else {
 			blend = 0; // Should not happen.
 		}
-	} else if (!do_start && cur_remaining <= fade_out) {
+	} else if (!do_start && !is_fading_out && cur_remaining <= fade_out) {
+		is_fading_out = true;
+		cur_fade_out_remaining = cur_remaining;
+		set_parameter(internal_active, false);
+	}
+
+	if (is_fading_out) {
 		use_blend = true;
 		if (fade_out > 0) {
-			blend = (cur_remaining / fade_out);
+			blend = cur_fade_out_remaining / fade_out;
+			if (fade_out_curve.is_valid()) {
+				blend = 1.0 - fade_out_curve->sample(1.0 - blend);
+			}
 		} else {
 			blend = 0;
 		}
@@ -385,13 +449,17 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
 		cur_remaining = os_rem;
 	}
 
-	if (!p_seek) {
+	if (p_seek) {
+		cur_time = p_time;
+	} else {
 		cur_time += p_time;
 		cur_remaining = os_rem;
-		if (cur_remaining <= 0) {
+		cur_fade_out_remaining -= p_time;
+		if (cur_remaining <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
+			set_parameter(internal_active, false);
 			set_parameter(active, false);
-			if (autorestart) {
-				double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay;
+			if (auto_restart) {
+				double restart_sec = auto_restart_delay + Math::randd() * auto_restart_random_delay;
 				set_parameter(time_to_restart, restart_sec);
 			}
 		}
@@ -399,25 +467,32 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
 
 	set_parameter(time, cur_time);
 	set_parameter(remaining, cur_remaining);
+	set_parameter(fade_out_remaining, cur_fade_out_remaining);
 
 	return MAX(main_rem, cur_remaining);
 }
 
 void AnimationNodeOneShot::_bind_methods() {
-	ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fadein_time);
-	ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fadein_time);
+	ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fade_in_time);
+	ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fade_in_time);
+
+	ClassDB::bind_method(D_METHOD("set_fadein_curve", "curve"), &AnimationNodeOneShot::set_fade_in_curve);
+	ClassDB::bind_method(D_METHOD("get_fadein_curve"), &AnimationNodeOneShot::get_fade_in_curve);
+
+	ClassDB::bind_method(D_METHOD("set_fadeout_time", "time"), &AnimationNodeOneShot::set_fade_out_time);
+	ClassDB::bind_method(D_METHOD("get_fadeout_time"), &AnimationNodeOneShot::get_fade_out_time);
 
-	ClassDB::bind_method(D_METHOD("set_fadeout_time", "time"), &AnimationNodeOneShot::set_fadeout_time);
-	ClassDB::bind_method(D_METHOD("get_fadeout_time"), &AnimationNodeOneShot::get_fadeout_time);
+	ClassDB::bind_method(D_METHOD("set_fadeout_curve", "curve"), &AnimationNodeOneShot::set_fade_out_curve);
+	ClassDB::bind_method(D_METHOD("get_fadeout_curve"), &AnimationNodeOneShot::get_fade_out_curve);
 
-	ClassDB::bind_method(D_METHOD("set_autorestart", "enable"), &AnimationNodeOneShot::set_autorestart);
-	ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::has_autorestart);
+	ClassDB::bind_method(D_METHOD("set_autorestart", "active"), &AnimationNodeOneShot::set_auto_restart_enabled);
+	ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::is_auto_restart_enabled);
 
-	ClassDB::bind_method(D_METHOD("set_autorestart_delay", "enable"), &AnimationNodeOneShot::set_autorestart_delay);
-	ClassDB::bind_method(D_METHOD("get_autorestart_delay"), &AnimationNodeOneShot::get_autorestart_delay);
+	ClassDB::bind_method(D_METHOD("set_autorestart_delay", "time"), &AnimationNodeOneShot::set_auto_restart_delay);
+	ClassDB::bind_method(D_METHOD("get_autorestart_delay"), &AnimationNodeOneShot::get_auto_restart_delay);
 
-	ClassDB::bind_method(D_METHOD("set_autorestart_random_delay", "enable"), &AnimationNodeOneShot::set_autorestart_random_delay);
-	ClassDB::bind_method(D_METHOD("get_autorestart_random_delay"), &AnimationNodeOneShot::get_autorestart_random_delay);
+	ClassDB::bind_method(D_METHOD("set_autorestart_random_delay", "time"), &AnimationNodeOneShot::set_auto_restart_random_delay);
+	ClassDB::bind_method(D_METHOD("get_autorestart_random_delay"), &AnimationNodeOneShot::get_auto_restart_random_delay);
 
 	ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode);
 	ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode);
@@ -425,7 +500,9 @@ void AnimationNodeOneShot::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
 
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadein_time", "get_fadein_time");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadein_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadein_curve", "get_fadein_curve");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadeout_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadeout_curve", "get_fadeout_curve");
 
 	ADD_GROUP("Auto Restart", "autorestart_");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
@@ -436,6 +513,7 @@ void AnimationNodeOneShot::_bind_methods() {
 	BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE);
 	BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE);
 	BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT);
+	BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FADE_OUT);
 
 	BIND_ENUM_CONSTANT(MIX_MODE_BLEND);
 	BIND_ENUM_CONSTANT(MIX_MODE_ADD);

+ 24 - 13
scene/animation/animation_blend_tree.h

@@ -100,6 +100,7 @@ public:
 		ONE_SHOT_REQUEST_NONE,
 		ONE_SHOT_REQUEST_FIRE,
 		ONE_SHOT_REQUEST_ABORT,
+		ONE_SHOT_REQUEST_FADE_OUT,
 	};
 
 	enum MixMode {
@@ -109,17 +110,21 @@ public:
 
 private:
 	double fade_in = 0.0;
+	Ref<Curve> fade_in_curve;
 	double fade_out = 0.0;
+	Ref<Curve> fade_out_curve;
 
-	bool autorestart = false;
-	double autorestart_delay = 1.0;
-	double autorestart_random_delay = 0.0;
+	bool auto_restart = false;
+	double auto_restart_delay = 1.0;
+	double auto_restart_random_delay = 0.0;
 	MixMode mix = MIX_MODE_BLEND;
 
 	StringName request = PNAME("request");
 	StringName active = PNAME("active");
+	StringName internal_active = PNAME("internal_active");
 	StringName time = "time";
 	StringName remaining = "remaining";
+	StringName fade_out_remaining = "fade_out_remaining";
 	StringName time_to_restart = "time_to_restart";
 
 protected:
@@ -132,19 +137,25 @@ public:
 
 	virtual String get_caption() const override;
 
-	void set_fadein_time(double p_time);
-	void set_fadeout_time(double p_time);
+	void set_fade_in_time(double p_time);
+	double get_fade_in_time() const;
 
-	double get_fadein_time() const;
-	double get_fadeout_time() const;
+	void set_fade_in_curve(const Ref<Curve> &p_curve);
+	Ref<Curve> get_fade_in_curve() const;
 
-	void set_autorestart(bool p_active);
-	void set_autorestart_delay(double p_time);
-	void set_autorestart_random_delay(double p_time);
+	void set_fade_out_time(double p_time);
+	double get_fade_out_time() const;
 
-	bool has_autorestart() const;
-	double get_autorestart_delay() const;
-	double get_autorestart_random_delay() const;
+	void set_fade_out_curve(const Ref<Curve> &p_curve);
+	Ref<Curve> get_fade_out_curve() const;
+
+	void set_auto_restart_enabled(bool p_enabled);
+	void set_auto_restart_delay(double p_time);
+	void set_auto_restart_random_delay(double p_time);
+
+	bool is_auto_restart_enabled() const;
+	double get_auto_restart_delay() const;
+	double get_auto_restart_random_delay() const;
 
 	void set_mix_mode(MixMode p_mix);
 	MixMode get_mix_mode() const;