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

Add AwaitTweener #79712

Open
wants to merge 1 commit into
base: master
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
21 changes: 21 additions & 0 deletions doc/classes/AwaitTweener.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AwaitTweener" inherits="Tweener" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Awaits a specified signal.
</brief_description>
<description>
[AwaitTweener] is used to await a specified signal, allowing asynchronous steps in [Tween] animation. See [method Tween.tween_await] for more usage information.
The [signal Tweener.finished] signal is emitted when either the awaited signal is received, when timeout is reached, or when the target object is freed.
</description>
<tutorials>
</tutorials>
<methods>
<method name="set_timeout">
<return type="AwaitTweener" />
<param index="0" name="timeout" type="float" />
<description>
Sets the maximum time an [AwaitTweener] can wait for the signal. Can be used as a safeguard for signals that may never be emitted. If not specified, the tweener will wait indefinitely.
</description>
</method>
</methods>
</class>
30 changes: 30 additions & 0 deletions doc/classes/Tween.xml
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,36 @@
[b]Note:[/b] If a Tween is stopped and not bound to any node, it will exist indefinitely until manually started or invalidated. If you lose a reference to such Tween, you can retrieve it using [method SceneTree.get_processed_tweens].
</description>
</method>
<method name="tween_await">
<return type="AwaitTweener" />
<param index="0" name="signal" type="Signal" />
<description>
Creates and appends an [AwaitTweener]. This method can be used to await a signal to be emitted and create asynchronous animations or cutscenes.
The animation will not progress to the next step until the awaited 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 AwaitTweener.set_timeout].
[b]Example:[/b] An object launches itself and explodes upon collision or after 4 seconds.
[codeblock]
var tween = create_tween()
tween.tween_callback(launch)
tween.tween_await(collided).set_timeout(4.0)
tween.tween_callback(explode)
[/codeblock]
[b]Example:[/b] A character walks to a specific point, says some lines and walks back when the player closes the message box.
[codeblock]
var tween = create_tween()
tween.tween_callback(walk_to.bind(600.0))
tween.tween_await(destination_reached)
tween.tween_callback(say_dialogue.bind("Good day, sir!"))
tween.tween_await(dialogue_closed)
tween.tween_callback(walk_to.bind(0.0))
[/codeblock]
[b]Note:[/b] If you are awaiting a signal from a callback called in the same [Tween], make sure the signal is emitted [i]after[/i] the await starts. If it can't be reasonably guaranteed, you can await and emit in the same step:
[codeblock]
var tween = create_tween()
tween.tween_await(signal)
tween.parallel().tween_callback(method_that_emits_signal)
[/codeblock]
</description>
</method>
<method name="tween_callback">
<return type="CallbackTweener" />
<param index="0" name="callback" type="Callable" />
Expand Down
64 changes: 64 additions & 0 deletions scene/animation/tween.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ Ref<SubtweenTweener> Tween::tween_subtween(const Ref<Tween> &p_subtween) {
return tweener;
}

Ref<AwaitTweener> Tween::tween_await(const Signal &p_signal) {
CHECK_VALID();

Ref<AwaitTweener> tweener = memnew(AwaitTweener(p_signal));
append(tweener);
return tweener;
}

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

Expand Down Expand Up @@ -476,6 +484,7 @@ void Tween::_bind_methods() {
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("tween_await", "signal"), &Tween::tween_await);

ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
Expand Down Expand Up @@ -915,3 +924,58 @@ SubtweenTweener::SubtweenTweener(const Ref<Tween> &p_subtween) {
SubtweenTweener::SubtweenTweener() {
ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween.");
}

Ref<AwaitTweener> AwaitTweener::set_timeout(double p_timeout) {
timeout = p_timeout;
return this;
}

bool AwaitTweener::step(double &r_delta) {
if (finished) {
return false;
}

if (!signal.get_object() || !signal.is_connected(target_callable)) { // In case the object was destroyed before emitting.
_finish();
return false;
}

elapsed_time += r_delta;

if (timeout >= 0 && elapsed_time >= timeout) {
_finish();
r_delta = elapsed_time - timeout;
return false;
}

r_delta = 0; // "Consume" all remaining time to prevent infinite loops.
if (received) {
_finish();
return false;
}
return true;
}

AwaitTweener::AwaitTweener(const Signal &p_signal) {
signal = p_signal;
target_callable = Callable(this, SNAME("_signal_callback"));
signal.connect(target_callable);

Object *signal_instance = p_signal.get_object();
if (signal_instance && signal_instance->is_ref_counted()) {
ref_copy = signal_instance;
}
}

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

void AwaitTweener::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &AwaitTweener::set_timeout);
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &AwaitTweener::_signal_received, MethodInfo("_signal_callback"));
}

void AwaitTweener::_signal_received(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
received = true;
}
29 changes: 29 additions & 0 deletions scene/animation/tween.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class IntervalTweener;
class CallbackTweener;
class MethodTweener;
class SubtweenTweener;
class AwaitTweener;

class Tween : public RefCounted {
GDCLASS(Tween, RefCounted);
Expand Down Expand Up @@ -148,6 +149,7 @@ class Tween : public RefCounted {
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<SubtweenTweener> tween_subtween(const Ref<Tween> &p_subtween);
Ref<AwaitTweener> tween_await(const Signal &p_signal);
void append(Ref<Tweener> p_tweener);

bool custom_step(double p_delta);
Expand Down Expand Up @@ -324,3 +326,30 @@ class SubtweenTweener : public Tweener {
private:
double delay = 0;
};

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

public:
Ref<AwaitTweener> set_timeout(double p_timeout);

void start() override {} // Nothing to do at start.
bool step(double &r_delta) override;

AwaitTweener(const Signal &p_signal);
AwaitTweener();

protected:
static void _bind_methods();

private:
Signal signal;
Callable target_callable;
bool received = false;

double timeout = -1;

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

Ref<RefCounted> ref_copy;
};
1 change: 1 addition & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ void register_scene_types() {
GDREGISTER_CLASS(CallbackTweener);
GDREGISTER_CLASS(MethodTweener);
GDREGISTER_CLASS(SubtweenTweener);
GDREGISTER_CLASS(AwaitTweener);

GDREGISTER_ABSTRACT_CLASS(AnimationMixer);
GDREGISTER_CLASS(AnimationPlayer);
Expand Down