From be266138d7496e71b146455a63eb31b3fc697edc Mon Sep 17 00:00:00 2001 From: Malcolm Anderson Date: Mon, 28 Oct 2024 22:30:33 -0700 Subject: [PATCH] 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 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 Apply suggestions from code review Co-authored-by: Thaddeus Crews Apply suggestions from code review Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Early return from `tween_subtween` if the subtween is `null` --- doc/classes/SubtweenTweener.xml | 21 ++++++++++ doc/classes/Tween.xml | 21 ++++++++++ scene/animation/tween.cpp | 73 +++++++++++++++++++++++++++++++++ scene/animation/tween.h | 25 +++++++++++ scene/main/scene_tree.cpp | 8 +++- scene/main/scene_tree.h | 1 + scene/register_scene_types.cpp | 1 + 7 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 doc/classes/SubtweenTweener.xml diff --git a/doc/classes/SubtweenTweener.xml b/doc/classes/SubtweenTweener.xml new file mode 100644 index 00000000000..c8999ac4f3d --- /dev/null +++ b/doc/classes/SubtweenTweener.xml @@ -0,0 +1,21 @@ + + + + Runs a [Tween] nested within another [Tween]. + + + [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. + + + + + + + + + Sets the time in seconds after which the [SubtweenTweener] will start running the subtween. By default there's no delay. + + + + diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 3b5b6f7844b..45fb9f92a80 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -473,6 +473,27 @@ [/codeblocks] + + + + + 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. + + diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 7a2c7643558..4fa78cf5712 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -154,6 +154,25 @@ Ref Tween::tween_method(const Callable &p_callback, const Variant return tweener; } +Ref Tween::tween_subtween(const Ref &p_subtween) { + CHECK_VALID(); + + // Ensure that the subtween being added is not null. + ERR_FAIL_COND_V(p_subtween.is_null(), nullptr); + + Ref 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 p_tweener) { 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_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_subtween", "subtween"), &Tween::tween_subtween); ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step); ClassDB::bind_method(D_METHOD("stop"), &Tween::stop); @@ -512,6 +532,11 @@ Tween::Tween(bool p_valid) { valid = p_valid; } +Tween::Tween(SceneTree *p_parent_tree) { + parent_tree = p_parent_tree; + valid = true; +} + Ref PropertyTweener::from(const Variant &p_value) { Ref tween = _get_tween(); ERR_FAIL_COND_V(tween.is_null(), nullptr); @@ -854,3 +879,51 @@ MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from, MethodTweener::MethodTweener() { 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::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 &p_subtween) { + subtween = p_subtween; +} + +SubtweenTweener::SubtweenTweener() { + ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween."); +} diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 4b9ffbfae19..793a5aa4b4b 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -35,6 +35,7 @@ class Tween; class Node; +class SceneTree; class Tweener : public RefCounted { GDCLASS(Tweener, RefCounted); @@ -60,6 +61,7 @@ class PropertyTweener; class IntervalTweener; class CallbackTweener; class MethodTweener; +class SubtweenTweener; class Tween : public RefCounted { GDCLASS(Tween, RefCounted); @@ -109,6 +111,7 @@ private: EaseType default_ease = EaseType::EASE_IN_OUT; ObjectID bound_node; + SceneTree *parent_tree = nullptr; Vector>> tweeners; double total_time = 0; int current_step = -1; @@ -145,6 +148,7 @@ public: Ref tween_interval(double p_time); Ref tween_callback(const Callable &p_callback); Ref tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration); + Ref tween_subtween(const Ref &p_subtween); void append(Ref p_tweener); bool custom_step(double p_delta); @@ -187,6 +191,7 @@ public: Tween(); Tween(bool p_valid); + Tween(SceneTree *p_parent_tree); }; VARIANT_ENUM_CAST(Tween::TweenPauseMode); @@ -305,4 +310,24 @@ private: Ref ref_copy; }; +class SubtweenTweener : public Tweener { + GDCLASS(SubtweenTweener, Tweener); + +public: + Ref subtween; + void start() override; + bool step(double &r_delta) override; + + Ref set_delay(double p_delay); + + SubtweenTweener(const Ref &p_subtween); + SubtweenTweener(); + +protected: + static void _bind_methods(); + +private: + double delay = 0; +}; + #endif // TWEEN_H diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index a9fccb4746a..cabcbca65d0 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1583,11 +1583,17 @@ Ref SceneTree::create_timer(double p_delay_sec, bool p_process_a Ref SceneTree::create_tween() { _THREAD_SAFE_METHOD_ - Ref tween = memnew(Tween(true)); + Ref tween; + tween.instantiate(this); tweens.push_back(tween); return tween; } +bool SceneTree::remove_tween(const Ref &p_tween) { + _THREAD_SAFE_METHOD_ + return tweens.erase(p_tween); +} + TypedArray SceneTree::get_processed_tweens() { _THREAD_SAFE_METHOD_ TypedArray ret; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index f7df4c12a84..8d1d53917a4 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -411,6 +411,7 @@ public: Ref create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false); Ref create_tween(); + bool remove_tween(const Ref &p_tween); TypedArray get_processed_tweens(); //used by Main::start, don't use otherwise diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 64bc90f8843..53d8043329c 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -504,6 +504,7 @@ void register_scene_types() { GDREGISTER_CLASS(IntervalTweener); GDREGISTER_CLASS(CallbackTweener); GDREGISTER_CLASS(MethodTweener); + GDREGISTER_CLASS(SubtweenTweener); GDREGISTER_ABSTRACT_CLASS(AnimationMixer); GDREGISTER_CLASS(AnimationPlayer);