Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/animation/tween.cpp
9903 views
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
41
Tween::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
56
void Tweener::set_tween(const Ref<Tween> &p_tween) {
57
tween_id = p_tween->get_instance_id();
58
}
59
60
void Tweener::start() {
61
elapsed_time = 0;
62
finished = false;
63
}
64
65
Ref<Tween> Tweener::_get_tween() {
66
return ObjectDB::get_ref<Tween>(tween_id);
67
}
68
69
void Tweener::_finish() {
70
finished = true;
71
emit_signal(SceneStringName(finished));
72
}
73
74
void Tweener::_bind_methods() {
75
ADD_SIGNAL(MethodInfo("finished"));
76
}
77
78
void Tween::_start_tweeners() {
79
if (tweeners.is_empty()) {
80
dead = true;
81
ERR_FAIL_MSG("Tween without commands, aborting.");
82
}
83
84
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
85
tweener->start();
86
}
87
}
88
89
void Tween::_stop_internal(bool p_reset) {
90
running = false;
91
if (p_reset) {
92
started = false;
93
dead = false;
94
total_time = 0;
95
}
96
}
97
98
Ref<PropertyTweener> Tween::tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration) {
99
ERR_FAIL_NULL_V(p_target, nullptr);
100
CHECK_VALID();
101
102
Vector<StringName> property_subnames = p_property.get_as_property_path().get_subnames();
103
#ifdef DEBUG_ENABLED
104
bool prop_valid;
105
const Variant &prop_value = p_target->get_indexed(property_subnames, &prop_valid);
106
ERR_FAIL_COND_V_MSG(!prop_valid, nullptr, vformat("The tweened property \"%s\" does not exist in object \"%s\".", p_property, p_target));
107
#else
108
const Variant &prop_value = p_target->get_indexed(property_subnames);
109
#endif
110
111
if (!Animation::validate_type_match(prop_value, p_to)) {
112
return nullptr;
113
}
114
115
Ref<PropertyTweener> tweener;
116
tweener.instantiate(p_target, property_subnames, p_to, p_duration);
117
append(tweener);
118
return tweener;
119
}
120
121
Ref<IntervalTweener> Tween::tween_interval(double p_time) {
122
CHECK_VALID();
123
124
Ref<IntervalTweener> tweener;
125
tweener.instantiate(p_time);
126
append(tweener);
127
return tweener;
128
}
129
130
Ref<CallbackTweener> Tween::tween_callback(const Callable &p_callback) {
131
CHECK_VALID();
132
133
Ref<CallbackTweener> tweener;
134
tweener.instantiate(p_callback);
135
append(tweener);
136
return tweener;
137
}
138
139
Ref<MethodTweener> Tween::tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration) {
140
CHECK_VALID();
141
142
if (!Animation::validate_type_match(p_from, p_to)) {
143
return nullptr;
144
}
145
146
Ref<MethodTweener> tweener;
147
tweener.instantiate(p_callback, p_from, p_to, p_duration);
148
append(tweener);
149
return tweener;
150
}
151
152
Ref<SubtweenTweener> Tween::tween_subtween(const Ref<Tween> &p_subtween) {
153
CHECK_VALID();
154
155
// Ensure that the subtween being added is not null.
156
ERR_FAIL_COND_V(p_subtween.is_null(), nullptr);
157
158
Ref<SubtweenTweener> tweener;
159
tweener.instantiate(p_subtween);
160
161
// Remove the tween from its parent tree, if it has one.
162
// If the user created this tween without a parent tree attached,
163
// then this step isn't necessary.
164
if (tweener->subtween->parent_tree != nullptr) {
165
tweener->subtween->parent_tree->remove_tween(tweener->subtween);
166
}
167
append(tweener);
168
return tweener;
169
}
170
171
void Tween::append(Ref<Tweener> p_tweener) {
172
p_tweener->set_tween(this);
173
174
if (parallel_enabled) {
175
current_step = MAX(current_step, 0);
176
} else {
177
current_step++;
178
}
179
parallel_enabled = default_parallel;
180
181
tweeners.resize(current_step + 1);
182
tweeners.write[current_step].push_back(p_tweener);
183
}
184
185
void Tween::stop() {
186
_stop_internal(true);
187
}
188
189
void Tween::pause() {
190
_stop_internal(false);
191
}
192
193
void Tween::play() {
194
ERR_FAIL_COND_MSG(!valid, "Tween invalid. Either finished or created outside scene tree.");
195
ERR_FAIL_COND_MSG(dead, "Can't play finished Tween, use stop() first to reset its state.");
196
running = true;
197
}
198
199
void Tween::kill() {
200
running = false; // For the sake of is_running().
201
valid = false;
202
dead = true;
203
}
204
205
bool Tween::is_running() {
206
return running;
207
}
208
209
bool Tween::is_valid() {
210
return valid;
211
}
212
213
void Tween::clear() {
214
valid = false;
215
tweeners.clear();
216
}
217
218
Ref<Tween> Tween::bind_node(const Node *p_node) {
219
ERR_FAIL_NULL_V(p_node, this);
220
221
bound_node = p_node->get_instance_id();
222
is_bound = true;
223
return this;
224
}
225
226
Ref<Tween> Tween::set_process_mode(TweenProcessMode p_mode) {
227
process_mode = p_mode;
228
return this;
229
}
230
231
Tween::TweenProcessMode Tween::get_process_mode() const {
232
return process_mode;
233
}
234
235
Ref<Tween> Tween::set_pause_mode(TweenPauseMode p_mode) {
236
pause_mode = p_mode;
237
return this;
238
}
239
240
Tween::TweenPauseMode Tween::get_pause_mode() const {
241
return pause_mode;
242
}
243
244
Ref<Tween> Tween::set_ignore_time_scale(bool p_ignore) {
245
ignore_time_scale = p_ignore;
246
return this;
247
}
248
249
bool Tween::is_ignoring_time_scale() const {
250
return ignore_time_scale;
251
}
252
253
Ref<Tween> Tween::set_parallel(bool p_parallel) {
254
default_parallel = p_parallel;
255
parallel_enabled = p_parallel;
256
return this;
257
}
258
259
Ref<Tween> Tween::set_loops(int p_loops) {
260
loops = p_loops;
261
return this;
262
}
263
264
int Tween::get_loops_left() const {
265
if (loops <= 0) {
266
return -1; // Infinite loop.
267
} else {
268
return loops - loops_done;
269
}
270
}
271
272
Ref<Tween> Tween::set_speed_scale(float p_speed) {
273
speed_scale = p_speed;
274
return this;
275
}
276
277
Ref<Tween> Tween::set_trans(TransitionType p_trans) {
278
default_transition = p_trans;
279
return this;
280
}
281
282
Tween::TransitionType Tween::get_trans() const {
283
return default_transition;
284
}
285
286
Ref<Tween> Tween::set_ease(EaseType p_ease) {
287
default_ease = p_ease;
288
return this;
289
}
290
291
Tween::EaseType Tween::get_ease() const {
292
return default_ease;
293
}
294
295
Ref<Tween> Tween::parallel() {
296
parallel_enabled = true;
297
return this;
298
}
299
300
Ref<Tween> Tween::chain() {
301
parallel_enabled = false;
302
return this;
303
}
304
305
bool Tween::custom_step(double p_delta) {
306
ERR_FAIL_COND_V_MSG(in_step, true, "Can't call custom_step() during another Tween step.");
307
308
bool r = running;
309
running = true;
310
bool ret = step(p_delta);
311
running = running && r; // Running might turn false when Tween finished.
312
return ret;
313
}
314
315
bool Tween::step(double p_delta) {
316
if (dead) {
317
return false;
318
}
319
320
if (is_bound) {
321
Node *node = get_bound_node();
322
if (node) {
323
if (!node->is_inside_tree()) {
324
return true;
325
}
326
} else {
327
return false;
328
}
329
}
330
331
if (!running) {
332
return true;
333
}
334
in_step = true;
335
336
if (!started) {
337
if (tweeners.is_empty()) {
338
String tween_id;
339
Node *node = get_bound_node();
340
if (node) {
341
tween_id = vformat("Tween (bound to %s)", node->is_inside_tree() ? (String)node->get_path() : (String)node->get_name());
342
} else {
343
tween_id = to_string();
344
}
345
in_step = false;
346
ERR_FAIL_V_MSG(false, tween_id + ": started with no Tweeners.");
347
}
348
current_step = 0;
349
loops_done = 0;
350
total_time = 0;
351
_start_tweeners();
352
started = true;
353
}
354
355
double rem_delta = p_delta * speed_scale;
356
bool step_active = false;
357
total_time += rem_delta;
358
359
#ifdef DEBUG_ENABLED
360
double initial_delta = rem_delta;
361
bool potential_infinite = false;
362
#endif
363
364
while (running && rem_delta > 0) {
365
double step_delta = rem_delta;
366
step_active = false;
367
368
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
369
// Modified inside Tweener.step().
370
double temp_delta = rem_delta;
371
// Turns to true if any Tweener returns true (i.e. is still not finished).
372
step_active = tweener->step(temp_delta) || step_active;
373
step_delta = MIN(temp_delta, step_delta);
374
}
375
376
rem_delta = step_delta;
377
378
if (!step_active) {
379
emit_signal(SNAME("step_finished"), current_step);
380
current_step++;
381
382
if (current_step == tweeners.size()) {
383
loops_done++;
384
if (loops_done == loops) {
385
running = false;
386
dead = true;
387
emit_signal(SceneStringName(finished));
388
break;
389
} else {
390
emit_signal(SNAME("loop_finished"), loops_done);
391
current_step = 0;
392
_start_tweeners();
393
#ifdef DEBUG_ENABLED
394
if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
395
if (!potential_infinite) {
396
potential_infinite = true;
397
} else {
398
// Looped twice without using any time, this is 100% certain infinite loop.
399
in_step = false;
400
ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
401
}
402
}
403
#endif
404
}
405
} else {
406
_start_tweeners();
407
}
408
}
409
}
410
in_step = false;
411
return true;
412
}
413
414
bool Tween::can_process(bool p_tree_paused) const {
415
if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
416
Node *node = get_bound_node();
417
if (node) {
418
return node->is_inside_tree() && node->can_process();
419
}
420
}
421
422
return !p_tree_paused || pause_mode == TWEEN_PAUSE_PROCESS;
423
}
424
425
Node *Tween::get_bound_node() const {
426
if (is_bound) {
427
return ObjectDB::get_instance<Node>(bound_node);
428
} else {
429
return nullptr;
430
}
431
}
432
433
double Tween::get_total_time() const {
434
return total_time;
435
}
436
437
real_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) {
438
if (p_duration == 0) {
439
// Special case to avoid dividing by 0 in equations.
440
return p_initial + p_delta;
441
}
442
443
interpolater func = interpolaters[p_trans_type][p_ease_type];
444
return func(p_time, p_initial, p_delta, p_duration);
445
}
446
447
Variant 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) {
448
ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant());
449
ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant());
450
451
Variant ret = Animation::add_variant(p_initial_val, p_delta_val);
452
ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration), p_initial_val.is_string());
453
return ret;
454
}
455
456
String Tween::to_string() {
457
String ret = Object::to_string();
458
Node *node = get_bound_node();
459
if (node) {
460
ret += vformat(" (bound to %s)", node->get_name());
461
}
462
return ret;
463
}
464
465
void Tween::_bind_methods() {
466
ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property);
467
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
468
ClassDB::bind_method(D_METHOD("tween_callback", "callback"), &Tween::tween_callback);
469
ClassDB::bind_method(D_METHOD("tween_method", "method", "from", "to", "duration"), &Tween::tween_method);
470
ClassDB::bind_method(D_METHOD("tween_subtween", "subtween"), &Tween::tween_subtween);
471
472
ClassDB::bind_method(D_METHOD("custom_step", "delta"), &Tween::custom_step);
473
ClassDB::bind_method(D_METHOD("stop"), &Tween::stop);
474
ClassDB::bind_method(D_METHOD("pause"), &Tween::pause);
475
ClassDB::bind_method(D_METHOD("play"), &Tween::play);
476
ClassDB::bind_method(D_METHOD("kill"), &Tween::kill);
477
ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &Tween::get_total_time);
478
479
ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running);
480
ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid);
481
ClassDB::bind_method(D_METHOD("bind_node", "node"), &Tween::bind_node);
482
ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Tween::set_process_mode);
483
ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Tween::set_pause_mode);
484
ClassDB::bind_method(D_METHOD("set_ignore_time_scale", "ignore"), &Tween::set_ignore_time_scale, DEFVAL(true));
485
486
ClassDB::bind_method(D_METHOD("set_parallel", "parallel"), &Tween::set_parallel, DEFVAL(true));
487
ClassDB::bind_method(D_METHOD("set_loops", "loops"), &Tween::set_loops, DEFVAL(0));
488
ClassDB::bind_method(D_METHOD("get_loops_left"), &Tween::get_loops_left);
489
ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &Tween::set_speed_scale);
490
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &Tween::set_trans);
491
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &Tween::set_ease);
492
493
ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
494
ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
495
496
ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
497
498
ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
499
ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
500
ADD_SIGNAL(MethodInfo("finished"));
501
502
BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS);
503
BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE);
504
505
BIND_ENUM_CONSTANT(TWEEN_PAUSE_BOUND);
506
BIND_ENUM_CONSTANT(TWEEN_PAUSE_STOP);
507
BIND_ENUM_CONSTANT(TWEEN_PAUSE_PROCESS);
508
509
BIND_ENUM_CONSTANT(TRANS_LINEAR);
510
BIND_ENUM_CONSTANT(TRANS_SINE);
511
BIND_ENUM_CONSTANT(TRANS_QUINT);
512
BIND_ENUM_CONSTANT(TRANS_QUART);
513
BIND_ENUM_CONSTANT(TRANS_QUAD);
514
BIND_ENUM_CONSTANT(TRANS_EXPO);
515
BIND_ENUM_CONSTANT(TRANS_ELASTIC);
516
BIND_ENUM_CONSTANT(TRANS_CUBIC);
517
BIND_ENUM_CONSTANT(TRANS_CIRC);
518
BIND_ENUM_CONSTANT(TRANS_BOUNCE);
519
BIND_ENUM_CONSTANT(TRANS_BACK);
520
BIND_ENUM_CONSTANT(TRANS_SPRING);
521
522
BIND_ENUM_CONSTANT(EASE_IN);
523
BIND_ENUM_CONSTANT(EASE_OUT);
524
BIND_ENUM_CONSTANT(EASE_IN_OUT);
525
BIND_ENUM_CONSTANT(EASE_OUT_IN);
526
}
527
528
Tween::Tween() {
529
ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
530
}
531
532
Tween::Tween(SceneTree *p_parent_tree) {
533
parent_tree = p_parent_tree;
534
valid = true;
535
}
536
537
double PropertyTweener::_get_custom_interpolated_value(const Variant &p_value) {
538
const Variant *argptr = &p_value;
539
540
Variant result;
541
Callable::CallError ce;
542
custom_method.callp(&argptr, 1, result, ce);
543
if (ce.error != Callable::CallError::CALL_OK) {
544
ERR_FAIL_V_MSG(false, "Error calling custom method from PropertyTweener: " + Variant::get_callable_error_text(custom_method, &argptr, 1, ce) + ".");
545
} else if (result.get_type() != Variant::FLOAT) {
546
ERR_FAIL_V_MSG(false, vformat("Wrong return type in PropertyTweener custom method. Expected float, got %s.", Variant::get_type_name(result.get_type())));
547
}
548
return result;
549
}
550
551
Ref<PropertyTweener> PropertyTweener::from(const Variant &p_value) {
552
Ref<Tween> tween = _get_tween();
553
ERR_FAIL_COND_V(tween.is_null(), nullptr);
554
555
Variant from_value = p_value;
556
if (!Animation::validate_type_match(final_val, from_value)) {
557
return nullptr;
558
}
559
560
initial_val = from_value;
561
do_continue = false;
562
return this;
563
}
564
565
Ref<PropertyTweener> PropertyTweener::from_current() {
566
do_continue = false;
567
return this;
568
}
569
570
Ref<PropertyTweener> PropertyTweener::as_relative() {
571
relative = true;
572
return this;
573
}
574
575
Ref<PropertyTweener> PropertyTweener::set_trans(Tween::TransitionType p_trans) {
576
trans_type = p_trans;
577
return this;
578
}
579
580
Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) {
581
ease_type = p_ease;
582
return this;
583
}
584
585
Ref<PropertyTweener> PropertyTweener::set_custom_interpolator(const Callable &p_method) {
586
custom_method = p_method;
587
return this;
588
}
589
590
Ref<PropertyTweener> PropertyTweener::set_delay(double p_delay) {
591
delay = p_delay;
592
return this;
593
}
594
595
void PropertyTweener::start() {
596
Tweener::start();
597
598
Object *target_instance = ObjectDB::get_instance(target);
599
if (!target_instance) {
600
return;
601
}
602
603
if (do_continue) {
604
if (Math::is_zero_approx(delay)) {
605
initial_val = target_instance->get_indexed(property);
606
} else {
607
do_continue_delayed = true;
608
}
609
}
610
611
if (relative) {
612
final_val = Animation::add_variant(initial_val, base_final_val);
613
}
614
615
delta_val = Animation::subtract_variant(final_val, initial_val);
616
}
617
618
bool PropertyTweener::step(double &r_delta) {
619
if (finished) {
620
// This is needed in case there's a parallel Tweener with longer duration.
621
return false;
622
}
623
624
Object *target_instance = ObjectDB::get_instance(target);
625
if (!target_instance) {
626
_finish();
627
return false;
628
}
629
elapsed_time += r_delta;
630
631
if (elapsed_time < delay) {
632
r_delta = 0;
633
return true;
634
} else if (do_continue_delayed && !Math::is_zero_approx(delay)) {
635
initial_val = target_instance->get_indexed(property);
636
delta_val = Animation::subtract_variant(final_val, initial_val);
637
do_continue_delayed = false;
638
}
639
640
Ref<Tween> tween = _get_tween();
641
642
double time = MIN(elapsed_time - delay, duration);
643
if (time < duration) {
644
if (custom_method.is_valid()) {
645
const Variant t = tween->interpolate_variant(0.0, 1.0, time, duration, trans_type, ease_type);
646
double result = _get_custom_interpolated_value(t);
647
target_instance->set_indexed(property, Animation::interpolate_variant(initial_val, final_val, result));
648
} else {
649
target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
650
}
651
r_delta = 0;
652
return true;
653
} else {
654
if (custom_method.is_valid()) {
655
double final_t = _get_custom_interpolated_value(1.0);
656
target_instance->set_indexed(property, Animation::interpolate_variant(initial_val, final_val, final_t));
657
} else {
658
target_instance->set_indexed(property, final_val);
659
}
660
r_delta = elapsed_time - delay - duration;
661
_finish();
662
return false;
663
}
664
}
665
666
void PropertyTweener::set_tween(const Ref<Tween> &p_tween) {
667
Tweener::set_tween(p_tween);
668
if (trans_type == Tween::TRANS_MAX) {
669
trans_type = p_tween->get_trans();
670
}
671
if (ease_type == Tween::EASE_MAX) {
672
ease_type = p_tween->get_ease();
673
}
674
}
675
676
void PropertyTweener::_bind_methods() {
677
ClassDB::bind_method(D_METHOD("from", "value"), &PropertyTweener::from);
678
ClassDB::bind_method(D_METHOD("from_current"), &PropertyTweener::from_current);
679
ClassDB::bind_method(D_METHOD("as_relative"), &PropertyTweener::as_relative);
680
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &PropertyTweener::set_trans);
681
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &PropertyTweener::set_ease);
682
ClassDB::bind_method(D_METHOD("set_custom_interpolator", "interpolator_method"), &PropertyTweener::set_custom_interpolator);
683
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay);
684
}
685
686
PropertyTweener::PropertyTweener(const Object *p_target, const Vector<StringName> &p_property, const Variant &p_to, double p_duration) {
687
target = p_target->get_instance_id();
688
property = p_property;
689
initial_val = p_target->get_indexed(property);
690
base_final_val = p_to;
691
final_val = base_final_val;
692
duration = p_duration;
693
694
if (p_target->is_ref_counted()) {
695
ref_copy = p_target;
696
}
697
}
698
699
PropertyTweener::PropertyTweener() {
700
ERR_FAIL_MSG("PropertyTweener can't be created directly. Use the tween_property() method in Tween.");
701
}
702
703
bool IntervalTweener::step(double &r_delta) {
704
if (finished) {
705
return false;
706
}
707
708
elapsed_time += r_delta;
709
710
if (elapsed_time < duration) {
711
r_delta = 0;
712
return true;
713
} else {
714
r_delta = elapsed_time - duration;
715
_finish();
716
return false;
717
}
718
}
719
720
IntervalTweener::IntervalTweener(double p_time) {
721
duration = p_time;
722
}
723
724
IntervalTweener::IntervalTweener() {
725
ERR_FAIL_MSG("IntervalTweener can't be created directly. Use the tween_interval() method in Tween.");
726
}
727
728
Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) {
729
delay = p_delay;
730
return this;
731
}
732
733
bool CallbackTweener::step(double &r_delta) {
734
if (finished) {
735
return false;
736
}
737
738
if (!callback.is_valid()) {
739
_finish();
740
return false;
741
}
742
743
elapsed_time += r_delta;
744
if (elapsed_time >= delay) {
745
Variant result;
746
Callable::CallError ce;
747
callback.callp(nullptr, 0, result, ce);
748
if (ce.error != Callable::CallError::CALL_OK) {
749
ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
750
}
751
752
r_delta = elapsed_time - delay;
753
_finish();
754
return false;
755
}
756
757
r_delta = 0;
758
return true;
759
}
760
761
void CallbackTweener::_bind_methods() {
762
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &CallbackTweener::set_delay);
763
}
764
765
CallbackTweener::CallbackTweener(const Callable &p_callback) {
766
callback = p_callback;
767
768
Object *callback_instance = p_callback.get_object();
769
if (callback_instance && callback_instance->is_ref_counted()) {
770
ref_copy = callback_instance;
771
}
772
}
773
774
CallbackTweener::CallbackTweener() {
775
ERR_FAIL_MSG("CallbackTweener can't be created directly. Use the tween_callback() method in Tween.");
776
}
777
778
Ref<MethodTweener> MethodTweener::set_delay(double p_delay) {
779
delay = p_delay;
780
return this;
781
}
782
783
Ref<MethodTweener> MethodTweener::set_trans(Tween::TransitionType p_trans) {
784
trans_type = p_trans;
785
return this;
786
}
787
788
Ref<MethodTweener> MethodTweener::set_ease(Tween::EaseType p_ease) {
789
ease_type = p_ease;
790
return this;
791
}
792
793
bool MethodTweener::step(double &r_delta) {
794
if (finished) {
795
return false;
796
}
797
798
if (!callback.is_valid()) {
799
_finish();
800
return false;
801
}
802
803
elapsed_time += r_delta;
804
805
if (elapsed_time < delay) {
806
r_delta = 0;
807
return true;
808
}
809
810
Ref<Tween> tween = _get_tween();
811
812
Variant current_val;
813
double time = MIN(elapsed_time - delay, duration);
814
if (time < duration) {
815
current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
816
} else {
817
current_val = final_val;
818
}
819
const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
820
argptr[0] = &current_val;
821
822
Variant result;
823
Callable::CallError ce;
824
callback.callp(argptr, 1, result, ce);
825
if (ce.error != Callable::CallError::CALL_OK) {
826
ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce) + ".");
827
}
828
829
if (time < duration) {
830
r_delta = 0;
831
return true;
832
} else {
833
r_delta = elapsed_time - delay - duration;
834
_finish();
835
return false;
836
}
837
}
838
839
void MethodTweener::set_tween(const Ref<Tween> &p_tween) {
840
Tweener::set_tween(p_tween);
841
if (trans_type == Tween::TRANS_MAX) {
842
trans_type = p_tween->get_trans();
843
}
844
if (ease_type == Tween::EASE_MAX) {
845
ease_type = p_tween->get_ease();
846
}
847
}
848
849
void MethodTweener::_bind_methods() {
850
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &MethodTweener::set_delay);
851
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &MethodTweener::set_trans);
852
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease);
853
}
854
855
MethodTweener::MethodTweener(const Callable &p_callback, const Variant &p_from, const Variant &p_to, double p_duration) {
856
callback = p_callback;
857
initial_val = p_from;
858
delta_val = Animation::subtract_variant(p_to, p_from);
859
final_val = p_to;
860
duration = p_duration;
861
862
Object *callback_instance = p_callback.get_object();
863
if (callback_instance && callback_instance->is_ref_counted()) {
864
ref_copy = callback_instance;
865
}
866
}
867
868
MethodTweener::MethodTweener() {
869
ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
870
}
871
872
void SubtweenTweener::start() {
873
Tweener::start();
874
875
// Reset the subtween.
876
subtween->stop();
877
878
// It's possible that a subtween could be killed before it is started;
879
// if so, we just want to skip it entirely.
880
if (subtween->is_valid()) {
881
subtween->play();
882
} else {
883
_finish();
884
}
885
}
886
887
bool SubtweenTweener::step(double &r_delta) {
888
if (finished) {
889
return false;
890
}
891
892
elapsed_time += r_delta;
893
894
if (elapsed_time < delay) {
895
r_delta = 0;
896
return true;
897
}
898
899
if (!subtween->step(r_delta)) {
900
r_delta = elapsed_time - delay - subtween->get_total_time();
901
_finish();
902
return false;
903
}
904
905
r_delta = 0;
906
return true;
907
}
908
909
Ref<SubtweenTweener> SubtweenTweener::set_delay(double p_delay) {
910
delay = p_delay;
911
return this;
912
}
913
914
void SubtweenTweener::_bind_methods() {
915
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &SubtweenTweener::set_delay);
916
}
917
918
SubtweenTweener::SubtweenTweener(const Ref<Tween> &p_subtween) {
919
subtween = p_subtween;
920
}
921
922
SubtweenTweener::SubtweenTweener() {
923
ERR_FAIL_MSG("SubtweenTweener can't be created directly. Use the tween_subtween() method in Tween.");
924
}
925
926