Skip to content

Commit

Permalink
Add YieldTweener
Browse files Browse the repository at this point in the history
  • Loading branch information
marko authored and Marigem committed May 23, 2024
1 parent 0a0942e commit b1a47a5
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 0 deletions.
31 changes: 31 additions & 0 deletions doc/classes/SceneTreeTween.xml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,37 @@
[/codeblock]
</description>
</method>
<method name="tween_yield">
<return type="YieldTweener" />
<argument index="0" name="object" type="Object" />
<argument index="1" name="signal" type="String" />
<description>
Creates and appends a [YieldTweener]. This method can be used to wait for a signal to be emitted and create asynchronous animations/cutscenes.
The animation will not progress to the next step until the yielded signal is emitted or the connection becomes invalid (e.g. as a result of freeing the target object). If you know that the emission may not happen, use [method YieldTweener.set_timeout].
[b]Note:[/b] If you are yielding a signal from a method called in the same [SceneTreeTween], make sure the signal is emitted [i]after[/i] the yield starts. If it can't be reasonably guaranteed, you can yield and emit in the same step:
[codeblock]
var tween = create_tween()
tween.tween_yield(object, "signal_name")
tween.parallel().tween_callback(object, "method_that_emits_signal")
[/codeblock]
[b]Example:[/b] An object launches itself and explodes upon collision or after 4 seconds.
[codeblock]
var tween = create_tween()
tween.tween_callback(self, "launch")
tween.tween_yield(self, "collided").set_timeout(4.0)
tween.tween_callback(self, "explode")
[/codeblock]
[b]Example:[/b] A character walks to a specific point, says some lines and walks back when the player closes dialogue.
[codeblock]
var tween = create_tween()
tween.tween_callback(self, "walk_to", [600.0])
tween.tween_yield(self, "destination_reached")
tween.tween_callback(self, "say_dialogue", ["stuff"])
tween.tween_yield(self, "dialogue_closed")
tween.tween_callback(self, "walk_to", [0.0])
[/codeblock]
</description>
</method>
<method name="tween_property">
<return type="PropertyTweener" />
<argument index="0" name="object" type="Object" />
Expand Down
21 changes: 21 additions & 0 deletions doc/classes/YieldTweener.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="YieldTweener" inherits="Tweener" version="3.6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Yields a specified signal.
</brief_description>
<description>
[YieldTweener] is used to wait for a specified signal to be emitted, allowing asynchronous steps in [SceneTreeTween] animation. See [method SceneTreeTween.tween_yield] for more usage information.
</description>
<tutorials>
</tutorials>
<methods>
<method name="set_timeout">
<return type="YieldTweener" />
<argument index="0" name="timeout" type="float" />
<description>
Sets the maximum time a [YieldTweener] can wait for the signal. Can be used as a safeguard for signals that may never be emitted.
[code]timeout[/code] should be greater than [code]0[/code]. If not specified, the tweener will wait indefinitely.
</description>
</method>
</methods>
</class>
65 changes: 65 additions & 0 deletions scene/animation/scene_tree_tween.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ Ref<MethodTweener> SceneTreeTween::tween_method(Object *p_target, StringName p_m
return tweener;
}

Ref<YieldTweener> SceneTreeTween::tween_yield(Object *p_target, StringName p_signal) {
ERR_FAIL_NULL_V(p_target, nullptr);
ERR_FAIL_COND_V_MSG(!valid, nullptr, "SceneTreeTween invalid. Either finished or created outside scene tree.");
ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a SceneTreeTween that has started. Use stop() first.");

Ref<YieldTweener> tweener = memnew(YieldTweener(p_target, p_signal));
append(tweener);
return tweener;
}

void SceneTreeTween::append(Ref<Tweener> p_tweener) {
p_tweener->set_tween(this);

Expand Down Expand Up @@ -582,6 +592,7 @@ void SceneTreeTween::_bind_methods() {
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &SceneTreeTween::tween_interval);
ClassDB::bind_method(D_METHOD("tween_callback", "object", "method", "binds"), &SceneTreeTween::tween_callback, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("tween_method", "object", "method", "from", "to", "duration", "binds"), &SceneTreeTween::tween_method, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("tween_yield", "object", "signal"), &SceneTreeTween::tween_yield);

ClassDB::bind_method(D_METHOD("custom_step", "delta"), &SceneTreeTween::custom_step);
ClassDB::bind_method(D_METHOD("stop"), &SceneTreeTween::stop);
Expand Down Expand Up @@ -938,3 +949,57 @@ MethodTweener::MethodTweener(Object *p_target, StringName p_method, Variant p_fr
MethodTweener::MethodTweener() {
ERR_FAIL_MSG("Can't create empty MethodTweener. Use get_tree().tween_method() instead.");
}

Ref<YieldTweener> YieldTweener::set_timeout(float p_timeout) {
timeout = p_timeout;
return this;
}

void YieldTweener::start() {
Object *target_instance = ObjectDB::get_instance(target);
if (!target_instance) {
return;
}

target_instance->connect(signal, this, "_signal_received");
}

bool YieldTweener::step(float &r_delta) {
if (received) {
return false;
}

if (timeout >= 0) {
timeout -= r_delta;
if (timeout <= 0) {
return false;
}
}

Object *target_instance = ObjectDB::get_instance(target);
if (!target_instance) {
return false;
}

r_delta = 0; // "Consume" all remaining time to prevent infinite loops.
return true;
}

YieldTweener::YieldTweener(Object *p_target, StringName p_signal) {
target = p_target->get_instance_id();
signal = p_signal;
}

YieldTweener::YieldTweener() {
ERR_FAIL_MSG("YieldTweener can't be created directly. Use the tween_yield() method in Tween.");
}

Variant YieldTweener::_signal_received(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
received = true;
return Variant();
}

void YieldTweener::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &YieldTweener::set_timeout);
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_received", &YieldTweener::_signal_received, MethodInfo("_signal_received"));
}
28 changes: 28 additions & 0 deletions scene/animation/scene_tree_tween.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class PropertyTweener;
class IntervalTweener;
class CallbackTweener;
class MethodTweener;
class YieldTweener;

class SceneTreeTween : public Reference {
GDCLASS(SceneTreeTween, Reference);
Expand Down Expand Up @@ -102,6 +103,7 @@ class SceneTreeTween : public Reference {
Ref<IntervalTweener> tween_interval(float p_time);
Ref<CallbackTweener> tween_callback(Object *p_target, StringName p_method, const Vector<Variant> &p_binds = Vector<Variant>());
Ref<MethodTweener> tween_method(Object *p_target, StringName p_method, Variant p_from, Variant p_to, float p_duration, const Vector<Variant> &p_binds = Vector<Variant>());
Ref<YieldTweener> tween_yield(Object *p_target, StringName p_signal);
void append(Ref<Tweener> p_tweener);

bool custom_step(float p_delta);
Expand Down Expand Up @@ -253,4 +255,30 @@ class MethodTweener : public Tweener {
Vector<Variant> binds;
};

class YieldTweener : public Tweener {
GDCLASS(YieldTweener, Tweener);

public:
Ref<YieldTweener> set_timeout(float p_timeout);

virtual void start();
virtual bool step(float &r_delta);

Variant _signal_received(const Variant **p_args, int p_argcount, Variant::CallError &r_error);

YieldTweener(Object *p_target, StringName p_signal);
YieldTweener();


protected:
static void _bind_methods();

private:
bool received = false;
float timeout = -1;

ObjectID target;
StringName signal;
};

#endif // SCENE_TREE_TWEEN_H
1 change: 1 addition & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ void register_scene_types() {
ClassDB::register_class<IntervalTweener>();
ClassDB::register_class<CallbackTweener>();
ClassDB::register_class<MethodTweener>();
ClassDB::register_class<YieldTweener>();

ClassDB::register_class<AnimationTreePlayer>();
ClassDB::register_class<AnimationTree>();
Expand Down

0 comments on commit b1a47a5

Please sign in to comment.