Skip to content

Commit c78d9d2

Browse files
committed
Merge pull request #98660 from Meorge/tween-subtween
Add `Tween.tween_subtween` method for nesting tweens within each other
2 parents be4678b + be26613 commit c78d9d2

File tree

7 files changed

+149
-1
lines changed

7 files changed

+149
-1
lines changed

doc/classes/SubtweenTweener.xml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="SubtweenTweener" inherits="Tweener" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
Runs a [Tween] nested within another [Tween].
5+
</brief_description>
6+
<description>
7+
[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.
8+
[b]Note:[/b] [method Tween.tween_subtween] is the only correct way to create [SubtweenTweener]. Any [SubtweenTweener] created manually will not function correctly.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="set_delay">
14+
<return type="SubtweenTweener" />
15+
<param index="0" name="delay" type="float" />
16+
<description>
17+
Sets the time in seconds after which the [SubtweenTweener] will start running the subtween. By default there's no delay.
18+
</description>
19+
</method>
20+
</methods>
21+
</class>

doc/classes/Tween.xml

+21
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,27 @@
473473
[/codeblocks]
474474
</description>
475475
</method>
476+
<method name="tween_subtween">
477+
<return type="SubtweenTweener" />
478+
<param index="0" name="subtween" type="Tween" />
479+
<description>
480+
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.
481+
[codeblock]
482+
# Subtween will rotate the object.
483+
var subtween = create_tween()
484+
subtween.tween_property(self, "rotation_degrees", 45.0, 1.0)
485+
subtween.tween_property(self, "rotation_degrees", 0.0, 1.0)
486+
487+
# Parent tween will execute the subtween as one of its steps.
488+
var tween = create_tween()
489+
tween.tween_property(self, "position:x", 500, 3.0)
490+
tween.tween_subtween(subtween)
491+
tween.tween_property(self, "position:x", 300, 2.0)
492+
[/codeblock]
493+
[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.
494+
[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.
495+
</description>
496+
</method>
476497
</methods>
477498
<signals>
478499
<signal name="finished">

scene/animation/tween.cpp

+73
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,25 @@ Ref<MethodTweener> Tween::tween_method(const Callable &p_callback, const Variant
154154
return tweener;
155155
}
156156

157+
Ref<SubtweenTweener> Tween::tween_subtween(const Ref<Tween> &p_subtween) {
158+
CHECK_VALID();
159+
160+
// Ensure that the subtween being added is not null.
161+
ERR_FAIL_COND_V(p_subtween.is_null(), nullptr);
162+
163+
Ref<SubtweenTweener> tweener;
164+
tweener.instantiate(p_subtween);
165+
166+
// Remove the tween from its parent tree, if it has one.
167+
// If the user created this tween without a parent tree attached,
168+
// then this step isn't necessary.
169+
if (tweener->subtween->parent_tree != nullptr) {
170+
tweener->subtween->parent_tree->remove_tween(tweener->subtween);
171+
}
172+
append(tweener);
173+
return tweener;
174+
}
175+
157176
void Tween::append(Ref<Tweener> p_tweener) {
158177
p_tweener->set_tween(this);
159178

@@ -447,6 +466,7 @@ void Tween::_bind_methods() {
447466
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
448467
ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
449468
ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
469+
ClassDB::bind_method(D_METHOD("tween_subtween", "subtween"), &Tween::tween_subtween);
450470

451471
ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
452472
ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
@@ -512,6 +532,11 @@ Tween::Tween(bool p_valid) {
512532
valid = p_valid;
513533
}
514534

535+
Tween::Tween(SceneTree *p_parent_tree) {
536+
parent_tree = p_parent_tree;
537+
valid = true;
538+
}
539+
515540
Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
516541
Ref<Tween> tween = _get_tween();
517542
ERR_FAIL_COND_V(tween.is_null(), nullptr);
@@ -854,3 +879,51 @@ MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from,
854879
MethodTweener::MethodTweener() {
855880
ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
856881
}
882+
883+
void SubtweenTweener::start() {
884+
elapsed_time = 0;
885+
finished = false;
886+
887+
// Reset the subtween.
888+
subtween->stop();
889+
subtween->play();
890+
}
891+
892+
bool SubtweenTweener::step(double &r_delta) {
893+
if (finished) {
894+
return false;
895+
}
896+
897+
elapsed_time += r_delta;
898+
899+
if (elapsed_time < delay) {
900+
r_delta = 0;
901+
return true;
902+
}
903+
904+
if (!subtween->step(r_delta)) {
905+
r_delta = elapsed_time - delay - subtween->get_total_time();
906+
_finish();
907+
return false;
908+
}
909+
910+
r_delta = 0;
911+
return true;
912+
}
913+
914+
Ref<SubtweenTweener> SubtweenTweener::set_delay(double p_delay) {
915+
delay = p_delay;
916+
return this;
917+
}
918+
919+
void SubtweenTweener::_bind_methods() {
920+
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &SubtweenTweener::set_delay);
921+
}
922+
923+
SubtweenTweener::SubtweenTweener(const Ref<Tween> &p_subtween) {
924+
subtween = p_subtween;
925+
}
926+
927+
SubtweenTweener::SubtweenTweener() {
928+
ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween.");
929+
}

scene/animation/tween.h

+25
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
class Tween;
3737
class Node;
38+
class SceneTree;
3839

3940
class Tweener : public RefCounted {
4041
GDCLASS(Tweener, RefCounted);
@@ -60,6 +61,7 @@ class PropertyTweener;
6061
class IntervalTweener;
6162
class CallbackTweener;
6263
class MethodTweener;
64+
class SubtweenTweener;
6365

6466
class Tween : public RefCounted {
6567
GDCLASS(Tween, RefCounted);
@@ -109,6 +111,7 @@ class Tween : public RefCounted {
109111
EaseType default_ease = EaseType::EASE_IN_OUT;
110112
ObjectID bound_node;
111113

114+
SceneTree *parent_tree = nullptr;
112115
Vector<List<Ref<Tweener>>> tweeners;
113116
double total_time = 0;
114117
int current_step = -1;
@@ -145,6 +148,7 @@ class Tween : public RefCounted {
145148
Ref<IntervalTweener> tween_interval(double p_time);
146149
Ref<CallbackTweener> tween_callback(const Callable &p_callback);
147150
Ref<MethodTweener> tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration);
151+
Ref<SubtweenTweener> tween_subtween(const Ref<Tween> &p_subtween);
148152
void append(Ref<Tweener> p_tweener);
149153

150154
bool custom_step(double p_delta);
@@ -187,6 +191,7 @@ class Tween : public RefCounted {
187191

188192
Tween();
189193
Tween(bool p_valid);
194+
Tween(SceneTree *p_parent_tree);
190195
};
191196

192197
VARIANT_ENUM_CAST(Tween::TweenPauseMode);
@@ -305,4 +310,24 @@ class MethodTweener : public Tweener {
305310
Ref<RefCounted> ref_copy;
306311
};
307312

313+
class SubtweenTweener : public Tweener {
314+
GDCLASS(SubtweenTweener, Tweener);
315+
316+
public:
317+
Ref<Tween> subtween;
318+
void start() override;
319+
bool step(double &r_delta) override;
320+
321+
Ref<SubtweenTweener> set_delay(double p_delay);
322+
323+
SubtweenTweener(const Ref<Tween> &p_subtween);
324+
SubtweenTweener();
325+
326+
protected:
327+
static void _bind_methods();
328+
329+
private:
330+
double delay = 0;
331+
};
332+
308333
#endif // TWEEN_H

scene/main/scene_tree.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -1583,11 +1583,17 @@ Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_a
15831583

15841584
Ref<Tween> SceneTree::create_tween() {
15851585
_THREAD_SAFE_METHOD_
1586-
Ref<Tween> tween = memnew(Tween(true));
1586+
Ref<Tween> tween;
1587+
tween.instantiate(this);
15871588
tweens.push_back(tween);
15881589
return tween;
15891590
}
15901591

1592+
bool SceneTree::remove_tween(const Ref<Tween> &p_tween) {
1593+
_THREAD_SAFE_METHOD_
1594+
return tweens.erase(p_tween);
1595+
}
1596+
15911597
TypedArray<Tween> SceneTree::get_processed_tweens() {
15921598
_THREAD_SAFE_METHOD_
15931599
TypedArray<Tween> ret;

scene/main/scene_tree.h

+1
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ class SceneTree : public MainLoop {
411411

412412
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);
413413
Ref<Tween> create_tween();
414+
bool remove_tween(const Ref<Tween> &p_tween);
414415
TypedArray<Tween> get_processed_tweens();
415416

416417
//used by Main::start, don't use otherwise

scene/register_scene_types.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ void register_scene_types() {
504504
GDREGISTER_CLASS(IntervalTweener);
505505
GDREGISTER_CLASS(CallbackTweener);
506506
GDREGISTER_CLASS(MethodTweener);
507+
GDREGISTER_CLASS(SubtweenTweener);
507508

508509
GDREGISTER_ABSTRACT_CLASS(AnimationMixer);
509510
GDREGISTER_CLASS(AnimationPlayer);

0 commit comments

Comments
 (0)