diff --git a/doc/classes/PropertyTweener.xml b/doc/classes/PropertyTweener.xml
index 73e594218d9d..d3875ddfc222 100644
--- a/doc/classes/PropertyTweener.xml
+++ b/doc/classes/PropertyTweener.xml
@@ -43,6 +43,25 @@
[/codeblock]
+
+
+
+
+ Allows interpolating the value with a custom easing function. The provided [param interpolator_method] will be called with a value ranging from [code]0.0[/code] to [code]1.0[/code] and is expected to return a value within the same range (values outside the range can be used for overshoot). The return value of the method is then used for interpolation between initial and final value. Note that the parameter passed to the method is still subject to the tweener's own easing.
+ [b]Example:[/b]
+ [codeblock]
+ @export var curve: Curve
+
+ func _ready():
+ var tween = create_tween()
+ # Interpolate the value using a custom curve.
+ tween.tween_property(self, "position:x", 300, 1).as_relative().set_custom_interpolator(tween_curve)
+
+ func tween_curve(v):
+ return curve.sample_baked(v)
+ [/codeblock]
+
+
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index c778129eb629..8db6be7d1273 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -531,6 +531,11 @@ Ref PropertyTweener::set_ease(Tween::EaseType p_ease) {
return this;
}
+Ref PropertyTweener::set_custom_interpolator(const Callable &p_method) {
+ custom_method = p_method;
+ return this;
+}
+
Ref PropertyTweener::set_delay(double p_delay) {
delay = p_delay;
return this;
@@ -581,7 +586,23 @@ bool PropertyTweener::step(double &r_delta) {
double time = MIN(elapsed_time - delay, duration);
if (time < duration) {
- target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
+ if (custom_method.is_valid()) {
+ const Variant t = tween->interpolate_variant(0.0, 1.0, time, duration, trans_type, ease_type);
+ const Variant *argptr = &t;
+
+ Variant result;
+ Callable::CallError ce;
+ custom_method.callp(&argptr, 1, result, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(false, "Error calling custom method from PropertyTweener: " + Variant::get_callable_error_text(custom_method, &argptr, 1, ce) + ".");
+ } else if (result.get_type() != Variant::FLOAT) {
+ ERR_FAIL_V_MSG(false, vformat("Wrong return type in PropertyTweener custom method. Expected float, got %s.", Variant::get_type_name(result.get_type())));
+ }
+
+ target_instance->set_indexed(property, Animation::interpolate_variant(initial_val, final_val, result));
+ } else {
+ target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
+ }
r_delta = 0;
return true;
} else {
@@ -609,6 +630,7 @@ void PropertyTweener::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_relative"), &PropertyTweener::as_relative);
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &PropertyTweener::set_trans);
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &PropertyTweener::set_ease);
+ ClassDB::bind_method(D_METHOD("set_custom_interpolator", "interpolator_method"), &PropertyTweener::set_custom_interpolator);
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay);
}
@@ -685,7 +707,7 @@ bool CallbackTweener::step(double &r_delta) {
Callable::CallError ce;
callback.callp(nullptr, 0, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
}
finished = true;
@@ -765,7 +787,7 @@ bool MethodTweener::step(double &r_delta) {
Callable::CallError ce;
callback.callp(argptr, 1, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce) + ".");
}
if (time < duration) {
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 10c7a272ef16..b9be0afe6e39 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -197,6 +197,7 @@ class PropertyTweener : public Tweener {
Ref as_relative();
Ref set_trans(Tween::TransitionType p_trans);
Ref set_ease(Tween::EaseType p_ease);
+ Ref set_custom_interpolator(const Callable &p_method);
Ref set_delay(double p_delay);
void set_tween(const Ref &p_tween) override;
@@ -222,6 +223,7 @@ class PropertyTweener : public Tweener {
double duration = 0;
Tween::TransitionType trans_type = Tween::TRANS_MAX; // This is set inside set_tween();
Tween::EaseType ease_type = Tween::EASE_MAX;
+ Callable custom_method;
double delay = 0;
bool do_continue = true;