Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Add YieldTweener #92270

Open
wants to merge 1 commit into
base: 3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions doc/classes/SceneTreeTween.xml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,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>
</methods>
<signals>
<signal name="finished">
Expand Down
23 changes: 23 additions & 0 deletions doc/classes/YieldTweener.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?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>
<constants>
</constants>
</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("Can't create empty YieldTweener. Use get_tree().create_tween().tween_yield() instead.");
}

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"));
}
27 changes: 27 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,29 @@ 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
Loading