ソースを参照

Add `tween_subtween` method for nesting Tweens

No actual functionality yet

Actual subtween functionality implemented

Added documentation for Tween.tween_subtween and SubtweenTweener

Implemented some additional functions

`set_ease`, `set_trans`, and `set_delay`
Documentation only for `set_delay` so far, since I have tested it

Removed set_ease and set_trans

Upon further investigation, the way they are implemented for Tween doesn't appear to work here

Fixed indentation in documentation

Reset subtween when parent loops

Fix return type of `SubtweenTweener.set_delay`

Add notes to documentation

Apply suggestions from code review

Co-authored-by: Tomasz Chabora <[email protected]>

Apply some suggested changes

- Remove excessive documentation
- Add Tween constructor that takes in SceneTree
- Make `SubtweenTweener::subtween` public so that `Tween` doesn't have to be a friend class

Remove unneeded friend class SceneTree

Remove superfluous documentation describing subtween behavior

Apply suggestions from code review

Co-authored-by: Tomasz Chabora <[email protected]>

Apply suggestions from code review

Co-authored-by: Thaddeus Crews <[email protected]>

Apply suggestions from code review

Co-authored-by: A Thousand Ships <[email protected]>

Early return from `tween_subtween` if the subtween is `null`
Malcolm Anderson 9 ヶ月 前
コミット
be266138d7

+ 21 - 0
doc/classes/SubtweenTweener.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SubtweenTweener" inherits="Tweener" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Runs a [Tween] nested within another [Tween].
+	</brief_description>
+	<description>
+		[SubtweenTweener] is used to execute a [Tween] as one step in a sequence defined by another [Tween]. See [method Tween.tween_subtween] for more usage information.
+		[b]Note:[/b] [method Tween.tween_subtween] is the only correct way to create [SubtweenTweener]. Any [SubtweenTweener] created manually will not function correctly.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="set_delay">
+			<return type="SubtweenTweener" />
+			<param index="0" name="delay" type="float" />
+			<description>
+				Sets the time in seconds after which the [SubtweenTweener] will start running the subtween. By default there's no delay.
+			</description>
+		</method>
+	</methods>
+</class>

+ 21 - 0
doc/classes/Tween.xml

@@ -473,6 +473,27 @@
 				[/codeblocks]
 				[/codeblocks]
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="tween_subtween">
+			<return type="SubtweenTweener" />
+			<param index="0" name="subtween" type="Tween" />
+			<description>
+				Creates and appends a [SubtweenTweener]. This method can be used to nest [param subtween] within this [Tween], allowing for the creation of more complex and composable sequences.
+				[codeblock]
+				# Subtween will rotate the object.
+				var subtween = create_tween()
+				subtween.tween_property(self, "rotation_degrees", 45.0, 1.0)
+				subtween.tween_property(self, "rotation_degrees", 0.0, 1.0)
+
+				# Parent tween will execute the subtween as one of its steps.
+				var tween = create_tween()
+				tween.tween_property(self, "position:x", 500, 3.0)
+				tween.tween_subtween(subtween)
+				tween.tween_property(self, "position:x", 300, 2.0)
+				[/codeblock]
+				[b]Note:[/b] The methods [method pause], [method stop], and [method set_loops] can cause the parent [Tween] to get stuck on the subtween step; see the documentation for those methods for more information.
+				[b]Note:[/b] The pause and process modes set by [method set_pause_mode] and [method set_process_mode] on [param subtween] will be overridden by the parent [Tween]'s settings.
+			</description>
+		</method>
 	</methods>
 	</methods>
 	<signals>
 	<signals>
 		<signal name="finished">
 		<signal name="finished">

+ 73 - 0
scene/animation/tween.cpp

@@ -154,6 +154,25 @@ Ref<MethodTweener> Tween::tween_method(const Callable &p_callback, const Variant
 	return tweener;
 	return tweener;
 }
 }
 
 
+Ref<SubtweenTweener> Tween::tween_subtween(const Ref<Tween> &p_subtween) {
+	CHECK_VALID();
+
+	// Ensure that the subtween being added is not null.
+	ERR_FAIL_COND_V(p_subtween.is_null(), nullptr);
+
+	Ref<SubtweenTweener> tweener;
+	tweener.instantiate(p_subtween);
+
+	// Remove the tween from its parent tree, if it has one.
+	// If the user created this tween without a parent tree attached,
+	// then this step isn't necessary.
+	if (tweener->subtween->parent_tree != nullptr) {
+		tweener->subtween->parent_tree->remove_tween(tweener->subtween);
+	}
+	append(tweener);
+	return tweener;
+}
+
 void Tween::append(Ref<Tweener> p_tweener) {
 void Tween::append(Ref<Tweener> p_tweener) {
 	p_tweener->set_tween(this);
 	p_tweener->set_tween(this);
 
 
@@ -447,6 +466,7 @@ void Tween::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
 	ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
 	ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
 	ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
 	ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
 	ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
+	ClassDB::bind_method(D_METHOD("tween_subtween", "subtween"), &Tween::tween_subtween);
 
 
 	ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
 	ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
 	ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
 	ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
@@ -512,6 +532,11 @@ Tween::Tween(bool p_valid) {
 	valid = p_valid;
 	valid = p_valid;
 }
 }
 
 
+Tween::Tween(SceneTree *p_parent_tree) {
+	parent_tree = p_parent_tree;
+	valid = true;
+}
+
 Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
 Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
 	Ref<Tween> tween = _get_tween();
 	Ref<Tween> tween = _get_tween();
 	ERR_FAIL_COND_V(tween.is_null(), nullptr);
 	ERR_FAIL_COND_V(tween.is_null(), nullptr);
@@ -854,3 +879,51 @@ MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from,
 MethodTweener::MethodTweener() {
 MethodTweener::MethodTweener() {
 	ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
 	ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
 }
 }
+
+void SubtweenTweener::start() {
+	elapsed_time = 0;
+	finished = false;
+
+	// Reset the subtween.
+	subtween->stop();
+	subtween->play();
+}
+
+bool SubtweenTweener::step(double &r_delta) {
+	if (finished) {
+		return false;
+	}
+
+	elapsed_time += r_delta;
+
+	if (elapsed_time < delay) {
+		r_delta = 0;
+		return true;
+	}
+
+	if (!subtween->step(r_delta)) {
+		r_delta = elapsed_time - delay - subtween->get_total_time();
+		_finish();
+		return false;
+	}
+
+	r_delta = 0;
+	return true;
+}
+
+Ref<SubtweenTweener> SubtweenTweener::set_delay(double p_delay) {
+	delay = p_delay;
+	return this;
+}
+
+void SubtweenTweener::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_delay", "delay"), &SubtweenTweener::set_delay);
+}
+
+SubtweenTweener::SubtweenTweener(const Ref<Tween> &p_subtween) {
+	subtween = p_subtween;
+}
+
+SubtweenTweener::SubtweenTweener() {
+	ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween.");
+}

+ 25 - 0
scene/animation/tween.h

@@ -35,6 +35,7 @@
 
 
 class Tween;
 class Tween;
 class Node;
 class Node;
+class SceneTree;
 
 
 class Tweener : public RefCounted {
 class Tweener : public RefCounted {
 	GDCLASS(Tweener, RefCounted);
 	GDCLASS(Tweener, RefCounted);
@@ -60,6 +61,7 @@ class PropertyTweener;
 class IntervalTweener;
 class IntervalTweener;
 class CallbackTweener;
 class CallbackTweener;
 class MethodTweener;
 class MethodTweener;
+class SubtweenTweener;
 
 
 class Tween : public RefCounted {
 class Tween : public RefCounted {
 	GDCLASS(Tween, RefCounted);
 	GDCLASS(Tween, RefCounted);
@@ -109,6 +111,7 @@ private:
 	EaseType default_ease = EaseType::EASE_IN_OUT;
 	EaseType default_ease = EaseType::EASE_IN_OUT;
 	ObjectID bound_node;
 	ObjectID bound_node;
 
 
+	SceneTree *parent_tree = nullptr;
 	Vector<List<Ref<Tweener>>> tweeners;
 	Vector<List<Ref<Tweener>>> tweeners;
 	double total_time = 0;
 	double total_time = 0;
 	int current_step = -1;
 	int current_step = -1;
@@ -145,6 +148,7 @@ public:
 	Ref<IntervalTweener> tween_interval(double p_time);
 	Ref<IntervalTweener> tween_interval(double p_time);
 	Ref<CallbackTweener> tween_callback(const Callable &p_callback);
 	Ref<CallbackTweener> tween_callback(const Callable &p_callback);
 	Ref<MethodTweener> tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration);
 	Ref<MethodTweener> tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration);
+	Ref<SubtweenTweener> tween_subtween(const Ref<Tween> &p_subtween);
 	void append(Ref<Tweener> p_tweener);
 	void append(Ref<Tweener> p_tweener);
 
 
 	bool custom_step(double p_delta);
 	bool custom_step(double p_delta);
@@ -187,6 +191,7 @@ public:
 
 
 	Tween();
 	Tween();
 	Tween(bool p_valid);
 	Tween(bool p_valid);
+	Tween(SceneTree *p_parent_tree);
 };
 };
 
 
 VARIANT_ENUM_CAST(Tween::TweenPauseMode);
 VARIANT_ENUM_CAST(Tween::TweenPauseMode);
@@ -305,4 +310,24 @@ private:
 	Ref<RefCounted> ref_copy;
 	Ref<RefCounted> ref_copy;
 };
 };
 
 
+class SubtweenTweener : public Tweener {
+	GDCLASS(SubtweenTweener, Tweener);
+
+public:
+	Ref<Tween> subtween;
+	void start() override;
+	bool step(double &r_delta) override;
+
+	Ref<SubtweenTweener> set_delay(double p_delay);
+
+	SubtweenTweener(const Ref<Tween> &p_subtween);
+	SubtweenTweener();
+
+protected:
+	static void _bind_methods();
+
+private:
+	double delay = 0;
+};
+
 #endif // TWEEN_H
 #endif // TWEEN_H

+ 7 - 1
scene/main/scene_tree.cpp

@@ -1583,11 +1583,17 @@ Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_a
 
 
 Ref<Tween> SceneTree::create_tween() {
 Ref<Tween> SceneTree::create_tween() {
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_
-	Ref<Tween> tween = memnew(Tween(true));
+	Ref<Tween> tween;
+	tween.instantiate(this);
 	tweens.push_back(tween);
 	tweens.push_back(tween);
 	return tween;
 	return tween;
 }
 }
 
 
+bool SceneTree::remove_tween(const Ref<Tween> &p_tween) {
+	_THREAD_SAFE_METHOD_
+	return tweens.erase(p_tween);
+}
+
 TypedArray<Tween> SceneTree::get_processed_tweens() {
 TypedArray<Tween> SceneTree::get_processed_tweens() {
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_
 	TypedArray<Tween> ret;
 	TypedArray<Tween> ret;

+ 1 - 0
scene/main/scene_tree.h

@@ -411,6 +411,7 @@ public:
 
 
 	Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false);
 	Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false);
 	Ref<Tween> create_tween();
 	Ref<Tween> create_tween();
+	bool remove_tween(const Ref<Tween> &p_tween);
 	TypedArray<Tween> get_processed_tweens();
 	TypedArray<Tween> get_processed_tweens();
 
 
 	//used by Main::start, don't use otherwise
 	//used by Main::start, don't use otherwise

+ 1 - 0
scene/register_scene_types.cpp

@@ -504,6 +504,7 @@ void register_scene_types() {
 	GDREGISTER_CLASS(IntervalTweener);
 	GDREGISTER_CLASS(IntervalTweener);
 	GDREGISTER_CLASS(CallbackTweener);
 	GDREGISTER_CLASS(CallbackTweener);
 	GDREGISTER_CLASS(MethodTweener);
 	GDREGISTER_CLASS(MethodTweener);
+	GDREGISTER_CLASS(SubtweenTweener);
 
 
 	GDREGISTER_ABSTRACT_CLASS(AnimationMixer);
 	GDREGISTER_ABSTRACT_CLASS(AnimationMixer);
 	GDREGISTER_CLASS(AnimationPlayer);
 	GDREGISTER_CLASS(AnimationPlayer);