mirror of
https://github.com/godotengine/godot.git
synced 2025-11-01 06:01:14 +00:00
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 <kobewi4e@gmail.com> 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 <kobewi4e@gmail.com> Apply suggestions from code review Co-authored-by: Thaddeus Crews <repiteo@outlook.com> 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`
This commit is contained in:
parent
2582793d40
commit
be266138d7
7 changed files with 149 additions and 1 deletions
21
doc/classes/SubtweenTweener.xml
Normal file
21
doc/classes/SubtweenTweener.xml
Normal file
|
|
@ -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>
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue