Backport the new Tween system as SceneTreeTween
Co-authored-by: Tomasz Chabora <kobewi4e@gmail.com>
This commit is contained in:
parent
036f36b5f1
commit
153dc4d57e
|
@ -96,6 +96,13 @@ Copyright: 1997-2017, Sam Lantinga
|
|||
2014-2022, Godot Engine contributors.
|
||||
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
|
||||
Comment: Open Dynamics Engine
|
||||
Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh
|
||||
|
@ -273,11 +280,6 @@ Comment: Clipper
|
|||
Copyright: 2010-2017, Angus Johnson
|
||||
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
|
||||
./thirdparty/misc/fastlz.h
|
||||
Comment: FastLZ
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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.
|
||||
</description>
|
||||
</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">
|
||||
<return type="Node" />
|
||||
<argument index="0" name="flags" type="int" default="15" />
|
||||
|
|
|
@ -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>
|
|
@ -73,6 +73,12 @@
|
|||
The timer will be automatically freed after its time elapses.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_tween">
|
||||
<return type="SceneTreeTween" />
|
||||
<description>
|
||||
Creates and returns a new [SceneTreeTween].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_frame" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
|
@ -104,6 +110,12 @@
|
|||
Returns a list of all nodes assigned to the given group.
|
||||
</description>
|
||||
</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">
|
||||
<return type="int" />
|
||||
<description>
|
||||
|
|
|
@ -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>
|
|
@ -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.
|
||||
[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] For an alternative method of tweening, that doesn't require using nodes, see [SceneTreeTween].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
|
|
@ -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>
|
|
@ -2,23 +2,9 @@
|
|||
|
||||
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
|
||||
|
||||
scene_obj = []
|
||||
|
||||
env.add_source_files(scene_obj, "*.cpp")
|
||||
env.scene_sources += scene_obj
|
||||
|
||||
# Needed to force rebuilding the scene files when the thirdparty code is updated.
|
||||
env.Depends(scene_obj, thirdparty_obj)
|
||||
|
|
|
@ -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
|
|
@ -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(¤t_val);
|
||||
} else {
|
||||
bind_mem.resize(1 + binds.size());
|
||||
|
||||
bind_mem.write[0] = ¤t_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.");
|
||||
}
|
|
@ -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
|
|
@ -31,6 +31,32 @@
|
|||
#include "tween.h"
|
||||
|
||||
#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) {
|
||||
// Add a new pending command and reference it
|
||||
|
@ -444,23 +470,23 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
|
|||
Variant result;
|
||||
|
||||
#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?
|
||||
switch (initial_val.get_type()) {
|
||||
case Variant::BOOL:
|
||||
// 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;
|
||||
|
||||
case Variant::INT:
|
||||
// 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;
|
||||
|
||||
case Variant::REAL:
|
||||
// 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;
|
||||
|
||||
case Variant::VECTOR2: {
|
||||
|
|
|
@ -130,7 +130,6 @@ private:
|
|||
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
|
||||
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_initial_val(const InterpolateData &p_data) const;
|
||||
Variant _get_final_val(const InterpolateData &p_data) const;
|
||||
|
@ -152,6 +151,8 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
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;
|
||||
void set_active(bool p_active);
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "core/message_queue.h"
|
||||
#include "core/print_string.h"
|
||||
#include "instance_placeholder.h"
|
||||
#include "scene/animation/scene_tree_tween.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/scene_string_names.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 {
|
||||
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() {
|
||||
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("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("replace_by", "node", "keep_data"), &Node::replace_by, DEFVAL(false));
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
|
||||
class Viewport;
|
||||
class SceneState;
|
||||
class SceneTreeTween;
|
||||
|
||||
class Node : public Object {
|
||||
GDCLASS(Node, Object);
|
||||
OBJ_CATEGORY("Nodes");
|
||||
|
@ -327,6 +329,8 @@ public:
|
|||
void remove_and_skip();
|
||||
int get_index() const;
|
||||
|
||||
Ref<SceneTreeTween> create_tween();
|
||||
|
||||
void print_tree();
|
||||
void print_tree_pretty();
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "core/project_settings.h"
|
||||
#include "main/input_default.h"
|
||||
#include "node.h"
|
||||
#include "scene/animation/scene_tree_tween.h"
|
||||
#include "scene/debugger/script_debugger_remote.h"
|
||||
#include "scene/resources/dynamic_font.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);
|
||||
_flush_ugc();
|
||||
MessageQueue::get_singleton()->flush(); //small little hack
|
||||
|
||||
process_tweens(p_time, true);
|
||||
|
||||
flush_transform_notifications();
|
||||
call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds");
|
||||
root_lock--;
|
||||
|
@ -644,6 +648,8 @@ bool SceneTree::idle(float p_time) {
|
|||
E = N;
|
||||
}
|
||||
|
||||
process_tweens(p_time, false);
|
||||
|
||||
flush_transform_notifications(); //additional transforms after timers update
|
||||
|
||||
_call_idle_callbacks();
|
||||
|
@ -684,6 +690,32 @@ bool SceneTree::idle(float p_time) {
|
|||
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() {
|
||||
_flush_delete_queue();
|
||||
|
||||
|
@ -1787,6 +1819,23 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa
|
|||
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) {
|
||||
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("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_frame"), &SceneTree::get_frame);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
class PackedScene;
|
||||
class Node;
|
||||
class SceneTreeTween;
|
||||
class Spatial;
|
||||
class Viewport;
|
||||
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);
|
||||
|
||||
List<Ref<SceneTreeTimer>> timers;
|
||||
List<Ref<SceneTreeTween>> tweens;
|
||||
|
||||
///network///
|
||||
|
||||
|
@ -208,6 +210,7 @@ private:
|
|||
void node_added(Node *p_node);
|
||||
void node_removed(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);
|
||||
void remove_from_group(const StringName &p_group, Node *p_node);
|
||||
|
@ -387,6 +390,8 @@ public:
|
|||
Error reload_current_scene();
|
||||
|
||||
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
|
||||
void add_current_scene(Node *p_current);
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#include "scene/animation/animation_tree.h"
|
||||
#include "scene/animation/animation_tree_player.h"
|
||||
#include "scene/animation/root_motion_view.h"
|
||||
#include "scene/animation/scene_tree_tween.h"
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/audio/audio_stream_player.h"
|
||||
#include "scene/gui/aspect_ratio_container.h"
|
||||
|
@ -392,6 +393,12 @@ void register_scene_types() {
|
|||
ClassDB::register_class<Skeleton>();
|
||||
ClassDB::register_class<AnimationPlayer>();
|
||||
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<AnimationTree>();
|
||||
|
|
|
@ -59,6 +59,8 @@ SceneStringNames::SceneStringNames() {
|
|||
sleeping_state_changed = StaticCString::create("sleeping_state_changed");
|
||||
|
||||
finished = StaticCString::create("finished");
|
||||
loop_finished = StaticCString::create("loop_finished");
|
||||
step_finished = StaticCString::create("step_finished");
|
||||
emission_finished = StaticCString::create("emission_finished");
|
||||
animation_finished = StaticCString::create("animation_finished");
|
||||
animation_changed = StaticCString::create("animation_changed");
|
||||
|
|
|
@ -91,6 +91,8 @@ public:
|
|||
StringName sort_children;
|
||||
|
||||
StringName finished;
|
||||
StringName loop_finished;
|
||||
StringName step_finished;
|
||||
StringName emission_finished;
|
||||
StringName animation_finished;
|
||||
StringName animation_changed;
|
||||
|
|
|
@ -342,10 +342,6 @@ Collection of single-file libraries used in Godot components.
|
|||
* Upstream: https://sourceforge.net/projects/polyclipping
|
||||
* Version: 6.4.2 (2017) + Godot changes (added optional exceptions handling)
|
||||
* 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}`
|
||||
* Upstream: https://github.com/ariya/FastLZ
|
||||
* Version: 0.5.0 (4f20f54d46f5a6dd4fae4def134933369b7602d2, 2020)
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue