Переглянути джерело

Merge pull request #60581 from timothyqiu/neo-tween

Rémi Verschelde 3 роки тому
батько
коміт
3a3dd2076b

+ 7 - 5
COPYRIGHT.txt

@@ -96,6 +96,13 @@ Copyright: 1997-2017, Sam Lantinga
  2014-2022, Godot Engine contributors.
  2014-2022, Godot Engine contributors.
 License: Expat and Zlib
 License: Expat and Zlib
 
 
+Files: ./scene/animation/easing_equations.h
+Comment: Robert Penner's Easing Functions
+Copyright: 2001, Robert Penner
+ 2007-2022 Juan Linietsky, Ariel Manzur.
+ 2014-2022 Godot Engine contributors.
+License: Expat
+
 Files: ./servers/physics/collision_solver_sat.cpp
 Files: ./servers/physics/collision_solver_sat.cpp
 Comment: Open Dynamics Engine
 Comment: Open Dynamics Engine
 Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
 Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
@@ -273,11 +280,6 @@ Comment: Clipper
 Copyright: 2010-2017, Angus Johnson
 Copyright: 2010-2017, Angus Johnson
 License: BSL-1.0
 License: BSL-1.0
 
 
-Files: ./thirdparty/misc/easing_equations.cpp
-Comment: Robert Penner's Easing Functions
-Copyright: 2001, Robert Penner
-License: BSD-3-clause
-
 Files: ./thirdparty/misc/fastlz.c
 Files: ./thirdparty/misc/fastlz.c
  ./thirdparty/misc/fastlz.h
  ./thirdparty/misc/fastlz.h
 Comment: FastLZ
 Comment: FastLZ

+ 27 - 0
doc/classes/CallbackTweener.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CallbackTweener" inherits="Tweener" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Calls the specified method after optional delay.
+	</brief_description>
+	<description>
+		[CallbackTweener] is used to call a method in a tweening sequence. See [method SceneTreeTween.tween_callback] for more usage information.
+		[b]Note:[/b] [method SceneTreeTween.tween_callback] is the only correct way to create [CallbackTweener]. Any [CallbackTweener] created manually will not function correctly.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="set_delay">
+			<return type="CallbackTweener" />
+			<argument index="0" name="delay" type="float" />
+			<description>
+				Makes the callback call delayed by given time in seconds. Example:
+				[codeblock]
+				var tween = get_tree().create_tween()
+				tween.tween_callback(queue_free).set_delay(2) #this will call queue_free() after 2 seconds
+				[/codeblock]
+			</description>
+		</method>
+	</methods>
+	<constants>
+	</constants>
+</class>

+ 16 - 0
doc/classes/IntervalTweener.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="IntervalTweener" inherits="Tweener" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Creates an idle interval in a [SceneTreeTween] animation.
+	</brief_description>
+	<description>
+		[IntervalTweener] is used to make delays in a tweening sequence. See [method SceneTreeTween.tween_interval] for more usage information.
+		[b]Note:[/b] [method SceneTreeTween.tween_interval] is the only correct way to create [IntervalTweener]. Any [IntervalTweener] created manually will not function correctly.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<constants>
+	</constants>
+</class>

+ 37 - 0
doc/classes/MethodTweener.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MethodTweener" inherits="Tweener" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Interpolates an abstract value and supplies it to a method called over time.
+	</brief_description>
+	<description>
+		[MethodTweener] is similar to a combination of [CallbackTweener] and [PropertyTweener]. It calls a method providing an interpolated value as a parameter. See [method SceneTreeTween.tween_method] for more usage information.
+		[b]Note:[/b] [method SceneTreeTween.tween_method] is the only correct way to create [MethodTweener]. Any [MethodTweener] created manually will not function correctly.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="set_delay">
+			<return type="MethodTweener" />
+			<argument index="0" name="delay" type="float" />
+			<description>
+				Sets the time in seconds after which the [MethodTweener] will start interpolating. By default there's no delay.
+			</description>
+		</method>
+		<method name="set_ease">
+			<return type="MethodTweener" />
+			<argument index="0" name="ease" type="int" enum="Tween.EaseType" />
+			<description>
+				Sets the type of used easing from [enum Tween.EaseType]. If not set, the default easing is used from the [SceneTreeTween] that contains this Tweener.
+			</description>
+		</method>
+		<method name="set_trans">
+			<return type="MethodTweener" />
+			<argument index="0" name="trans" type="int" enum="Tween.TransitionType" />
+			<description>
+				Sets the type of used transition from [enum Tween.TransitionType]. If not set, the default transition is used from the [SceneTreeTween] that contains this Tweener.
+			</description>
+		</method>
+	</methods>
+	<constants>
+	</constants>
+</class>

+ 9 - 0
doc/classes/Node.xml

@@ -147,6 +147,15 @@
 				Returns [code]true[/code] if the node can process while the scene tree is paused (see [member pause_mode]). Always returns [code]true[/code] if the scene tree is not paused, and [code]false[/code] if the node is not in the tree.
 				Returns [code]true[/code] if the node can process while the scene tree is paused (see [member pause_mode]). Always returns [code]true[/code] if the scene tree is not paused, and [code]false[/code] if the node is not in the tree.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="create_tween">
+			<return type="SceneTreeTween" />
+			<description>
+				Creates a new [SceneTreeTween] and binds it to this node. This is equivalent of doing:
+				[codeblock]
+				get_tree().create_tween().bind_node(self)
+				[/codeblock]
+			</description>
+		</method>
 		<method name="duplicate" qualifiers="const">
 		<method name="duplicate" qualifiers="const">
 			<return type="Node" />
 			<return type="Node" />
 			<argument index="0" name="flags" type="int" default="15" />
 			<argument index="0" name="flags" type="int" default="15" />

+ 68 - 0
doc/classes/PropertyTweener.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PropertyTweener" inherits="Tweener" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Interpolates an [Object]'s property over time.
+	</brief_description>
+	<description>
+		[PropertyTweener] is used to interpolate a property in an object. See [method SceneTreeTween.tween_property] for more usage information.
+		[b]Note:[/b] [method SceneTreeTween.tween_property] is the only correct way to create [PropertyTweener]. Any [PropertyTweener] created manually will not function correctly.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="as_relative">
+			<return type="PropertyTweener" />
+			<description>
+				When called, the final value will be used as a relative value instead. Example:
+				[codeblock]
+				var tween = get_tree().create_tween()
+				tween.tween_property(self, "position", Vector2.RIGHT * 100, 1).as_relative() #the node will move by 100 pixels to the right
+				[/codeblock]
+			</description>
+		</method>
+		<method name="from">
+			<return type="PropertyTweener" />
+			<argument index="0" name="value" type="Variant" />
+			<description>
+				Sets a custom initial value to the [PropertyTweener]. Example:
+				[codeblock]
+				var tween = get_tree().create_tween()
+				tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100) #this will move the node from position (100, 100) to (200, 100)
+				[/codeblock]
+			</description>
+		</method>
+		<method name="from_current">
+			<return type="PropertyTweener" />
+			<description>
+				Makes the [PropertyTweener] use the current property value (i.e. at the time of creating this [PropertyTweener]) as a starting point. This is equivalent of using [method from] with the current value. These two calls will do the same:
+				[codeblock]
+				tween.tween_property(self, "position", Vector2(200, 100), 1).from(position)
+				tween.tween_property(self, "position", Vector2(200, 100), 1).from_current()
+				[/codeblock]
+			</description>
+		</method>
+		<method name="set_delay">
+			<return type="PropertyTweener" />
+			<argument index="0" name="delay" type="float" />
+			<description>
+				Sets the time in seconds after which the [PropertyTweener] will start interpolating. By default there's no delay.
+			</description>
+		</method>
+		<method name="set_ease">
+			<return type="PropertyTweener" />
+			<argument index="0" name="ease" type="int" enum="Tween.EaseType" />
+			<description>
+				Sets the type of used easing from [enum Tween.EaseType]. If not set, the default easing is used from the [Tween] that contains this Tweener.
+			</description>
+		</method>
+		<method name="set_trans">
+			<return type="PropertyTweener" />
+			<argument index="0" name="trans" type="int" enum="Tween.TransitionType" />
+			<description>
+				Sets the type of used transition from [enum Tween.TransitionType]. If not set, the default transition is used from the [Tween] that contains this Tweener.
+			</description>
+		</method>
+	</methods>
+	<constants>
+	</constants>
+</class>

+ 12 - 0
doc/classes/SceneTree.xml

@@ -73,6 +73,12 @@
 				The timer will be automatically freed after its time elapses.
 				The timer will be automatically freed after its time elapses.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="create_tween">
+			<return type="SceneTreeTween" />
+			<description>
+				Creates and returns a new [SceneTreeTween].
+			</description>
+		</method>
 		<method name="get_frame" qualifiers="const">
 		<method name="get_frame" qualifiers="const">
 			<return type="int" />
 			<return type="int" />
 			<description>
 			<description>
@@ -104,6 +110,12 @@
 				Returns a list of all nodes assigned to the given group.
 				Returns a list of all nodes assigned to the given group.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="get_processed_tweens">
+			<return type="Array" />
+			<description>
+				Returns an array of currently existing [SceneTreeTween]s in the [SceneTree] (both running and paused).
+			</description>
+		</method>
 		<method name="get_rpc_sender_id" qualifiers="const">
 		<method name="get_rpc_sender_id" qualifiers="const">
 			<return type="int" />
 			<return type="int" />
 			<description>
 			<description>

+ 324 - 0
doc/classes/SceneTreeTween.xml

@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SceneTreeTween" inherits="Reference" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Lightweight object used for general-purpose animation via script, using [Tweener]s.
+	</brief_description>
+	<description>
+		[SceneTreeTween] is a tween managed by the scene tree. As opposed to [Tween], it does not require the instantiation of a node.
+		[SceneTreeTween]s are more light-weight than [AnimationPlayer], so they are very much suited for simple animations or general tasks that don't require visual tweaking provided by the editor. They can be used in a fire-and-forget manner for some logic that normally would be done by code. You can e.g. make something shoot periodically by using a looped [CallbackTweener] with a delay.
+		A [SceneTreeTween] can be created by using either [method SceneTree.create_tween] or [method Node.create_tween]. [SceneTreeTween]s created manually (i.e. by using [code]Tween.new()[/code]) are invalid. They can't be used for tweening values, but you can do manual interpolation with [method interpolate_value].
+		A [SceneTreeTween] animation is composed of a sequence of [Tweener]s, which by default are executed one after another. You can create a sequence by appending [Tweener]s to the [SceneTreeTween]. Animating something with a [Tweener] is called tweening. Example tweening sequence looks like this:
+		[codeblock]
+		var tween = get_tree().create_tween()
+		tween.tween_property($Sprite, "modulate", Color.red, 1)
+		tween.tween_property($Sprite, "scale", Vector2(), 1)
+		tween.tween_callback($Sprite, "queue_free")
+		[/codeblock]
+		This sequence will make the [code]$Sprite[/code] node turn red, then shrink and finally the [method Node.queue_free] is called to remove the sprite. See methods [method tween_property], [method tween_interval], [method tween_callback] and [method tween_method] for more usage information.
+		When a [Tweener] is created with one of the [code]tween_*[/code] methods, a chained method call can be used to tweak the properties of this [Tweener]. For example, if you want to set different transition type in the above example, you can do:
+		[codeblock]
+		var tween = get_tree().create_tween()
+		tween.tween_property($Sprite, "modulate", Color.red, 1).set_trans(Tween.TRANS_SINE)
+		tween.tween_property($Sprite, "scale", Vector2(), 1).set_trans(Tween.TRANS_BOUNCE)
+		tween.tween_callback($Sprite, "queue_free")
+		[/codeblock]
+		Most of the [SceneTreeTween] methods can be chained this way too. In this example the [SceneTreeTween] is bound and have set a default transition:
+		[codeblock]
+		var tween = get_tree().create_tween().bind_node(self).set_trans(Tween.TRANS_ELASTIC)
+		tween.tween_property($Sprite, "modulate", Color.red, 1)
+		tween.tween_property($Sprite, "scale", Vector2(), 1)
+		tween.tween_callback($Sprite, "queue_free")
+		[/codeblock]
+		Another interesting use for [SceneTreeTween]s is animating arbitrary set of objects:
+		[codeblock]
+		var tween = create_tween()
+		for sprite in get_children():
+		    tween.tween_property(sprite, "position", Vector2(), 1)
+		[/codeblock]
+		In the example above, all children of a node are moved one after another to position (0, 0).
+		Some [Tweener]s use transitions and eases. The first accepts an [enum Tween.TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum Tween.EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum Tween.TransitionType] constants with [constant Tween.EASE_IN_OUT], and use the one that looks best.
+		[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url]
+		[b]Note:[/b] All [SceneTreeTween]s will automatically start by default. To prevent a [SceneTreeTween] from autostarting, you can call [method stop] immediately after it was created.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="bind_node">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="node" type="Node" />
+			<description>
+				Binds this [SceneTreeTween] with the given [code]node[/code]. [SceneTreeTween]s are processed directly by the [SceneTree], so they run independently of the animated nodes. When you bind a [Node] with the [SceneTreeTween], the [SceneTreeTween] will halt the animation when the object is not inside tree and the [SceneTreeTween] will be automatically killed when the bound object is freed. Also [constant TWEEN_PAUSE_BOUND] will make the pausing behavior dependent on the bound node.
+				For a shorter way to create and bind a [SceneTreeTween], you can use [method Node.create_tween].
+			</description>
+		</method>
+		<method name="chain">
+			<return type="SceneTreeTween" />
+			<description>
+				Used to chain two [Tweener]s after [method set_parallel] is called with [code]true[/code].
+				[codeblock]
+				var tween = create_tween().set_parallel(true)
+				tween.tween_property(...)
+				tween.tween_property(...) # Will run parallelly with above.
+				tween.chain().tween_property(...) # Will run after two above are finished.
+				[/codeblock]
+			</description>
+		</method>
+		<method name="custom_step">
+			<return type="bool" />
+			<argument index="0" name="delta" type="float" />
+			<description>
+				Processes the [SceneTreeTween] by given [code]delta[/code] value, in seconds. Mostly useful when the [SceneTreeTween] is paused, for controlling it manually. Can also be used to end the [SceneTreeTween] animation immediately, by using [code]delta[/code] longer than the whole duration.
+				Returns [code]true[/code] if the [SceneTreeTween] still has [Tweener]s that haven't finished.
+				[b]Note:[/b] The [SceneTreeTween] will become invalid after finished, but you can call [method stop] after the step, to keep it and reset.
+			</description>
+		</method>
+		<method name="get_total_elapsed_time" qualifiers="const">
+			<return type="float" />
+			<description>
+				Returns the total time in seconds the [SceneTreeTween] has been animating (i.e. time since it started, not counting pauses etc.). The time is affected by [method set_speed_scale] and [method stop] will reset it to [code]0[/code].
+				[b]Note:[/b] As it results from accumulating frame deltas, the time returned after the [SceneTreeTween] has finished animating will be slightly greater than the actual [SceneTreeTween] duration.
+			</description>
+		</method>
+		<method name="interpolate_value" qualifiers="const">
+			<return type="Variant" />
+			<argument index="0" name="initial_value" type="Variant" />
+			<argument index="1" name="delta_value" type="Variant" />
+			<argument index="2" name="elapsed_time" type="float" />
+			<argument index="3" name="duration" type="float" />
+			<argument index="4" name="trans_type" type="int" enum="Tween.TransitionType" />
+			<argument index="5" name="ease_type" type="int" enum="Tween.EaseType" />
+			<description>
+				This method can be used for manual interpolation of a value, when you don't want [SceneTreeTween] to do animating for you. It's similar to [method @GDScript.lerp], but with support for custom transition and easing.
+				[code]initial_value[/code] is the starting value of the interpolation.
+				[code]delta_value[/code] is the change of the value in the interpolation, i.e. it's equal to [code]final_value - initial_value[/code].
+				[code]elapsed_time[/code] is the time in seconds that passed after the interpolation started and it's used to control the position of the interpolation. E.g. when it's equal to half of the [code]duration[/code], the interpolated value will be halfway between initial and final values. This value can also be greater than [code]duration[/code] or lower than 0, which will extrapolate the value.
+				[code]duration[/code] is the total time of the interpolation.
+				[b]Note:[/b] If [code]duration[/code] is equal to [code]0[/code], the method will always return the final value, regardless of [code]elapsed_time[/code] provided.
+			</description>
+		</method>
+		<method name="is_running" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns whether the [SceneTreeTween] is currently running, i.e. it wasn't paused and it's not finished.
+			</description>
+		</method>
+		<method name="is_valid" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns whether the [SceneTreeTween] is valid. A valid [SceneTreeTween] is a [SceneTreeTween] contained by the scene tree (i.e. the array from [method SceneTree.get_processed_tweens] will contain this [SceneTreeTween]). [SceneTreeTween] might become invalid when it has finished tweening or was killed, also when created with [code]Tween.new()[/code]. Invalid [SceneTreeTween] can't have [Tweener]s appended, because it can't animate them. You can however still use [method interpolate_value].
+			</description>
+		</method>
+		<method name="kill">
+			<return type="void" />
+			<description>
+				Aborts all tweening operations and invalidates the [SceneTreeTween].
+			</description>
+		</method>
+		<method name="parallel">
+			<return type="SceneTreeTween" />
+			<description>
+				Makes the next [Tweener] run parallelly to the previous one. Example:
+				[codeblock]
+				var tween = create_tween()
+				tween.tween_property(...)
+				tween.parallel().tween_property(...)
+				tween.parallel().tween_property(...)
+				[/codeblock]
+				All [Tweener]s in the example will run at the same time.
+				You can make the [SceneTreeTween] parallel by default by using [method set_parallel].
+			</description>
+		</method>
+		<method name="pause">
+			<return type="void" />
+			<description>
+				Pauses the tweening. The animation can be resumed by using [method play].
+			</description>
+		</method>
+		<method name="play">
+			<return type="void" />
+			<description>
+				Resumes a paused or stopped [SceneTreeTween].
+			</description>
+		</method>
+		<method name="set_ease">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="ease" type="int" enum="Tween.EaseType" />
+			<description>
+				Sets the default ease type for [PropertyTweener]s and [MethodTweener]s animated by this [SceneTreeTween].
+			</description>
+		</method>
+		<method name="set_loops">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="loops" type="int" default="0" />
+			<description>
+				Sets the number of times the tweening sequence will be repeated, i.e. [code]set_loops(2)[/code] will run the animation twice.
+				Calling this method without arguments will make the [SceneTreeTween] run infinitely, until it is either killed by [method kill] or by freeing bound node, or all the animated objects have been freed (which makes further animation impossible).
+				[b]Warning:[/b] Make sure to always add some duration/delay when using infinite loops. 0-duration looped animations (e.g. single [CallbackTweener] with no delay or [PropertyTweener] with invalid node) are equivalent to infinite [code]while[/code] loops and will freeze your game. If a [SceneTreeTween]'s lifetime depends on some node, always use [method bind_node].
+			</description>
+		</method>
+		<method name="set_parallel">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="parallel" type="bool" default="true" />
+			<description>
+				If [code]parallel[/code] is [code]true[/code], the [Tweener]s appended after this method will by default run simultaneously, as opposed to sequentially.
+			</description>
+		</method>
+		<method name="set_pause_mode">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="mode" type="int" enum="SceneTreeTween.TweenPauseMode" />
+			<description>
+				Determines the behavior of the [SceneTreeTween] when the [SceneTree] is paused. Check [enum TweenPauseMode] for options.
+				Default value is [constant TWEEN_PAUSE_BOUND].
+			</description>
+		</method>
+		<method name="set_process_mode">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="mode" type="int" enum="Tween.TweenProcessMode" />
+			<description>
+				Determines whether the [SceneTreeTween] should run during idle frame (see [method Node._process]) or physics frame (see [method Node._physics_process].
+				Default value is [constant Tween.TWEEN_PROCESS_IDLE].
+			</description>
+		</method>
+		<method name="set_speed_scale">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="speed" type="float" />
+			<description>
+				Scales the speed of tweening. This affects all [Tweener]s and their delays.
+			</description>
+		</method>
+		<method name="set_trans">
+			<return type="SceneTreeTween" />
+			<argument index="0" name="trans" type="int" enum="Tween.TransitionType" />
+			<description>
+				Sets the default transition type for [PropertyTweener]s and [MethodTweener]s animated by this [SceneTreeTween].
+			</description>
+		</method>
+		<method name="stop">
+			<return type="void" />
+			<description>
+				Stops the tweening and resets the [SceneTreeTween] to its initial state. This will not remove any appended [Tweener]s.
+			</description>
+		</method>
+		<method name="tween_callback">
+			<return type="CallbackTweener" />
+			<argument index="0" name="object" type="Object" />
+			<argument index="1" name="method" type="String" />
+			<argument index="2" name="binds" type="Array" default="[  ]" />
+			<description>
+				Creates and appends a [CallbackTweener]. This method can be used to call an arbitrary method in any object. Use [code]binds[/code] to bind additional arguments for the call.
+				Example: object that keeps shooting every 1 second.
+				[codeblock]
+				var tween = get_tree().create_tween().set_loops()
+				tween.tween_callback(self, "shoot").set_delay(1)
+				[/codeblock]
+				Example: turning a sprite red and then blue, with 2 second delay.
+				[codeblock]
+				var tween = get_tree().create_tween()
+				tween.tween_callback($Sprite, "set_modulate", [Color.red]).set_delay(2)
+				tween.tween_callback($Sprite, "set_modulate", [Color.blue]).set_delay(2)
+				[/codeblock]
+			</description>
+		</method>
+		<method name="tween_interval">
+			<return type="IntervalTweener" />
+			<argument index="0" name="time" type="float" />
+			<description>
+				Creates and appends an [IntervalTweener]. This method can be used to create delays in the tween animation, as an alternative for using the delay in other [Tweener]s or when there's no animation (in which case the [SceneTreeTween] acts as a timer). [code]time[/code] is the length of the interval, in seconds.
+				Example: creating an interval in code execution.
+				[codeblock]
+				# ... some code
+				yield(create_tween().tween_interval(2), "finished")
+				# ... more code
+				[/codeblock]
+				Example: creating an object that moves back and forth and jumps every few seconds.
+				[codeblock]
+				var tween = create_tween().set_loops()
+				tween.tween_property($Sprite, "position:x", 200.0, 1).as_relative()
+				tween.tween_callback(self, "jump")
+				tween.tween_interval(2)
+				tween.tween_property($Sprite, "position:x", -200.0, 1).as_relative()
+				tween.tween_callback(self, "jump")
+				tween.tween_interval(2)
+				[/codeblock]
+			</description>
+		</method>
+		<method name="tween_method">
+			<return type="MethodTweener" />
+			<argument index="0" name="object" type="Object" />
+			<argument index="1" name="method" type="String" />
+			<argument index="2" name="from" type="Variant" />
+			<argument index="3" name="to" type="Variant" />
+			<argument index="4" name="duration" type="float" />
+			<argument index="5" name="binds" type="Array" default="[  ]" />
+			<description>
+				Creates and appends a [MethodTweener]. This method is similar to a combination of [method tween_callback] and [method tween_property]. It calls a method over time with a tweened value provided as an argument. The value is tweened between [code]from[/code] and [code]to[/code] over the time specified by [code]duration[/code], in seconds. Use [code]binds[/code] to bind additional arguments for the call. You can use [method MethodTweener.set_ease] and [method MethodTweener.set_trans] to tweak the easing and transition of the value or [method MethodTweener.set_delay] to delay the tweening.
+				Example: making a 3D object look from one point to another point.
+				[codeblock]
+				var tween = create_tween()
+				tween.tween_method(self, "look_at", Vector3(-1, 0, -1), Vector3(1, 0, -1), 1, [Vector3.UP]) # The look_at() method takes up vector as second argument.
+				[/codeblock]
+				Example: setting a text of a [Label], using an intermediate method and after a delay.
+				[codeblock]
+				func _ready():
+				    var tween = create_tween()
+				    tween.tween_method(self, "set_label_text", 0, 10, 1).set_delay(1)
+
+				func set_label_text(value: int):
+				    $Label.text = "Counting " + str(value)
+				[/codeblock]
+			</description>
+		</method>
+		<method name="tween_property">
+			<return type="PropertyTweener" />
+			<argument index="0" name="object" type="Object" />
+			<argument index="1" name="property" type="NodePath" />
+			<argument index="2" name="final_val" type="Variant" />
+			<argument index="3" name="duration" type="float" />
+			<description>
+				Creates and appends a [PropertyTweener]. This method tweens a [code]property[/code] of an [code]object[/code] between an initial value and [code]final_val[/code] in a span of time equal to [code]duration[/code], in seconds. The initial value by default is a value at the time the tweening of the [PropertyTweener] start. For example:
+				[codeblock]
+				var tween = create_tween()
+				tween.tween_property($Sprite, "position", Vector2(100, 200), 1)
+				tween.tween_property($Sprite, "position", Vector2(200, 300), 1)
+				[/codeblock]
+				will move the sprite to position (100, 200) and then to (200, 300). If you use [method PropertyTweener.from] or [method PropertyTweener.from_current], the starting position will be overwritten by the given value instead. See other methods in [PropertyTweener] to see how the tweening can be tweaked further.
+				[b]Note:[/b] You can find the correct property name by hovering over the property in the Inspector. You can also provide the components of a property directly by using [code]"property:component"[/code] (eg. [code]position:x[/code]), where it would only apply to that particular component.
+				Example: moving object twice from the same position, with different transition types.
+				[codeblock]
+				var tween = create_tween()
+				tween.tween_property($Sprite, "position", Vector2.RIGHT * 300, 1).as_relative().set_trans(Tween.TRANS_SINE)
+				tween.tween_property($Sprite, "position", Vector2.RIGHT * 300, 1).as_relative().from_current().set_trans(Tween.TRANS_EXPO)
+				[/codeblock]
+			</description>
+		</method>
+	</methods>
+	<signals>
+		<signal name="finished">
+			<description>
+			</description>
+		</signal>
+		<signal name="loop_finished">
+			<argument index="0" name="loop_count" type="int" />
+			<description>
+				Emitted when a full loop is complete (see [method set_loops]), providing the loop index. This signal is not emitted after final loop, use [signal finished] instead for this case.
+			</description>
+		</signal>
+		<signal name="step_finished">
+			<argument index="0" name="idx" type="int" />
+			<description>
+				Emitted when one step of the [SceneTreeTween] is complete, providing the step index. One step is either a single [Tweener] or a group of [Tweener]s running parallelly.
+			</description>
+		</signal>
+	</signals>
+	<constants>
+		<constant name="TWEEN_PAUSE_BOUND" value="0" enum="TweenPauseMode">
+			If the [SceneTreeTween] has a bound node, it will process when that node can process (see [member Node.pause_mode]). Otherwise it's the same as [constant TWEEN_PAUSE_STOP].
+		</constant>
+		<constant name="TWEEN_PAUSE_STOP" value="1" enum="TweenPauseMode">
+			If [SceneTree] is paused, the [SceneTreeTween] will also pause.
+		</constant>
+		<constant name="TWEEN_PAUSE_PROCESS" value="2" enum="TweenPauseMode">
+			The [SceneTreeTween] will process regardless of whether [SceneTree] is paused.
+		</constant>
+	</constants>
+</class>

+ 1 - 0
doc/classes/Tween.xml

@@ -18,6 +18,7 @@
 		Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best.
 		Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best.
 		[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url]
 		[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url]
 		[b]Note:[/b] Tween methods will return [code]false[/code] if the requested operation cannot be completed.
 		[b]Note:[/b] Tween methods will return [code]false[/code] if the requested operation cannot be completed.
+		[b]Note:[/b] For an alternative method of tweening, that doesn't require using nodes, see [SceneTreeTween].
 	</description>
 	</description>
 	<tutorials>
 	<tutorials>
 	</tutorials>
 	</tutorials>

+ 22 - 0
doc/classes/Tweener.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Tweener" inherits="Reference" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Abstract class for all Tweeners used by [SceneTreeTween].
+	</brief_description>
+	<description>
+		Tweeners are objects that perform a specific animating task, e.g. interpolating a property or calling a method at a given time. A [Tweener] can't be created manually, you need to use a dedicated method from [SceneTreeTween].
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<signals>
+		<signal name="finished">
+			<description>
+				Emitted when the [Tweener] has just finished its job.
+			</description>
+		</signal>
+	</signals>
+	<constants>
+	</constants>
+</class>

+ 0 - 14
scene/animation/SCsub

@@ -2,23 +2,9 @@
 
 
 Import("env")
 Import("env")
 
 
-# Thirdparty code
-
-thirdparty_obj = []
-
-thirdparty_sources = "#thirdparty/misc/easing_equations.cpp"
-
-env_thirdparty = env.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
-env.scene_sources += thirdparty_obj
-
 # Godot source files
 # Godot source files
 
 
 scene_obj = []
 scene_obj = []
 
 
 env.add_source_files(scene_obj, "*.cpp")
 env.add_source_files(scene_obj, "*.cpp")
 env.scene_sources += scene_obj
 env.scene_sources += scene_obj
-
-# Needed to force rebuilding the scene files when the thirdparty code is updated.
-env.Depends(scene_obj, thirdparty_obj)

+ 405 - 0
scene/animation/easing_equations.h

@@ -0,0 +1,405 @@
+/*************************************************************************/
+/*  easing_equations.h                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+ * Derived from Robert Penner's easing equations: http://robertpenner.com/easing/
+ *
+ * Copyright (c) 2001 Robert Penner
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef EASING_EQUATIONS_H
+#define EASING_EQUATIONS_H
+
+namespace linear {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	return c * t / d + b;
+}
+}; // namespace linear
+
+namespace sine {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	return -c * cos(t / d * (Math_PI / 2)) + c + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	return c * sin(t / d * (Math_PI / 2)) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	return -c / 2 * (cos(Math_PI * t / d) - 1) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace sine
+
+namespace quint {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	return c * pow(t / d, 5) + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	return c * (pow(t / d - 1, 5) + 1) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	t = t / d * 2;
+
+	if (t < 1) {
+		return c / 2 * pow(t, 5) + b;
+	}
+	return c / 2 * (pow(t - 2, 5) + 2) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace quint
+
+namespace quart {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	return c * pow(t / d, 4) + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	return -c * (pow(t / d - 1, 4) - 1) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	t = t / d * 2;
+
+	if (t < 1) {
+		return c / 2 * pow(t, 4) + b;
+	}
+	return -c / 2 * (pow(t - 2, 4) - 2) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace quart
+
+namespace quad {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	return c * pow(t / d, 2) + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	t /= d;
+	return -c * t * (t - 2) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	t = t / d * 2;
+
+	if (t < 1) {
+		return c / 2 * pow(t, 2) + b;
+	}
+	return -c / 2 * ((t - 1) * (t - 3) - 1) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace quad
+
+namespace expo {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	if (t == 0) {
+		return b;
+	}
+	return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	if (t == d) {
+		return b + c;
+	}
+	return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	if (t == 0) {
+		return b;
+	}
+
+	if (t == d) {
+		return b + c;
+	}
+
+	t = t / d * 2;
+
+	if (t < 1) {
+		return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005;
+	}
+	return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace expo
+
+namespace elastic {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	if (t == 0) {
+		return b;
+	}
+
+	t /= d;
+	if (t == 1) {
+		return b + c;
+	}
+
+	t -= 1;
+	float p = d * 0.3f;
+	float a = c * pow(2, 10 * t);
+	float s = p / 4;
+
+	return -(a * sin((t * d - s) * (2 * Math_PI) / p)) + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	if (t == 0) {
+		return b;
+	}
+
+	t /= d;
+	if (t == 1) {
+		return b + c;
+	}
+
+	float p = d * 0.3f;
+	float s = p / 4;
+
+	return (c * pow(2, -10 * t) * sin((t * d - s) * (2 * Math_PI) / p) + c + b);
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	if (t == 0) {
+		return b;
+	}
+
+	if ((t /= d / 2) == 2) {
+		return b + c;
+	}
+
+	float p = d * (0.3f * 1.5f);
+	float a = c;
+	float s = p / 4;
+
+	if (t < 1) {
+		t -= 1;
+		a *= pow(2, 10 * t);
+		return -0.5f * (a * sin((t * d - s) * (2 * Math_PI) / p)) + b;
+	}
+
+	t -= 1;
+	a *= pow(2, -10 * t);
+	return a * sin((t * d - s) * (2 * Math_PI) / p) * 0.5f + c + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace elastic
+
+namespace cubic {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	t /= d;
+	return c * t * t * t + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	t = t / d - 1;
+	return c * (t * t * t + 1) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	t /= d / 2;
+	if (t < 1) {
+		return c / 2 * t * t * t + b;
+	}
+
+	t -= 2;
+	return c / 2 * (t * t * t + 2) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace cubic
+
+namespace circ {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	t /= d;
+	return -c * (sqrt(1 - t * t) - 1) + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	t = t / d - 1;
+	return c * sqrt(1 - t * t) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	t /= d / 2;
+	if (t < 1) {
+		return -c / 2 * (sqrt(1 - t * t) - 1) + b;
+	}
+
+	t -= 2;
+	return c / 2 * (sqrt(1 - t * t) + 1) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace circ
+
+namespace bounce {
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	t /= d;
+
+	if (t < (1 / 2.75f)) {
+		return c * (7.5625f * t * t) + b;
+	}
+
+	if (t < (2 / 2.75f)) {
+		t -= 1.5f / 2.75f;
+		return c * (7.5625f * t * t + 0.75f) + b;
+	}
+
+	if (t < (2.5 / 2.75)) {
+		t -= 2.25f / 2.75f;
+		return c * (7.5625f * t * t + 0.9375f) + b;
+	}
+
+	t -= 2.625f / 2.75f;
+	return c * (7.5625f * t * t + 0.984375f) + b;
+}
+
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	return c - out(d - t, 0, c, d) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return in(t * 2, b, c / 2, d);
+	}
+	return out(t * 2 - d, b + c / 2, c / 2, d);
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace bounce
+
+namespace back {
+static real_t in(real_t t, real_t b, real_t c, real_t d) {
+	float s = 1.70158f;
+	t /= d;
+
+	return c * t * t * ((s + 1) * t - s) + b;
+}
+
+static real_t out(real_t t, real_t b, real_t c, real_t d) {
+	float s = 1.70158f;
+	t = t / d - 1;
+
+	return c * (t * t * ((s + 1) * t + s) + 1) + b;
+}
+
+static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
+	float s = 1.70158f * 1.525f;
+	t /= d / 2;
+
+	if (t < 1) {
+		return c / 2 * (t * t * ((s + 1) * t - s)) + b;
+	}
+
+	t -= 2;
+	return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b;
+}
+
+static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
+	if (t < d / 2) {
+		return out(t * 2, b, c / 2, d);
+	}
+	return in(t * 2 - d, b + c / 2, c / 2, d);
+}
+}; // namespace back
+
+#endif

+ 927 - 0
scene/animation/scene_tree_tween.cpp

@@ -0,0 +1,927 @@
+/*************************************************************************/
+/*  scene_tree_tween.cpp                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "scene_tree_tween.h"
+
+#include "core/method_bind_ext.gen.inc"
+#include "scene/animation/tween.h"
+#include "scene/main/node.h"
+#include "scene/scene_string_names.h"
+
+void Tweener::set_tween(Ref<SceneTreeTween> p_tween) {
+	tween = p_tween;
+}
+
+void Tweener::clear_tween() {
+	tween.unref();
+}
+
+void Tweener::_bind_methods() {
+	ADD_SIGNAL(MethodInfo("finished"));
+}
+
+void SceneTreeTween::start_tweeners() {
+	if (tweeners.empty()) {
+		dead = true;
+		ERR_FAIL_MSG("SceneTreeTween without commands, aborting");
+	}
+
+	List<Ref<Tweener>> &step = tweeners.write[current_step];
+	for (int i = 0; i < step.size(); i++) {
+		Ref<Tweener> &tweener = step[i];
+		tweener->start();
+	}
+}
+
+Ref<PropertyTweener> SceneTreeTween::tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration) {
+	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.");
+
+#ifdef DEBUG_ENABLED
+	Variant::Type property_type = p_target->get_indexed(p_property.get_as_property_path().get_subnames()).get_type();
+	ERR_FAIL_COND_V_MSG(property_type != p_to.get_type(), Ref<PropertyTweener>(), "Type mismatch between property and final value: " + Variant::get_type_name(property_type) + " and " + Variant::get_type_name(p_to.get_type()));
+#endif
+
+	Ref<PropertyTweener> tweener = memnew(PropertyTweener(p_target, p_property, p_to, p_duration));
+	append(tweener);
+	return tweener;
+}
+
+Ref<IntervalTweener> SceneTreeTween::tween_interval(float p_time) {
+	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<IntervalTweener> tweener = memnew(IntervalTweener(p_time));
+	append(tweener);
+	return tweener;
+}
+
+Ref<CallbackTweener> SceneTreeTween::tween_callback(Object *p_target, StringName p_method, const Vector<Variant> &p_binds) {
+	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<CallbackTweener> tweener = memnew(CallbackTweener(p_target, p_method, p_binds));
+	append(tweener);
+	return tweener;
+}
+
+Ref<MethodTweener> SceneTreeTween::tween_method(Object *p_target, StringName p_method, Variant p_from, Variant p_to, float p_duration, const Vector<Variant> &p_binds) {
+	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<MethodTweener> tweener = memnew(MethodTweener(p_target, p_method, p_from, p_to, p_duration, p_binds));
+	append(tweener);
+	return tweener;
+}
+
+void SceneTreeTween::append(Ref<Tweener> p_tweener) {
+	p_tweener->set_tween(this);
+
+	if (parallel_enabled) {
+		current_step = MAX(current_step, 0);
+	} else {
+		current_step++;
+	}
+	parallel_enabled = default_parallel;
+
+	tweeners.resize(current_step + 1);
+	tweeners.write[current_step].push_back(p_tweener);
+}
+
+void SceneTreeTween::stop() {
+	started = false;
+	running = false;
+	dead = false;
+	total_time = 0;
+}
+
+void SceneTreeTween::pause() {
+	running = false;
+}
+
+void SceneTreeTween::play() {
+	ERR_FAIL_COND_MSG(!valid, "SceneTreeTween invalid. Either finished or created outside scene tree.");
+	ERR_FAIL_COND_MSG(dead, "Can't play finished SceneTreeTween, use stop() first to reset its state.");
+	running = true;
+}
+
+void SceneTreeTween::kill() {
+	running = false; // For the sake of is_running().
+	dead = true;
+}
+
+bool SceneTreeTween::is_running() const {
+	return running;
+}
+
+bool SceneTreeTween::is_valid() const {
+	return valid;
+}
+
+void SceneTreeTween::clear() {
+	valid = false;
+
+	for (int i = 0; i < tweeners.size(); i++) {
+		List<Ref<Tweener>> &step = tweeners.write[i];
+		for (int j = 0; j < step.size(); j++) {
+			Ref<Tweener> &tweener = step[j];
+			tweener->clear_tween();
+		}
+	}
+	tweeners.clear();
+}
+
+Ref<SceneTreeTween> SceneTreeTween::bind_node(Node *p_node) {
+	ERR_FAIL_NULL_V(p_node, this);
+
+	bound_node = p_node->get_instance_id();
+	is_bound = true;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_process_mode(Tween::TweenProcessMode p_mode) {
+	process_mode = p_mode;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_pause_mode(TweenPauseMode p_mode) {
+	pause_mode = p_mode;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_parallel(bool p_parallel) {
+	default_parallel = p_parallel;
+	parallel_enabled = p_parallel;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_loops(int p_loops) {
+	loops = p_loops;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_speed_scale(float p_speed) {
+	speed_scale = p_speed;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_trans(Tween::TransitionType p_trans) {
+	default_transition = p_trans;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::set_ease(Tween::EaseType p_ease) {
+	default_ease = p_ease;
+	return this;
+}
+
+Tween::TweenProcessMode SceneTreeTween::get_process_mode() const {
+	return process_mode;
+}
+
+SceneTreeTween::TweenPauseMode SceneTreeTween::get_pause_mode() const {
+	return pause_mode;
+}
+
+Tween::TransitionType SceneTreeTween::get_trans() const {
+	return default_transition;
+}
+
+Tween::EaseType SceneTreeTween::get_ease() const {
+	return default_ease;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::parallel() {
+	parallel_enabled = true;
+	return this;
+}
+
+Ref<SceneTreeTween> SceneTreeTween::chain() {
+	parallel_enabled = false;
+	return this;
+}
+
+bool SceneTreeTween::custom_step(float p_delta) {
+	bool r = running;
+	running = true;
+	bool ret = step(p_delta);
+	running = running && r; // Running might turn false when SceneTreeTween finished;
+	return ret;
+}
+
+bool SceneTreeTween::step(float p_delta) {
+	if (dead) {
+		return false;
+	}
+
+	if (!running) {
+		return true;
+	}
+
+	if (is_bound) {
+		Node *bound_node = get_bound_node();
+		if (bound_node) {
+			if (!bound_node->is_inside_tree()) {
+				return true;
+			}
+		} else {
+			return false;
+		}
+	}
+
+	if (!started) {
+		ERR_FAIL_COND_V_MSG(tweeners.empty(), false, "SceneTreeTween started, but has no Tweeners.");
+		current_step = 0;
+		loops_done = 0;
+		total_time = 0;
+		start_tweeners();
+		started = true;
+	}
+
+	float rem_delta = p_delta * speed_scale;
+	bool step_active = false;
+	total_time += rem_delta;
+
+	while (rem_delta > 0 && running) {
+		float step_delta = rem_delta;
+		step_active = false;
+
+#ifdef DEBUG_ENABLED
+		float prev_delta = rem_delta;
+#endif
+
+		List<Ref<Tweener>> &step = tweeners.write[current_step];
+		for (int i = 0; i < step.size(); i++) {
+			Ref<Tweener> &tweener = step[i];
+
+			// Modified inside Tweener.step().
+			float temp_delta = rem_delta;
+			// Turns to true if any Tweener returns true (i.e. is still not finished).
+			step_active = tweener->step(temp_delta) || step_active;
+			step_delta = MIN(temp_delta, step_delta);
+		}
+
+		rem_delta = step_delta;
+
+		if (!step_active) {
+			emit_signal(SceneStringNames::get_singleton()->step_finished, current_step);
+			current_step++;
+
+			if (current_step == tweeners.size()) {
+				loops_done++;
+				if (loops_done == loops) {
+					running = false;
+					dead = true;
+					emit_signal(SceneStringNames::get_singleton()->finished);
+				} else {
+					emit_signal(SceneStringNames::get_singleton()->loop_finished, loops_done);
+					current_step = 0;
+					start_tweeners();
+				}
+			} else {
+				start_tweeners();
+			}
+		}
+
+#ifdef DEBUG_ENABLED
+		if (Math::is_equal_approx(rem_delta, prev_delta) && running && loops <= 0) {
+			ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
+		}
+#endif
+	}
+
+	return true;
+}
+
+bool SceneTreeTween::can_process(bool p_tree_paused) const {
+	if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
+		Node *bound_node = get_bound_node();
+		if (bound_node) {
+			return bound_node->can_process();
+		}
+	}
+
+	return !p_tree_paused || pause_mode == TWEEN_PAUSE_PROCESS;
+}
+
+Node *SceneTreeTween::get_bound_node() const {
+	if (is_bound) {
+		return Object::cast_to<Node>(ObjectDB::get_instance(bound_node));
+	} else {
+		return nullptr;
+	}
+}
+
+float SceneTreeTween::get_total_time() const {
+	return total_time;
+}
+
+Variant SceneTreeTween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease) const {
+	ERR_FAIL_INDEX_V(p_trans, Tween::TRANS_COUNT, Variant());
+	ERR_FAIL_INDEX_V(p_ease, Tween::EASE_COUNT, Variant());
+
+// Helper macro to run equation on sub-elements of the value (e.g. x and y of Vector2).
+#define APPLY_EQUATION(element) \
+	r.element = Tween::run_equation(p_trans, p_ease, p_time, i.element, d.element, p_duration);
+
+	switch (p_initial_val.get_type()) {
+		case Variant::BOOL: {
+			return (Tween::run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration)) >= 0.5;
+		}
+
+		case Variant::INT: {
+			return (int)Tween::run_equation(p_trans, p_ease, p_time, (int)p_initial_val, (int)p_delta_val, p_duration);
+		}
+
+		case Variant::REAL: {
+			return Tween::run_equation(p_trans, p_ease, p_time, (real_t)p_initial_val, (real_t)p_delta_val, p_duration);
+		}
+
+		case Variant::VECTOR2: {
+			Vector2 i = p_initial_val;
+			Vector2 d = p_delta_val;
+			Vector2 r;
+
+			APPLY_EQUATION(x);
+			APPLY_EQUATION(y);
+			return r;
+		}
+
+		case Variant::RECT2: {
+			Rect2 i = p_initial_val;
+			Rect2 d = p_delta_val;
+			Rect2 r;
+
+			APPLY_EQUATION(position.x);
+			APPLY_EQUATION(position.y);
+			APPLY_EQUATION(size.x);
+			APPLY_EQUATION(size.y);
+			return r;
+		}
+
+		case Variant::VECTOR3: {
+			Vector3 i = p_initial_val;
+			Vector3 d = p_delta_val;
+			Vector3 r;
+
+			APPLY_EQUATION(x);
+			APPLY_EQUATION(y);
+			APPLY_EQUATION(z);
+			return r;
+		}
+
+		case Variant::TRANSFORM2D: {
+			Transform2D i = p_initial_val;
+			Transform2D d = p_delta_val;
+			Transform2D r;
+
+			APPLY_EQUATION(elements[0][0]);
+			APPLY_EQUATION(elements[0][1]);
+			APPLY_EQUATION(elements[1][0]);
+			APPLY_EQUATION(elements[1][1]);
+			APPLY_EQUATION(elements[2][0]);
+			APPLY_EQUATION(elements[2][1]);
+			return r;
+		}
+
+		case Variant::QUAT: {
+			Quat i = p_initial_val;
+			Quat d = p_delta_val;
+			Quat r;
+
+			APPLY_EQUATION(x);
+			APPLY_EQUATION(y);
+			APPLY_EQUATION(z);
+			APPLY_EQUATION(w);
+			return r;
+		}
+
+		case Variant::AABB: {
+			AABB i = p_initial_val;
+			AABB d = p_delta_val;
+			AABB r;
+
+			APPLY_EQUATION(position.x);
+			APPLY_EQUATION(position.y);
+			APPLY_EQUATION(position.z);
+			APPLY_EQUATION(size.x);
+			APPLY_EQUATION(size.y);
+			APPLY_EQUATION(size.z);
+			return r;
+		}
+
+		case Variant::BASIS: {
+			Basis i = p_initial_val;
+			Basis d = p_delta_val;
+			Basis r;
+
+			APPLY_EQUATION(elements[0][0]);
+			APPLY_EQUATION(elements[0][1]);
+			APPLY_EQUATION(elements[0][2]);
+			APPLY_EQUATION(elements[1][0]);
+			APPLY_EQUATION(elements[1][1]);
+			APPLY_EQUATION(elements[1][2]);
+			APPLY_EQUATION(elements[2][0]);
+			APPLY_EQUATION(elements[2][1]);
+			APPLY_EQUATION(elements[2][2]);
+			return r;
+		}
+
+		case Variant::TRANSFORM: {
+			Transform i = p_initial_val;
+			Transform d = p_delta_val;
+			Transform r;
+
+			APPLY_EQUATION(basis.elements[0][0]);
+			APPLY_EQUATION(basis.elements[0][1]);
+			APPLY_EQUATION(basis.elements[0][2]);
+			APPLY_EQUATION(basis.elements[1][0]);
+			APPLY_EQUATION(basis.elements[1][1]);
+			APPLY_EQUATION(basis.elements[1][2]);
+			APPLY_EQUATION(basis.elements[2][0]);
+			APPLY_EQUATION(basis.elements[2][1]);
+			APPLY_EQUATION(basis.elements[2][2]);
+			APPLY_EQUATION(origin.x);
+			APPLY_EQUATION(origin.y);
+			APPLY_EQUATION(origin.z);
+			return r;
+		}
+
+		case Variant::COLOR: {
+			Color i = p_initial_val;
+			Color d = p_delta_val;
+			Color r;
+
+			APPLY_EQUATION(r);
+			APPLY_EQUATION(g);
+			APPLY_EQUATION(b);
+			APPLY_EQUATION(a);
+			return r;
+		}
+
+		default: {
+			return p_initial_val;
+		}
+	};
+#undef APPLY_EQUATION
+}
+
+Variant SceneTreeTween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) {
+	ERR_FAIL_COND_V_MSG(p_intial_val.get_type() != p_final_val.get_type(), p_intial_val, "Type mismatch between initial and final value: " + Variant::get_type_name(p_intial_val.get_type()) + " and " + Variant::get_type_name(p_final_val.get_type()));
+
+	switch (p_intial_val.get_type()) {
+		case Variant::BOOL: {
+			return (int)p_final_val - (int)p_intial_val;
+		}
+
+		case Variant::RECT2: {
+			Rect2 i = p_intial_val;
+			Rect2 f = p_final_val;
+			return Rect2(f.position - i.position, f.size - i.size);
+		}
+
+		case Variant::TRANSFORM2D: {
+			Transform2D i = p_intial_val;
+			Transform2D f = p_final_val;
+			return Transform2D(f.elements[0][0] - i.elements[0][0],
+					f.elements[0][1] - i.elements[0][1],
+					f.elements[1][0] - i.elements[1][0],
+					f.elements[1][1] - i.elements[1][1],
+					f.elements[2][0] - i.elements[2][0],
+					f.elements[2][1] - i.elements[2][1]);
+		}
+
+		case Variant::AABB: {
+			AABB i = p_intial_val;
+			AABB f = p_final_val;
+			return AABB(f.position - i.position, f.size - i.size);
+		}
+
+		case Variant::BASIS: {
+			Basis i = p_intial_val;
+			Basis f = p_final_val;
+			return Basis(f.elements[0][0] - i.elements[0][0],
+					f.elements[0][1] - i.elements[0][1],
+					f.elements[0][2] - i.elements[0][2],
+					f.elements[1][0] - i.elements[1][0],
+					f.elements[1][1] - i.elements[1][1],
+					f.elements[1][2] - i.elements[1][2],
+					f.elements[2][0] - i.elements[2][0],
+					f.elements[2][1] - i.elements[2][1],
+					f.elements[2][2] - i.elements[2][2]);
+		}
+
+		case Variant::TRANSFORM: {
+			Transform i = p_intial_val;
+			Transform f = p_final_val;
+			return Transform(f.basis.elements[0][0] - i.basis.elements[0][0],
+					f.basis.elements[0][1] - i.basis.elements[0][1],
+					f.basis.elements[0][2] - i.basis.elements[0][2],
+					f.basis.elements[1][0] - i.basis.elements[1][0],
+					f.basis.elements[1][1] - i.basis.elements[1][1],
+					f.basis.elements[1][2] - i.basis.elements[1][2],
+					f.basis.elements[2][0] - i.basis.elements[2][0],
+					f.basis.elements[2][1] - i.basis.elements[2][1],
+					f.basis.elements[2][2] - i.basis.elements[2][2],
+					f.origin.x - i.origin.x,
+					f.origin.y - i.origin.y,
+					f.origin.z - i.origin.z);
+		}
+
+		default: {
+			return Variant::evaluate(Variant::OP_SUBTRACT, p_final_val, p_intial_val);
+		}
+	};
+}
+
+void SceneTreeTween::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &SceneTreeTween::tween_property);
+	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("custom_step", "delta"), &SceneTreeTween::custom_step);
+	ClassDB::bind_method(D_METHOD("stop"), &SceneTreeTween::stop);
+	ClassDB::bind_method(D_METHOD("pause"), &SceneTreeTween::pause);
+	ClassDB::bind_method(D_METHOD("play"), &SceneTreeTween::play);
+	ClassDB::bind_method(D_METHOD("kill"), &SceneTreeTween::kill);
+	ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &SceneTreeTween::get_total_time);
+
+	ClassDB::bind_method(D_METHOD("is_running"), &SceneTreeTween::is_running);
+	ClassDB::bind_method(D_METHOD("is_valid"), &SceneTreeTween::is_valid);
+	ClassDB::bind_method(D_METHOD("bind_node", "node"), &SceneTreeTween::bind_node);
+	ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &SceneTreeTween::set_process_mode);
+	ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &SceneTreeTween::set_pause_mode);
+
+	ClassDB::bind_method(D_METHOD("set_parallel", "parallel"), &SceneTreeTween::set_parallel, DEFVAL(true));
+	ClassDB::bind_method(D_METHOD("set_loops", "loops"), &SceneTreeTween::set_loops, DEFVAL(0));
+	ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &SceneTreeTween::set_speed_scale);
+	ClassDB::bind_method(D_METHOD("set_trans", "trans"), &SceneTreeTween::set_trans);
+	ClassDB::bind_method(D_METHOD("set_ease", "ease"), &SceneTreeTween::set_ease);
+
+	ClassDB::bind_method(D_METHOD("parallel"), &SceneTreeTween::parallel);
+	ClassDB::bind_method(D_METHOD("chain"), &SceneTreeTween::chain);
+
+	ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &SceneTreeTween::interpolate_variant);
+
+	ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
+	ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
+	ADD_SIGNAL(MethodInfo("finished"));
+
+	BIND_ENUM_CONSTANT(TWEEN_PAUSE_BOUND);
+	BIND_ENUM_CONSTANT(TWEEN_PAUSE_STOP);
+	BIND_ENUM_CONSTANT(TWEEN_PAUSE_PROCESS);
+}
+
+SceneTreeTween::SceneTreeTween(bool p_valid) {
+	valid = p_valid;
+}
+
+Ref<PropertyTweener> PropertyTweener::from(Variant p_value) {
+	initial_val = p_value;
+	do_continue = false;
+	return this;
+}
+
+Ref<PropertyTweener> PropertyTweener::from_current() {
+	do_continue = false;
+	return this;
+}
+
+Ref<PropertyTweener> PropertyTweener::as_relative() {
+	relative = true;
+	return this;
+}
+
+Ref<PropertyTweener> PropertyTweener::set_trans(Tween::TransitionType p_trans) {
+	trans_type = p_trans;
+	return this;
+}
+
+Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) {
+	ease_type = p_ease;
+	return this;
+}
+
+Ref<PropertyTweener> PropertyTweener::set_delay(float p_delay) {
+	delay = p_delay;
+	return this;
+}
+
+void PropertyTweener::start() {
+	elapsed_time = 0;
+	finished = false;
+
+	Object *target_instance = ObjectDB::get_instance(target);
+	if (!target_instance) {
+		WARN_PRINT("Target object freed before starting, aborting Tweener.");
+		return;
+	}
+
+	if (do_continue) {
+		initial_val = target_instance->get_indexed(property);
+	}
+
+	if (relative) {
+		final_val = Variant::evaluate(Variant::Operator::OP_ADD, initial_val, base_final_val);
+	}
+
+	delta_val = tween->calculate_delta_value(initial_val, final_val);
+}
+
+bool PropertyTweener::step(float &r_delta) {
+	if (finished) {
+		// This is needed in case there's a parallel Tweener with longer duration.
+		return false;
+	}
+
+	Object *target_instance = ObjectDB::get_instance(target);
+	if (!target_instance) {
+		return false;
+	}
+	elapsed_time += r_delta;
+
+	if (elapsed_time < delay) {
+		r_delta = 0;
+		return true;
+	}
+
+	float 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));
+		r_delta = 0;
+		return true;
+	} else {
+		target_instance->set_indexed(property, final_val);
+		finished = true;
+		r_delta = elapsed_time - delay - duration;
+		emit_signal(SceneStringNames::get_singleton()->finished);
+		return false;
+	}
+}
+
+void PropertyTweener::set_tween(Ref<SceneTreeTween> p_tween) {
+	tween = p_tween;
+	if (trans_type == Tween::TRANS_COUNT) {
+		trans_type = tween->get_trans();
+	}
+	if (ease_type == Tween::EASE_COUNT) {
+		ease_type = tween->get_ease();
+	}
+}
+
+void PropertyTweener::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("from", "value"), &PropertyTweener::from);
+	ClassDB::bind_method(D_METHOD("from_current"), &PropertyTweener::from_current);
+	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_delay", "delay"), &PropertyTweener::set_delay);
+}
+
+PropertyTweener::PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, float p_duration) {
+	target = p_target->get_instance_id();
+	property = p_property.get_as_property_path().get_subnames();
+	initial_val = p_target->get_indexed(property);
+	base_final_val = p_to;
+	final_val = base_final_val;
+	duration = p_duration;
+}
+
+PropertyTweener::PropertyTweener() {
+	ERR_FAIL_MSG("Can't create empty PropertyTweener. Use get_tree().tween_property() or tween_property() instead.");
+}
+
+void IntervalTweener::start() {
+	elapsed_time = 0;
+	finished = false;
+}
+
+bool IntervalTweener::step(float &r_delta) {
+	if (finished) {
+		return false;
+	}
+
+	elapsed_time += r_delta;
+
+	if (elapsed_time < duration) {
+		r_delta = 0;
+		return true;
+	} else {
+		finished = true;
+		r_delta = elapsed_time - duration;
+		emit_signal(SceneStringNames::get_singleton()->finished);
+		return false;
+	}
+}
+
+IntervalTweener::IntervalTweener(float p_time) {
+	duration = p_time;
+}
+
+IntervalTweener::IntervalTweener() {
+	ERR_FAIL_MSG("Can't create empty IntervalTweener. Use get_tree().tween_property() or tween_property() instead.");
+}
+
+Ref<CallbackTweener> CallbackTweener::set_delay(float p_delay) {
+	delay = p_delay;
+	return this;
+}
+
+void CallbackTweener::start() {
+	elapsed_time = 0;
+	finished = false;
+}
+
+bool CallbackTweener::step(float &r_delta) {
+	if (finished) {
+		return false;
+	}
+
+	Object *target_instance = ObjectDB::get_instance(target);
+	if (!target_instance) {
+		return false;
+	}
+
+	elapsed_time += r_delta;
+	if (elapsed_time >= delay) {
+		Vector<const Variant *> bind_mem;
+
+		if (binds.size()) {
+			bind_mem.resize(binds.size());
+
+			for (int i = 0; i < binds.size(); i++) {
+				bind_mem.write[i] = &binds[i];
+			}
+		}
+
+		const Variant **args = (const Variant **)bind_mem.ptr();
+		int argc = bind_mem.size();
+
+		Variant::CallError ce;
+		target_instance->call(method, args, argc, ce);
+		if (ce.error != Variant::CallError::CALL_OK) {
+			ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_call_error_text(target_instance, method, args, argc, ce));
+		}
+
+		finished = true;
+		r_delta = elapsed_time - delay;
+		emit_signal(SceneStringNames::get_singleton()->finished);
+		return false;
+	}
+
+	r_delta = 0;
+	return true;
+}
+
+void CallbackTweener::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_delay", "delay"), &CallbackTweener::set_delay);
+}
+
+CallbackTweener::CallbackTweener(Object *p_target, StringName p_method, const Vector<Variant> &p_binds) {
+	target = p_target->get_instance_id();
+	method = p_method;
+	binds = p_binds;
+}
+
+CallbackTweener::CallbackTweener() {
+	ERR_FAIL_MSG("Can't create empty CallbackTweener. Use get_tree().tween_callback() instead.");
+}
+
+Ref<MethodTweener> MethodTweener::set_delay(float p_delay) {
+	delay = p_delay;
+	return this;
+}
+
+Ref<MethodTweener> MethodTweener::set_trans(Tween::TransitionType p_trans) {
+	trans_type = p_trans;
+	return this;
+}
+
+Ref<MethodTweener> MethodTweener::set_ease(Tween::EaseType p_ease) {
+	ease_type = p_ease;
+	return this;
+}
+
+void MethodTweener::start() {
+	elapsed_time = 0;
+	finished = false;
+}
+
+bool MethodTweener::step(float &r_delta) {
+	if (finished) {
+		return false;
+	}
+
+	Object *target_instance = ObjectDB::get_instance(target);
+	if (!target_instance) {
+		return false;
+	}
+
+	elapsed_time += r_delta;
+
+	if (elapsed_time < delay) {
+		r_delta = 0;
+		return true;
+	}
+
+	Variant current_val;
+	float time = MIN(elapsed_time - delay, duration);
+	if (time < duration) {
+		current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
+	} else {
+		current_val = final_val;
+	}
+
+	Vector<const Variant *> bind_mem;
+
+	if (binds.empty()) {
+		bind_mem.push_back(&current_val);
+	} else {
+		bind_mem.resize(1 + binds.size());
+
+		bind_mem.write[0] = &current_val;
+		for (int i = 0; i < binds.size(); i++) {
+			bind_mem.write[1 + i] = &binds[i];
+		}
+	}
+
+	const Variant **args = (const Variant **)bind_mem.ptr();
+	int argc = bind_mem.size();
+
+	Variant::CallError ce;
+	target_instance->call(method, args, argc, ce);
+	if (ce.error != Variant::CallError::CALL_OK) {
+		ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_call_error_text(target_instance, method, args, argc, ce));
+	}
+
+	if (time < duration) {
+		r_delta = 0;
+		return true;
+	} else {
+		finished = true;
+		r_delta = elapsed_time - delay - duration;
+		emit_signal(SceneStringNames::get_singleton()->finished);
+		return false;
+	}
+}
+
+void MethodTweener::set_tween(Ref<SceneTreeTween> p_tween) {
+	tween = p_tween;
+	if (trans_type == Tween::TRANS_COUNT) {
+		trans_type = tween->get_trans();
+	}
+	if (ease_type == Tween::EASE_COUNT) {
+		ease_type = tween->get_ease();
+	}
+}
+
+void MethodTweener::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_delay", "delay"), &MethodTweener::set_delay);
+	ClassDB::bind_method(D_METHOD("set_trans", "trans"), &MethodTweener::set_trans);
+	ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease);
+}
+
+MethodTweener::MethodTweener(Object *p_target, StringName p_method, Variant p_from, Variant p_to, float p_duration, const Vector<Variant> &p_binds) {
+	target = p_target->get_instance_id();
+	method = p_method;
+	binds = p_binds;
+	initial_val = p_from;
+	delta_val = tween->calculate_delta_value(p_from, p_to);
+	final_val = p_to;
+	duration = p_duration;
+}
+
+MethodTweener::MethodTweener() {
+	ERR_FAIL_MSG("Can't create empty MethodTweener. Use get_tree().tween_method() instead.");
+}

+ 253 - 0
scene/animation/scene_tree_tween.h

@@ -0,0 +1,253 @@
+/*************************************************************************/
+/*  scene_tree_tween.h                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef SCENE_TREE_TWEEN_H
+#define SCENE_TREE_TWEEN_H
+
+#include "core/reference.h"
+#include "scene/animation/tween.h"
+
+class SceneTreeTween;
+
+class Tweener : public Reference {
+	GDCLASS(Tweener, Reference);
+
+public:
+	virtual void set_tween(Ref<SceneTreeTween> p_tween);
+	virtual void start() = 0;
+	virtual bool step(float &r_delta) = 0;
+	void clear_tween();
+
+protected:
+	static void _bind_methods();
+	Ref<SceneTreeTween> tween;
+	float elapsed_time = 0;
+	bool finished = false;
+};
+
+class PropertyTweener;
+class IntervalTweener;
+class CallbackTweener;
+class MethodTweener;
+
+class SceneTreeTween : public Reference {
+	GDCLASS(SceneTreeTween, Reference);
+
+public:
+	enum TweenPauseMode {
+		TWEEN_PAUSE_BOUND,
+		TWEEN_PAUSE_STOP,
+		TWEEN_PAUSE_PROCESS,
+	};
+
+private:
+	Tween::TweenProcessMode process_mode = Tween::TWEEN_PROCESS_IDLE;
+	TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_BOUND;
+	Tween::TransitionType default_transition = Tween::TRANS_LINEAR;
+	Tween::EaseType default_ease = Tween::EASE_IN_OUT;
+	ObjectID bound_node;
+
+	Vector<List<Ref<Tweener>>> tweeners;
+	float total_time = 0;
+	int current_step = -1;
+	int loops = 1;
+	int loops_done = 0;
+	float speed_scale = 1;
+
+	bool is_bound = false;
+	bool started = false;
+	bool running = true;
+	bool dead = false;
+	bool valid = false;
+	bool default_parallel = false;
+	bool parallel_enabled = false;
+
+	void start_tweeners();
+
+protected:
+	static void _bind_methods();
+
+public:
+	Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration);
+	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>());
+	void append(Ref<Tweener> p_tweener);
+
+	bool custom_step(float p_delta);
+	void stop();
+	void pause();
+	void play();
+	void kill();
+
+	bool is_running() const;
+	bool is_valid() const;
+	void clear();
+
+	Tween::TweenProcessMode get_process_mode() const;
+	TweenPauseMode get_pause_mode() const;
+	Tween::TransitionType get_trans() const;
+	Tween::EaseType get_ease() const;
+
+	Ref<SceneTreeTween> bind_node(Node *p_node);
+	Ref<SceneTreeTween> set_process_mode(Tween::TweenProcessMode p_mode);
+	Ref<SceneTreeTween> set_pause_mode(TweenPauseMode p_mode);
+	Ref<SceneTreeTween> set_parallel(bool p_parallel);
+	Ref<SceneTreeTween> set_loops(int p_loops);
+	Ref<SceneTreeTween> set_speed_scale(float p_speed);
+	Ref<SceneTreeTween> set_trans(Tween::TransitionType p_trans);
+	Ref<SceneTreeTween> set_ease(Tween::EaseType p_ease);
+
+	Ref<SceneTreeTween> parallel();
+	Ref<SceneTreeTween> chain();
+
+	Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease) const;
+	Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val);
+
+	bool step(float p_delta);
+	bool can_process(bool p_tree_paused) const;
+	Node *get_bound_node() const;
+	float get_total_time() const;
+
+	SceneTreeTween() = default;
+	SceneTreeTween(bool p_valid);
+};
+
+VARIANT_ENUM_CAST(SceneTreeTween::TweenPauseMode);
+
+class PropertyTweener : public Tweener {
+	GDCLASS(PropertyTweener, Tweener);
+
+public:
+	Ref<PropertyTweener> from(Variant p_value);
+	Ref<PropertyTweener> from_current();
+	Ref<PropertyTweener> as_relative();
+	Ref<PropertyTweener> set_trans(Tween::TransitionType p_trans);
+	Ref<PropertyTweener> set_ease(Tween::EaseType p_ease);
+	Ref<PropertyTweener> set_delay(float p_delay);
+
+	virtual void set_tween(Ref<SceneTreeTween> p_tween);
+	virtual void start();
+	virtual bool step(float &r_delta);
+
+	PropertyTweener(Object *p_target, NodePath p_property, Variant p_to, float p_duration);
+	PropertyTweener();
+
+protected:
+	static void _bind_methods();
+
+private:
+	ObjectID target;
+	Vector<StringName> property;
+	Variant initial_val;
+	Variant base_final_val;
+	Variant final_val;
+	Variant delta_val;
+
+	float duration = 0;
+	Tween::TransitionType trans_type = Tween::TRANS_COUNT; // This is set inside set_tween();
+	Tween::EaseType ease_type = Tween::EASE_COUNT;
+
+	float delay = 0;
+	bool do_continue = true;
+	bool relative = false;
+};
+
+class IntervalTweener : public Tweener {
+	GDCLASS(IntervalTweener, Tweener);
+
+public:
+	virtual void start();
+	virtual bool step(float &r_delta);
+
+	IntervalTweener(float p_time);
+	IntervalTweener();
+
+private:
+	float duration = 0;
+};
+
+class CallbackTweener : public Tweener {
+	GDCLASS(CallbackTweener, Tweener);
+
+public:
+	Ref<CallbackTweener> set_delay(float p_delay);
+
+	virtual void start();
+	virtual bool step(float &r_delta);
+
+	CallbackTweener(Object *p_target, StringName p_method, const Vector<Variant> &p_binds);
+	CallbackTweener();
+
+protected:
+	static void _bind_methods();
+
+private:
+	ObjectID target;
+	StringName method;
+	Vector<Variant> binds;
+	int args = 0;
+	float delay = 0;
+};
+
+class MethodTweener : public Tweener {
+	GDCLASS(MethodTweener, Tweener);
+
+public:
+	Ref<MethodTweener> set_trans(Tween::TransitionType p_trans);
+	Ref<MethodTweener> set_ease(Tween::EaseType p_ease);
+	Ref<MethodTweener> set_delay(float p_delay);
+
+	virtual void set_tween(Ref<SceneTreeTween> p_tween);
+	virtual void start();
+	virtual bool step(float &r_delta);
+
+	MethodTweener(Object *p_target, StringName p_method, Variant p_from, Variant p_to, float p_duration, const Vector<Variant> &p_binds);
+	MethodTweener();
+
+protected:
+	static void _bind_methods();
+
+private:
+	float duration = 0;
+	float delay = 0;
+	Tween::TransitionType trans_type = Tween::TRANS_COUNT;
+	Tween::EaseType ease_type = Tween::EASE_COUNT;
+
+	Ref<SceneTreeTween> tween;
+	Variant initial_val;
+	Variant delta_val;
+	Variant final_val;
+	ObjectID target;
+	StringName method;
+	Vector<Variant> binds;
+};
+
+#endif

+ 30 - 4
scene/animation/tween.cpp

@@ -31,6 +31,32 @@
 #include "tween.h"
 #include "tween.h"
 
 
 #include "core/method_bind_ext.gen.inc"
 #include "core/method_bind_ext.gen.inc"
+#include "scene/animation/easing_equations.h"
+
+Tween::interpolater Tween::interpolaters[Tween::TRANS_COUNT][Tween::EASE_COUNT] = {
+	{ &linear::in, &linear::in, &linear::in, &linear::in }, // Linear is the same for each easing.
+	{ &sine::in, &sine::out, &sine::in_out, &sine::out_in },
+	{ &quint::in, &quint::out, &quint::in_out, &quint::out_in },
+	{ &quart::in, &quart::out, &quart::in_out, &quart::out_in },
+	{ &quad::in, &quad::out, &quad::in_out, &quad::out_in },
+	{ &expo::in, &expo::out, &expo::in_out, &expo::out_in },
+	{ &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in },
+	{ &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in },
+	{ &circ::in, &circ::out, &circ::in_out, &circ::out_in },
+	{ &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in },
+	{ &back::in, &back::out, &back::in_out, &back::out_in },
+};
+
+real_t Tween::run_equation(Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) {
+	if (p_duration == 0) {
+		// Special case to avoid dividing by 0 in equations.
+		return p_initial + p_delta;
+	}
+
+	interpolater func = interpolaters[p_trans_type][p_ease_type];
+	ERR_FAIL_NULL_V(func, p_initial);
+	return func(p_time, p_initial, p_delta, p_duration);
+}
 
 
 void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) {
 void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) {
 	// Add a new pending command and reference it
 	// Add a new pending command and reference it
@@ -444,23 +470,23 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
 	Variant result;
 	Variant result;
 
 
 #define APPLY_EQUATION(element) \
 #define APPLY_EQUATION(element) \
-	r.element = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, i.element, d.element, p_data.duration);
+	r.element = run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, i.element, d.element, p_data.duration);
 
 
 	// What type of data are we interpolating?
 	// What type of data are we interpolating?
 	switch (initial_val.get_type()) {
 	switch (initial_val.get_type()) {
 		case Variant::BOOL:
 		case Variant::BOOL:
 			// Run the boolean specific equation (checking if it is at least 0.5)
 			// Run the boolean specific equation (checking if it is at least 0.5)
-			result = (_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, initial_val, delta_val, p_data.duration)) >= 0.5;
+			result = (run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, initial_val, delta_val, p_data.duration)) >= 0.5;
 			break;
 			break;
 
 
 		case Variant::INT:
 		case Variant::INT:
 			// Run the integer specific equation
 			// Run the integer specific equation
-			result = (int)_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (int)initial_val, (int)delta_val, p_data.duration);
+			result = (int)run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (int)initial_val, (int)delta_val, p_data.duration);
 			break;
 			break;
 
 
 		case Variant::REAL:
 		case Variant::REAL:
 			// Run the REAL specific equation
 			// Run the REAL specific equation
-			result = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (real_t)initial_val, (real_t)delta_val, p_data.duration);
+			result = run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (real_t)initial_val, (real_t)delta_val, p_data.duration);
 			break;
 			break;
 
 
 		case Variant::VECTOR2: {
 		case Variant::VECTOR2: {

+ 2 - 1
scene/animation/tween.h

@@ -130,7 +130,6 @@ private:
 	typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
 	typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
 	static interpolater interpolaters[TRANS_COUNT][EASE_COUNT];
 	static interpolater interpolaters[TRANS_COUNT][EASE_COUNT];
 
 
-	real_t _run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
 	Variant &_get_delta_val(InterpolateData &p_data);
 	Variant &_get_delta_val(InterpolateData &p_data);
 	Variant _get_initial_val(const InterpolateData &p_data) const;
 	Variant _get_initial_val(const InterpolateData &p_data) const;
 	Variant _get_final_val(const InterpolateData &p_data) const;
 	Variant _get_final_val(const InterpolateData &p_data) const;
@@ -152,6 +151,8 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
+	static real_t run_equation(Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration);
+
 	bool is_active() const;
 	bool is_active() const;
 	void set_active(bool p_active);
 	void set_active(bool p_active);
 
 

+ 10 - 0
scene/main/node.cpp

@@ -35,6 +35,7 @@
 #include "core/message_queue.h"
 #include "core/message_queue.h"
 #include "core/print_string.h"
 #include "core/print_string.h"
 #include "instance_placeholder.h"
 #include "instance_placeholder.h"
+#include "scene/animation/scene_tree_tween.h"
 #include "scene/resources/packed_scene.h"
 #include "scene/resources/packed_scene.h"
 #include "scene/scene_string_names.h"
 #include "scene/scene_string_names.h"
 #include "viewport.h"
 #include "viewport.h"
@@ -1810,6 +1811,14 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
 int Node::get_index() const {
 int Node::get_index() const {
 	return data.pos;
 	return data.pos;
 }
 }
+
+Ref<SceneTreeTween> Node::create_tween() {
+	ERR_FAIL_COND_V_MSG(!data.tree, nullptr, "Can't create SceneTreeTween when not inside scene tree.");
+	Ref<SceneTreeTween> tween = get_tree()->create_tween();
+	tween->bind_node(this);
+	return tween;
+}
+
 void Node::remove_and_skip() {
 void Node::remove_and_skip() {
 	ERR_FAIL_COND(!data.parent);
 	ERR_FAIL_COND(!data.parent);
 
 
@@ -2873,6 +2882,7 @@ void Node::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("reset_physics_interpolation"), &Node::reset_physics_interpolation);
 	ClassDB::bind_method(D_METHOD("reset_physics_interpolation"), &Node::reset_physics_interpolation);
 
 
 	ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree);
 	ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree);
+	ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween);
 
 
 	ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS));
 	ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS));
 	ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_data"), &Node::replace_by, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_data"), &Node::replace_by, DEFVAL(false));

+ 4 - 0
scene/main/node.h

@@ -41,6 +41,8 @@
 
 
 class Viewport;
 class Viewport;
 class SceneState;
 class SceneState;
+class SceneTreeTween;
+
 class Node : public Object {
 class Node : public Object {
 	GDCLASS(Node, Object);
 	GDCLASS(Node, Object);
 	OBJ_CATEGORY("Nodes");
 	OBJ_CATEGORY("Nodes");
@@ -327,6 +329,8 @@ public:
 	void remove_and_skip();
 	void remove_and_skip();
 	int get_index() const;
 	int get_index() const;
 
 
+	Ref<SceneTreeTween> create_tween();
+
 	void print_tree();
 	void print_tree();
 	void print_tree_pretty();
 	void print_tree_pretty();
 
 

+ 51 - 0
scene/main/scene_tree.cpp

@@ -40,6 +40,7 @@
 #include "core/project_settings.h"
 #include "core/project_settings.h"
 #include "main/input_default.h"
 #include "main/input_default.h"
 #include "node.h"
 #include "node.h"
+#include "scene/animation/scene_tree_tween.h"
 #include "scene/debugger/script_debugger_remote.h"
 #include "scene/debugger/script_debugger_remote.h"
 #include "scene/resources/dynamic_font.h"
 #include "scene/resources/dynamic_font.h"
 #include "scene/resources/material.h"
 #include "scene/resources/material.h"
@@ -552,6 +553,9 @@ bool SceneTree::iteration(float p_time) {
 	_notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS);
 	_notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS);
 	_flush_ugc();
 	_flush_ugc();
 	MessageQueue::get_singleton()->flush(); //small little hack
 	MessageQueue::get_singleton()->flush(); //small little hack
+
+	process_tweens(p_time, true);
+
 	flush_transform_notifications();
 	flush_transform_notifications();
 	call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds");
 	call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds");
 	root_lock--;
 	root_lock--;
@@ -644,6 +648,8 @@ bool SceneTree::idle(float p_time) {
 		E = N;
 		E = N;
 	}
 	}
 
 
+	process_tweens(p_time, false);
+
 	flush_transform_notifications(); //additional transforms after timers update
 	flush_transform_notifications(); //additional transforms after timers update
 
 
 	_call_idle_callbacks();
 	_call_idle_callbacks();
@@ -684,6 +690,32 @@ bool SceneTree::idle(float p_time) {
 	return _quit;
 	return _quit;
 }
 }
 
 
+void SceneTree::process_tweens(float p_delta, bool p_physics) {
+	// This methods works similarly to how SceneTreeTimers are handled.
+	List<Ref<SceneTreeTween>>::Element *L = tweens.back();
+
+	for (List<Ref<SceneTreeTween>>::Element *E = tweens.front(); E;) {
+		List<Ref<SceneTreeTween>>::Element *N = E->next();
+		// Don't process if paused or process mode doesn't match.
+		if (!E->get()->can_process(pause) || (p_physics == (E->get()->get_process_mode() == Tween::TWEEN_PROCESS_IDLE))) {
+			if (E == L) {
+				break;
+			}
+			E = N;
+			continue;
+		}
+
+		if (!E->get()->step(p_delta)) {
+			E->get()->clear();
+			tweens.erase(E);
+		}
+		if (E == L) {
+			break;
+		}
+		E = N;
+	}
+}
+
 void SceneTree::finish() {
 void SceneTree::finish() {
 	_flush_delete_queue();
 	_flush_delete_queue();
 
 
@@ -1787,6 +1819,23 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa
 	return stt;
 	return stt;
 }
 }
 
 
+Ref<SceneTreeTween> SceneTree::create_tween() {
+	Ref<SceneTreeTween> tween = memnew(SceneTreeTween(true));
+	tweens.push_back(tween);
+	return tween;
+}
+
+Array SceneTree::get_processed_tweens() {
+	Array ret;
+	ret.resize(tweens.size());
+
+	for (int i = 0; i < tweens.size(); i++) {
+		ret[i] = tweens[i];
+	}
+
+	return ret;
+}
+
 void SceneTree::_network_peer_connected(int p_id) {
 void SceneTree::_network_peer_connected(int p_id) {
 	emit_signal("network_peer_connected", p_id);
 	emit_signal("network_peer_connected", p_id);
 }
 }
@@ -1897,6 +1946,8 @@ void SceneTree::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("is_input_handled"), &SceneTree::is_input_handled);
 	ClassDB::bind_method(D_METHOD("is_input_handled"), &SceneTree::is_input_handled);
 
 
 	ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "pause_mode_process"), &SceneTree::create_timer, DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "pause_mode_process"), &SceneTree::create_timer, DEFVAL(true));
+	ClassDB::bind_method(D_METHOD("create_tween"), &SceneTree::create_tween);
+	ClassDB::bind_method(D_METHOD("get_processed_tweens"), &SceneTree::get_processed_tweens);
 
 
 	ClassDB::bind_method(D_METHOD("get_node_count"), &SceneTree::get_node_count);
 	ClassDB::bind_method(D_METHOD("get_node_count"), &SceneTree::get_node_count);
 	ClassDB::bind_method(D_METHOD("get_frame"), &SceneTree::get_frame);
 	ClassDB::bind_method(D_METHOD("get_frame"), &SceneTree::get_frame);

+ 5 - 0
scene/main/scene_tree.h

@@ -41,6 +41,7 @@
 
 
 class PackedScene;
 class PackedScene;
 class Node;
 class Node;
+class SceneTreeTween;
 class Spatial;
 class Spatial;
 class Viewport;
 class Viewport;
 class Material;
 class Material;
@@ -188,6 +189,7 @@ private:
 	//void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
 	//void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
 
 
 	List<Ref<SceneTreeTimer>> timers;
 	List<Ref<SceneTreeTimer>> timers;
+	List<Ref<SceneTreeTween>> tweens;
 
 
 	///network///
 	///network///
 
 
@@ -208,6 +210,7 @@ private:
 	void node_added(Node *p_node);
 	void node_added(Node *p_node);
 	void node_removed(Node *p_node);
 	void node_removed(Node *p_node);
 	void node_renamed(Node *p_node);
 	void node_renamed(Node *p_node);
+	void process_tweens(float p_delta, bool p_physics_frame);
 
 
 	Group *add_to_group(const StringName &p_group, Node *p_node);
 	Group *add_to_group(const StringName &p_group, Node *p_node);
 	void remove_from_group(const StringName &p_group, Node *p_node);
 	void remove_from_group(const StringName &p_group, Node *p_node);
@@ -387,6 +390,8 @@ public:
 	Error reload_current_scene();
 	Error reload_current_scene();
 
 
 	Ref<SceneTreeTimer> create_timer(float p_delay_sec, bool p_process_pause = true);
 	Ref<SceneTreeTimer> create_timer(float p_delay_sec, bool p_process_pause = true);
+	Ref<SceneTreeTween> create_tween();
+	Array get_processed_tweens();
 
 
 	//used by Main::start, don't use otherwise
 	//used by Main::start, don't use otherwise
 	void add_current_scene(Node *p_current);
 	void add_current_scene(Node *p_current);

+ 7 - 0
scene/register_scene_types.cpp

@@ -78,6 +78,7 @@
 #include "scene/animation/animation_tree.h"
 #include "scene/animation/animation_tree.h"
 #include "scene/animation/animation_tree_player.h"
 #include "scene/animation/animation_tree_player.h"
 #include "scene/animation/root_motion_view.h"
 #include "scene/animation/root_motion_view.h"
+#include "scene/animation/scene_tree_tween.h"
 #include "scene/animation/tween.h"
 #include "scene/animation/tween.h"
 #include "scene/audio/audio_stream_player.h"
 #include "scene/audio/audio_stream_player.h"
 #include "scene/gui/aspect_ratio_container.h"
 #include "scene/gui/aspect_ratio_container.h"
@@ -392,6 +393,12 @@ void register_scene_types() {
 	ClassDB::register_class<Skeleton>();
 	ClassDB::register_class<Skeleton>();
 	ClassDB::register_class<AnimationPlayer>();
 	ClassDB::register_class<AnimationPlayer>();
 	ClassDB::register_class<Tween>();
 	ClassDB::register_class<Tween>();
+	ClassDB::register_class<SceneTreeTween>();
+	ClassDB::register_virtual_class<Tweener>();
+	ClassDB::register_class<PropertyTweener>();
+	ClassDB::register_class<IntervalTweener>();
+	ClassDB::register_class<CallbackTweener>();
+	ClassDB::register_class<MethodTweener>();
 
 
 	ClassDB::register_class<AnimationTreePlayer>();
 	ClassDB::register_class<AnimationTreePlayer>();
 	ClassDB::register_class<AnimationTree>();
 	ClassDB::register_class<AnimationTree>();

+ 2 - 0
scene/scene_string_names.cpp

@@ -59,6 +59,8 @@ SceneStringNames::SceneStringNames() {
 	sleeping_state_changed = StaticCString::create("sleeping_state_changed");
 	sleeping_state_changed = StaticCString::create("sleeping_state_changed");
 
 
 	finished = StaticCString::create("finished");
 	finished = StaticCString::create("finished");
+	loop_finished = StaticCString::create("loop_finished");
+	step_finished = StaticCString::create("step_finished");
 	emission_finished = StaticCString::create("emission_finished");
 	emission_finished = StaticCString::create("emission_finished");
 	animation_finished = StaticCString::create("animation_finished");
 	animation_finished = StaticCString::create("animation_finished");
 	animation_changed = StaticCString::create("animation_changed");
 	animation_changed = StaticCString::create("animation_changed");

+ 2 - 0
scene/scene_string_names.h

@@ -91,6 +91,8 @@ public:
 	StringName sort_children;
 	StringName sort_children;
 
 
 	StringName finished;
 	StringName finished;
+	StringName loop_finished;
+	StringName step_finished;
 	StringName emission_finished;
 	StringName emission_finished;
 	StringName animation_finished;
 	StringName animation_finished;
 	StringName animation_changed;
 	StringName animation_changed;

+ 0 - 4
thirdparty/README.md

@@ -342,10 +342,6 @@ Collection of single-file libraries used in Godot components.
   * Upstream: https://sourceforge.net/projects/polyclipping
   * Upstream: https://sourceforge.net/projects/polyclipping
   * Version: 6.4.2 (2017) + Godot changes (added optional exceptions handling)
   * Version: 6.4.2 (2017) + Godot changes (added optional exceptions handling)
   * License: BSL-1.0
   * License: BSL-1.0
-- `easing_equations.cpp`
-  * Upstream: http://robertpenner.com/easing/ via https://github.com/jesusgollonet/ofpennereasing (modified to fit Godot types)
-  * Version: git (af72c147c3a74e7e872aa28c7e2abfcced04fdce, 2008) + Godot types and style changes
-  * License: BSD-3-Clause
 - `fastlz.{c,h}`
 - `fastlz.{c,h}`
   * Upstream: https://github.com/ariya/FastLZ
   * Upstream: https://github.com/ariya/FastLZ
   * Version: 0.5.0 (4f20f54d46f5a6dd4fae4def134933369b7602d2, 2020)
   * Version: 0.5.0 (4f20f54d46f5a6dd4fae4def134933369b7602d2, 2020)

+ 0 - 323
thirdparty/misc/easing_equations.cpp

@@ -1,323 +0,0 @@
-/**
- *  Adapted from Penner Easing equations' C++ port.
- *  Source: https://github.com/jesusgollonet/ofpennereasing
- *  License: BSD-3-clause
- */
-
-#include "scene/animation/tween.h"
-
-const real_t pi = 3.1415926535898;
-
-///////////////////////////////////////////////////////////////////////////
-// linear
-///////////////////////////////////////////////////////////////////////////
-namespace linear {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	return c * t / d + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	return c * t / d + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	return c * t / d + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return c * t / d + b;
-}
-}; // namespace linear
-///////////////////////////////////////////////////////////////////////////
-// sine
-///////////////////////////////////////////////////////////////////////////
-namespace sine {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	return -c * cos(t / d * (pi / 2)) + c + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	return c * sin(t / d * (pi / 2)) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	return -c / 2 * (cos(pi * t / d) - 1) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace sine
-///////////////////////////////////////////////////////////////////////////
-// quint
-///////////////////////////////////////////////////////////////////////////
-namespace quint {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	return c * pow(t / d, 5) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	return c * (pow(t / d - 1, 5) + 1) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	t = t / d * 2;
-	if (t < 1) return c / 2 * pow(t, 5) + b;
-	return c / 2 * (pow(t - 2, 5) + 2) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace quint
-///////////////////////////////////////////////////////////////////////////
-// quart
-///////////////////////////////////////////////////////////////////////////
-namespace quart {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	return c * pow(t / d, 4) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	return -c * (pow(t / d - 1, 4) - 1) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	t = t / d * 2;
-	if (t < 1) return c / 2 * pow(t, 4) + b;
-	return -c / 2 * (pow(t - 2, 4) - 2) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace quart
-///////////////////////////////////////////////////////////////////////////
-// quad
-///////////////////////////////////////////////////////////////////////////
-namespace quad {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	return c * pow(t / d, 2) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	t = t / d;
-	return -c * t * (t - 2) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	t = t / d * 2;
-	if (t < 1) return c / 2 * pow(t, 2) + b;
-	return -c / 2 * ((t - 1) * (t - 3) - 1) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace quad
-///////////////////////////////////////////////////////////////////////////
-// expo
-///////////////////////////////////////////////////////////////////////////
-namespace expo {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	if (t == 0) return b;
-	return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	if (t == d) return b + c;
-	return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	if (t == 0) return b;
-	if (t == d) return b + c;
-	t = t / d * 2;
-	if (t < 1) return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005;
-	return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace expo
-///////////////////////////////////////////////////////////////////////////
-// elastic
-///////////////////////////////////////////////////////////////////////////
-namespace elastic {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	if (t == 0) return b;
-	if ((t /= d) == 1) return b + c;
-	float p = d * 0.3f;
-	float a = c;
-	float s = p / 4;
-	float postFix = a * pow(2, 10 * (t -= 1)); // this is a fix, again, with post-increment operators
-	return -(postFix * sin((t * d - s) * (2 * pi) / p)) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	if (t == 0) return b;
-	if ((t /= d) == 1) return b + c;
-	float p = d * 0.3f;
-	float a = c;
-	float s = p / 4;
-	return (a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b);
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	if (t == 0) return b;
-	if ((t /= d / 2) == 2) return b + c;
-	float p = d * (0.3f * 1.5f);
-	float a = c;
-	float s = p / 4;
-
-	if (t < 1) {
-		float postFix = a * pow(2, 10 * (t -= 1)); // postIncrement is evil
-		return -0.5f * (postFix * sin((t * d - s) * (2 * pi) / p)) + b;
-	}
-	float postFix = a * pow(2, -10 * (t -= 1)); // postIncrement is evil
-	return postFix * sin((t * d - s) * (2 * pi) / p) * 0.5f + c + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace elastic
-///////////////////////////////////////////////////////////////////////////
-// cubic
-///////////////////////////////////////////////////////////////////////////
-namespace cubic {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	t /= d;
-	return c * t * t * t + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	t = t / d - 1;
-	return c * (t * t * t + 1) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	t /= d / 2;
-	if (t < 1) return c / 2 * t * t * t + b;
-	t -= 2;
-	return c / 2 * (t * t * t + 2) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace cubic
-///////////////////////////////////////////////////////////////////////////
-// circ
-///////////////////////////////////////////////////////////////////////////
-namespace circ {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	t /= d;
-	return -c * (sqrt(1 - t * t) - 1) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	t = t / d - 1;
-	return c * sqrt(1 - t * t) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	t /= d / 2;
-	if (t < 1) {
-		return -c / 2 * (sqrt(1 - t * t) - 1) + b;
-	}
-	t -= 2;
-	return c / 2 * (sqrt(1 - t * t) + 1) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace circ
-///////////////////////////////////////////////////////////////////////////
-// bounce
-///////////////////////////////////////////////////////////////////////////
-namespace bounce {
-static real_t out(real_t t, real_t b, real_t c, real_t d);
-
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	return c - out(d - t, 0, c, d) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	if ((t /= d) < (1 / 2.75f)) {
-		return c * (7.5625f * t * t) + b;
-	} else if (t < (2 / 2.75f)) {
-		float postFix = t -= (1.5f / 2.75f);
-		return c * (7.5625f * (postFix)*t + .75f) + b;
-	} else if (t < (2.5 / 2.75)) {
-		float postFix = t -= (2.25f / 2.75f);
-		return c * (7.5625f * (postFix)*t + .9375f) + b;
-	} else {
-		float postFix = t -= (2.625f / 2.75f);
-		return c * (7.5625f * (postFix)*t + .984375f) + b;
-	}
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? in(t * 2, b, c / 2, d) : out((t * 2) - d, b + c / 2, c / 2, d);
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace bounce
-///////////////////////////////////////////////////////////////////////////
-// back
-///////////////////////////////////////////////////////////////////////////
-namespace back {
-static real_t in(real_t t, real_t b, real_t c, real_t d) {
-	float s = 1.70158f;
-	float postFix = t /= d;
-	return c * (postFix)*t * ((s + 1) * t - s) + b;
-}
-
-static real_t out(real_t t, real_t b, real_t c, real_t d) {
-	float s = 1.70158f;
-	t = t / d - 1;
-	return c * (t * t * ((s + 1) * t + s) + 1) + b;
-}
-
-static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
-	float s = 1.70158f * 1.525f;
-	t /= d / 2;
-	if (t < 1) return c / 2 * (t * t * ((s + 1) * t - s)) + b;
-	t -= 2;
-	return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b;
-}
-
-static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
-	return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
-}
-}; // namespace back
-
-Tween::interpolater Tween::interpolaters[Tween::TRANS_COUNT][Tween::EASE_COUNT] = {
-	{ &linear::in, &linear::out, &linear::in_out, &linear::out_in },
-	{ &sine::in, &sine::out, &sine::in_out, &sine::out_in },
-	{ &quint::in, &quint::out, &quint::in_out, &quint::out_in },
-	{ &quart::in, &quart::out, &quart::in_out, &quart::out_in },
-	{ &quad::in, &quad::out, &quad::in_out, &quad::out_in },
-	{ &expo::in, &expo::out, &expo::in_out, &expo::out_in },
-	{ &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in },
-	{ &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in },
-	{ &circ::in, &circ::out, &circ::in_out, &circ::out_in },
-	{ &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in },
-	{ &back::in, &back::out, &back::in_out, &back::out_in },
-};
-
-real_t Tween::_run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d) {
-	if (d == 0) {
-		// Special case to avoid dividing by 0 in equations.
-		return b + c;
-	}
-
-	interpolater cb = interpolaters[p_trans_type][p_ease_type];
-	ERR_FAIL_COND_V(cb == NULL, b);
-	return cb(t, b, c, d);
-}