1/**************************************************************************/
2/* tween.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "tween.h"
32
33#include "scene/animation/easing_equations.h"
34#include "scene/main/node.h"
35#include "scene/resources/animation.h"
36
37#define CHECK_VALID() \
38 ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); \
39 ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first.");
40
41Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = {
42 { &linear::in, &linear::in, &linear::in, &linear::in }, // Linear is the same for each easing.
43 { &sine::in, &sine::out, &sine::in_out, &sine::out_in },
44 { &quint::in, &quint::out, &quint::in_out, &quint::out_in },
45 { &quart::in, &quart::out, &quart::in_out, &quart::out_in },
46 { &quad::in, &quad::out, &quad::in_out, &quad::out_in },
47 { &expo::in, &expo::out, &expo::in_out, &expo::out_in },
48 { &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in },
49 { &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in },
50 { &circ::in, &circ::out, &circ::in_out, &circ::out_in },
51 { &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in },
52 { &back::in, &back::out, &back::in_out, &back::out_in },
53 { &spring::in, &spring::out, &spring::in_out, &spring::out_in },
54};
55
56void Tweener::set_tween(const Ref<Tween> &p_tween) {
57 tween = p_tween;
58}
59
60void Tweener::clear_tween() {
61 tween.unref();
62}
63
64void Tweener::_bind_methods() {
65 ADD_SIGNAL(MethodInfo("finished"));
66}
67
68bool Tween::_validate_type_match(const Variant &p_from, Variant &r_to) {
69 if (p_from.get_type() != r_to.get_type()) {
70 // Cast r_to between double and int to avoid minor annoyances.
71 if (p_from.get_type() == Variant::FLOAT && r_to.get_type() == Variant::INT) {
72 r_to = double(r_to);
73 } else if (p_from.get_type() == Variant::INT && r_to.get_type() == Variant::FLOAT) {
74 r_to = int(r_to);
75 } else {
76 ERR_FAIL_V_MSG(false, "Type mismatch between initial and final value: " + Variant::get_type_name(p_from.get_type()) + " and " + Variant::get_type_name(r_to.get_type()));
77 }
78 }
79 return true;
80}
81
82void Tween::_start_tweeners() {
83 if (tweeners.is_empty()) {
84 dead = true;
85 ERR_FAIL_MSG("Tween without commands, aborting.");
86 }
87
88 for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
89 tweener->start();
90 }
91}
92
93void Tween::_stop_internal(bool p_reset) {
94 running = false;
95 if (p_reset) {
96 started = false;
97 dead = false;
98 total_time = 0;
99 }
100}
101
102Ref<PropertyTweener> Tween::tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration) {
103 ERR_FAIL_NULL_V(p_target, nullptr);
104 CHECK_VALID();
105
106 Vector<StringName> property_subnames = p_property.get_as_property_path().get_subnames();
107 if (!_validate_type_match(p_target->get_indexed(property_subnames), p_to)) {
108 return nullptr;
109 }
110
111 Ref<PropertyTweener> tweener = memnew(PropertyTweener(p_target, property_subnames, p_to, p_duration));
112 append(tweener);
113 return tweener;
114}
115
116Ref<IntervalTweener> Tween::tween_interval(double p_time) {
117 CHECK_VALID();
118
119 Ref<IntervalTweener> tweener = memnew(IntervalTweener(p_time));
120 append(tweener);
121 return tweener;
122}
123
124Ref<CallbackTweener> Tween::tween_callback(const Callable &p_callback) {
125 CHECK_VALID();
126
127 Ref<CallbackTweener> tweener = memnew(CallbackTweener(p_callback));
128 append(tweener);
129 return tweener;
130}
131
132Ref<MethodTweener> Tween::tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration) {
133 CHECK_VALID();
134
135 if (!_validate_type_match(p_from, p_to)) {
136 return nullptr;
137 }
138
139 Ref<MethodTweener> tweener = memnew(MethodTweener(p_callback, p_from, p_to, p_duration));
140 append(tweener);
141 return tweener;
142}
143
144void Tween::append(Ref<Tweener> p_tweener) {
145 p_tweener->set_tween(this);
146
147 if (parallel_enabled) {
148 current_step = MAX(current_step, 0);
149 } else {
150 current_step++;
151 }
152 parallel_enabled = default_parallel;
153
154 tweeners.resize(current_step + 1);
155 tweeners.write[current_step].push_back(p_tweener);
156}
157
158void Tween::stop() {
159 _stop_internal(true);
160}
161
162void Tween::pause() {
163 _stop_internal(false);
164}
165
166void Tween::play() {
167 ERR_FAIL_COND_MSG(!valid, "Tween invalid. Either finished or created outside scene tree.");
168 ERR_FAIL_COND_MSG(dead, "Can't play finished Tween, use stop() first to reset its state.");
169 running = true;
170}
171
172void Tween::kill() {
173 running = false; // For the sake of is_running().
174 dead = true;
175}
176
177bool Tween::is_running() {
178 return running;
179}
180
181bool Tween::is_valid() {
182 return valid;
183}
184
185void Tween::clear() {
186 valid = false;
187
188 for (List<Ref<Tweener>> &step : tweeners) {
189 for (Ref<Tweener> &tweener : step) {
190 tweener->clear_tween();
191 }
192 }
193 tweeners.clear();
194}
195
196Ref<Tween> Tween::bind_node(const Node *p_node) {
197 ERR_FAIL_NULL_V(p_node, this);
198
199 bound_node = p_node->get_instance_id();
200 is_bound = true;
201 return this;
202}
203
204Ref<Tween> Tween::set_process_mode(TweenProcessMode p_mode) {
205 process_mode = p_mode;
206 return this;
207}
208
209Tween::TweenProcessMode Tween::get_process_mode() {
210 return process_mode;
211}
212
213Ref<Tween> Tween::set_pause_mode(TweenPauseMode p_mode) {
214 pause_mode = p_mode;
215 return this;
216}
217
218Tween::TweenPauseMode Tween::get_pause_mode() {
219 return pause_mode;
220}
221
222Ref<Tween> Tween::set_parallel(bool p_parallel) {
223 default_parallel = p_parallel;
224 parallel_enabled = p_parallel;
225 return this;
226}
227
228Ref<Tween> Tween::set_loops(int p_loops) {
229 loops = p_loops;
230 return this;
231}
232
233int Tween::get_loops_left() const {
234 if (loops <= 0) {
235 return -1; // Infinite loop.
236 } else {
237 return loops - loops_done;
238 }
239}
240
241Ref<Tween> Tween::set_speed_scale(float p_speed) {
242 speed_scale = p_speed;
243 return this;
244}
245
246Ref<Tween> Tween::set_trans(TransitionType p_trans) {
247 default_transition = p_trans;
248 return this;
249}
250
251Tween::TransitionType Tween::get_trans() {
252 return default_transition;
253}
254
255Ref<Tween> Tween::set_ease(EaseType p_ease) {
256 default_ease = p_ease;
257 return this;
258}
259
260Tween::EaseType Tween::get_ease() {
261 return default_ease;
262}
263
264Ref<Tween> Tween::parallel() {
265 parallel_enabled = true;
266 return this;
267}
268
269Ref<Tween> Tween::chain() {
270 parallel_enabled = false;
271 return this;
272}
273
274bool Tween::custom_step(double p_delta) {
275 bool r = running;
276 running = true;
277 bool ret = step(p_delta);
278 running = running && r; // Running might turn false when Tween finished.
279 return ret;
280}
281
282bool Tween::step(double p_delta) {
283 if (dead) {
284 return false;
285 }
286
287 if (is_bound) {
288 Node *node = get_bound_node();
289 if (node) {
290 if (!node->is_inside_tree()) {
291 return true;
292 }
293 } else {
294 return false;
295 }
296 }
297
298 if (!running) {
299 return true;
300 }
301
302 if (!started) {
303 if (tweeners.is_empty()) {
304 String tween_id;
305 Node *node = get_bound_node();
306 if (node) {
307 tween_id = vformat("Tween (bound to %s)", node->is_inside_tree() ? (String)node->get_path() : (String)node->get_name());
308 } else {
309 tween_id = to_string();
310 }
311 ERR_FAIL_V_MSG(false, tween_id + ": started with no Tweeners.");
312 }
313 current_step = 0;
314 loops_done = 0;
315 total_time = 0;
316 _start_tweeners();
317 started = true;
318 }
319
320 double rem_delta = p_delta * speed_scale;
321 bool step_active = false;
322 total_time += rem_delta;
323
324#ifdef DEBUG_ENABLED
325 double initial_delta = rem_delta;
326 bool potential_infinite = false;
327#endif
328
329 while (rem_delta > 0 && running) {
330 double step_delta = rem_delta;
331 step_active = false;
332
333 for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
334 // Modified inside Tweener.step().
335 double temp_delta = rem_delta;
336 // Turns to true if any Tweener returns true (i.e. is still not finished).
337 step_active = tweener->step(temp_delta) || step_active;
338 step_delta = MIN(temp_delta, step_delta);
339 }
340
341 rem_delta = step_delta;
342
343 if (!step_active) {
344 emit_signal(SNAME("step_finished"), current_step);
345 current_step++;
346
347 if (current_step == tweeners.size()) {
348 loops_done++;
349 if (loops_done == loops) {
350 running = false;
351 dead = true;
352 emit_signal(SNAME("finished"));
353 break;
354 } else {
355 emit_signal(SNAME("loop_finished"), loops_done);
356 current_step = 0;
357 _start_tweeners();
358#ifdef DEBUG_ENABLED
359 if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
360 if (!potential_infinite) {
361 potential_infinite = true;
362 } else {
363 // Looped twice without using any time, this is 100% certain infinite loop.
364 ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
365 }
366 }
367#endif
368 }
369 } else {
370 _start_tweeners();
371 }
372 }
373 }
374
375 return true;
376}
377
378bool Tween::can_process(bool p_tree_paused) const {
379 if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
380 Node *node = get_bound_node();
381 if (node) {
382 return node->is_inside_tree() && node->can_process();
383 }
384 }
385
386 return !p_tree_paused || pause_mode == TWEEN_PAUSE_PROCESS;
387}
388
389Node *Tween::get_bound_node() const {
390 if (is_bound) {
391 return Object::cast_to<Node>(ObjectDB::get_instance(bound_node));
392 } else {
393 return nullptr;
394 }
395}
396
397double Tween::get_total_time() const {
398 return total_time;
399}
400
401real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) {
402 if (p_duration == 0) {
403 // Special case to avoid dividing by 0 in equations.
404 return p_initial + p_delta;
405 }
406
407 interpolater func = interpolaters[p_trans_type][p_ease_type];
408 return func(p_time, p_initial, p_delta, p_duration);
409}
410
411Variant Tween::interpolate_variant(const Variant &p_initial_val, const Variant &p_delta_val, double p_time, double p_duration, TransitionType p_trans, EaseType p_ease) {
412 ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant());
413 ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant());
414
415 // Special case for bool.
416 if (p_initial_val.get_type() == Variant::BOOL) {
417 return run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration) >= 0.5;
418 }
419
420 Variant ret = Animation::add_variant(p_initial_val, p_delta_val);
421 ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration));
422 return ret;
423}
424
425String Tween::to_string() {
426 String ret = Object::to_string();
427 Node *node = get_bound_node();
428 if (node) {
429 ret += vformat(" (bound to %s)", node->get_name());
430 }
431 return ret;
432}
433
434void Tween::_bind_methods() {
435 ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property);
436 ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
437 ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
438 ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
439
440 ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
441 ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
442 ClassDB::bind_method(D_METHOD("pause"), &Tween::pause);
443 ClassDB::bind_method(D_METHOD("play"), &Tween::play);
444 ClassDB::bind_method(D_METHOD("kill"), &Tween::kill);
445 ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &Tween::get_total_time);
446
447 ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running);
448 ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid);
449 ClassDB::bind_method(D_METHOD("bind_node", "node"), &Tween::bind_node);
450 ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Tween::set_process_mode);
451 ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Tween::set_pause_mode);
452
453 ClassDB::bind_method(D_METHOD("set_parallel", "parallel"), &Tween::set_parallel, DEFVAL(true));
454 ClassDB::bind_method(D_METHOD("set_loops", "loops"), &Tween::set_loops, DEFVAL(0));
455 ClassDB::bind_method(D_METHOD("get_loops_left"), &Tween::get_loops_left);
456 ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &Tween::set_speed_scale);
457 ClassDB::bind_method(D_METHOD("set_trans", "trans"), &Tween::set_trans);
458 ClassDB::bind_method(D_METHOD("set_ease", "ease"), &Tween::set_ease);
459
460 ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
461 ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
462
463 ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
464
465 ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
466 ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
467 ADD_SIGNAL(MethodInfo("finished"));
468
469 BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS);
470 BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE);
471
472 BIND_ENUM_CONSTANT(TWEEN_PAUSE_BOUND);
473 BIND_ENUM_CONSTANT(TWEEN_PAUSE_STOP);
474 BIND_ENUM_CONSTANT(TWEEN_PAUSE_PROCESS);
475
476 BIND_ENUM_CONSTANT(TRANS_LINEAR);
477 BIND_ENUM_CONSTANT(TRANS_SINE);
478 BIND_ENUM_CONSTANT(TRANS_QUINT);
479 BIND_ENUM_CONSTANT(TRANS_QUART);
480 BIND_ENUM_CONSTANT(TRANS_QUAD);
481 BIND_ENUM_CONSTANT(TRANS_EXPO);
482 BIND_ENUM_CONSTANT(TRANS_ELASTIC);
483 BIND_ENUM_CONSTANT(TRANS_CUBIC);
484 BIND_ENUM_CONSTANT(TRANS_CIRC);
485 BIND_ENUM_CONSTANT(TRANS_BOUNCE);
486 BIND_ENUM_CONSTANT(TRANS_BACK);
487 BIND_ENUM_CONSTANT(TRANS_SPRING);
488
489 BIND_ENUM_CONSTANT(EASE_IN);
490 BIND_ENUM_CONSTANT(EASE_OUT);
491 BIND_ENUM_CONSTANT(EASE_IN_OUT);
492 BIND_ENUM_CONSTANT(EASE_OUT_IN);
493}
494
495Tween::Tween() {
496 ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
497}
498
499Tween::Tween(bool p_valid) {
500 valid = p_valid;
501}
502
503Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
504 ERR_FAIL_COND_V(tween.is_null(), nullptr);
505 if (!tween->_validate_type_match(p_value, final_val)) {
506 return nullptr;
507 }
508
509 initial_val = p_value;
510 do_continue = false;
511 return this;
512}
513
514Ref<PropertyTweener> PropertyTweener::from_current() {
515 do_continue = false;
516 return this;
517}
518
519Ref<PropertyTweener> PropertyTweener::as_relative() {
520 relative = true;
521 return this;
522}
523
524Ref<PropertyTweener> PropertyTweener::set_trans(Tween::TransitionType p_trans) {
525 trans_type = p_trans;
526 return this;
527}
528
529Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) {
530 ease_type = p_ease;
531 return this;
532}
533
534Ref<PropertyTweener> PropertyTweener::set_delay(double p_delay) {
535 delay = p_delay;
536 return this;
537}
538
539void PropertyTweener::start() {
540 elapsed_time = 0;
541 finished = false;
542
543 Object *target_instance = ObjectDB::get_instance(target);
544 if (!target_instance) {
545 WARN_PRINT("Target object freed before starting, aborting Tweener.");
546 return;
547 }
548
549 if (do_continue && Math::is_zero_approx(delay)) {
550 initial_val = target_instance->get_indexed(property);
551 do_continue = false;
552 }
553
554 if (relative) {
555 final_val = Animation::add_variant(initial_val, base_final_val);
556 }
557
558 delta_val = Animation::subtract_variant(final_val, initial_val);
559}
560
561bool PropertyTweener::step(double &r_delta) {
562 if (finished) {
563 // This is needed in case there's a parallel Tweener with longer duration.
564 return false;
565 }
566
567 Object *target_instance = ObjectDB::get_instance(target);
568 if (!target_instance) {
569 return false;
570 }
571 elapsed_time += r_delta;
572
573 if (elapsed_time < delay) {
574 r_delta = 0;
575 return true;
576 } else if (do_continue && !Math::is_zero_approx(delay)) {
577 initial_val = target_instance->get_indexed(property);
578 delta_val = Animation::subtract_variant(final_val, initial_val);
579 do_continue = false;
580 }
581
582 double time = MIN(elapsed_time - delay, duration);
583 if (time < duration) {
584 target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
585 r_delta = 0;
586 return true;
587 } else {
588 target_instance->set_indexed(property, final_val);
589 finished = true;
590 r_delta = elapsed_time - delay - duration;
591 emit_signal(SNAME("finished"));
592 return false;
593 }
594}
595
596void PropertyTweener::set_tween(const Ref<Tween> &p_tween) {
597 tween = p_tween;
598 if (trans_type == Tween::TRANS_MAX) {
599 trans_type = tween->get_trans();
600 }
601 if (ease_type == Tween::EASE_MAX) {
602 ease_type = tween->get_ease();
603 }
604}
605
606void PropertyTweener::_bind_methods() {
607 ClassDB::bind_method(D_METHOD("from", "value"), &PropertyTweener::from);
608 ClassDB::bind_method(D_METHOD("from_current"), &PropertyTweener::from_current);
609 ClassDB::bind_method(D_METHOD("as_relative"), &PropertyTweener::as_relative);
610 ClassDB::bind_method(D_METHOD("set_trans", "trans"), &PropertyTweener::set_trans);
611 ClassDB::bind_method(D_METHOD("set_ease", "ease"), &PropertyTweener::set_ease);
612 ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay);
613}
614
615PropertyTweener::PropertyTweener(const Object *p_target, const Vector<StringName> &p_property, const Variant &p_to, double p_duration) {
616 target = p_target->get_instance_id();
617 property = p_property;
618 initial_val = p_target->get_indexed(property);
619 base_final_val = p_to;
620 final_val = base_final_val;
621 duration = p_duration;
622
623 if (p_target->is_ref_counted()) {
624 ref_copy = p_target;
625 }
626}
627
628PropertyTweener::PropertyTweener() {
629 ERR_FAIL_MSG("PropertyTweener can't be created directly. Use the tween_property() method in Tween.");
630}
631
632void IntervalTweener::start() {
633 elapsed_time = 0;
634 finished = false;
635}
636
637bool IntervalTweener::step(double &r_delta) {
638 if (finished) {
639 return false;
640 }
641
642 elapsed_time += r_delta;
643
644 if (elapsed_time < duration) {
645 r_delta = 0;
646 return true;
647 } else {
648 finished = true;
649 r_delta = elapsed_time - duration;
650 emit_signal(SNAME("finished"));
651 return false;
652 }
653}
654
655IntervalTweener::IntervalTweener(double p_time) {
656 duration = p_time;
657}
658
659IntervalTweener::IntervalTweener() {
660 ERR_FAIL_MSG("IntervalTweener can't be created directly. Use the tween_interval() method in Tween.");
661}
662
663Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) {
664 delay = p_delay;
665 return this;
666}
667
668void CallbackTweener::start() {
669 elapsed_time = 0;
670 finished = false;
671}
672
673bool CallbackTweener::step(double &r_delta) {
674 if (finished) {
675 return false;
676 }
677
678 if (!callback.get_object()) {
679 return false;
680 }
681
682 elapsed_time += r_delta;
683 if (elapsed_time >= delay) {
684 Variant result;
685 Callable::CallError ce;
686 callback.callp(nullptr, 0, result, ce);
687 if (ce.error != Callable::CallError::CALL_OK) {
688 ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
689 }
690
691 finished = true;
692 r_delta = elapsed_time - delay;
693 emit_signal(SNAME("finished"));
694 return false;
695 }
696
697 r_delta = 0;
698 return true;
699}
700
701void CallbackTweener::_bind_methods() {
702 ClassDB::bind_method(D_METHOD("set_delay", "delay"), &CallbackTweener::set_delay);
703}
704
705CallbackTweener::CallbackTweener(const Callable &p_callback) {
706 callback = p_callback;
707
708 Object *callback_instance = p_callback.get_object();
709 if (callback_instance && callback_instance->is_ref_counted()) {
710 ref_copy = callback_instance;
711 }
712}
713
714CallbackTweener::CallbackTweener() {
715 ERR_FAIL_MSG("CallbackTweener can't be created directly. Use the tween_callback() method in Tween.");
716}
717
718Ref<MethodTweener> MethodTweener::set_delay(double p_delay) {
719 delay = p_delay;
720 return this;
721}
722
723Ref<MethodTweener> MethodTweener::set_trans(Tween::TransitionType p_trans) {
724 trans_type = p_trans;
725 return this;
726}
727
728Ref<MethodTweener> MethodTweener::set_ease(Tween::EaseType p_ease) {
729 ease_type = p_ease;
730 return this;
731}
732
733void MethodTweener::start() {
734 elapsed_time = 0;
735 finished = false;
736}
737
738bool MethodTweener::step(double &r_delta) {
739 if (finished) {
740 return false;
741 }
742
743 if (!callback.get_object()) {
744 return false;
745 }
746
747 elapsed_time += r_delta;
748
749 if (elapsed_time < delay) {
750 r_delta = 0;
751 return true;
752 }
753
754 Variant current_val;
755 double time = MIN(elapsed_time - delay, duration);
756 if (time < duration) {
757 current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
758 } else {
759 current_val = final_val;
760 }
761 const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
762 argptr[0] = &current_val;
763
764 Variant result;
765 Callable::CallError ce;
766 callback.callp(argptr, 1, result, ce);
767 if (ce.error != Callable::CallError::CALL_OK) {
768 ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
769 }
770
771 if (time < duration) {
772 r_delta = 0;
773 return true;
774 } else {
775 finished = true;
776 r_delta = elapsed_time - delay - duration;
777 emit_signal(SNAME("finished"));
778 return false;
779 }
780}
781
782void MethodTweener::set_tween(const Ref<Tween> &p_tween) {
783 tween = p_tween;
784 if (trans_type == Tween::TRANS_MAX) {
785 trans_type = tween->get_trans();
786 }
787 if (ease_type == Tween::EASE_MAX) {
788 ease_type = tween->get_ease();
789 }
790}
791
792void MethodTweener::_bind_methods() {
793 ClassDB::bind_method(D_METHOD("set_delay", "delay"), &MethodTweener::set_delay);
794 ClassDB::bind_method(D_METHOD("set_trans", "trans"), &MethodTweener::set_trans);
795 ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease);
796}
797
798MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from, const Variant &p_to, double p_duration) {
799 callback = p_callback;
800 initial_val = p_from;
801 delta_val = Animation::subtract_variant(p_to, p_from);
802 final_val = p_to;
803 duration = p_duration;
804
805 Object *callback_instance = p_callback.get_object();
806 if (callback_instance && callback_instance->is_ref_counted()) {
807 ref_copy = callback_instance;
808 }
809}
810
811MethodTweener::MethodTweener() {
812 ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
813}
814