1098 lines
58 KiB
GDScript
1098 lines
58 KiB
GDScript
@tool
|
|
@icon("res://addons/TweenFX/icon.png")
|
|
## TweenFX — A juicy tween animation library for Godot 4.
|
|
## [br][br]
|
|
## Provides one-shot and looping animations for CanvasItem nodes.
|
|
## Call any animation directly, stop it explicitly, or await its completion.
|
|
## [br][br]
|
|
## All animations are tracked automatically. Use [method stop] and [method stop_all]
|
|
## to interrupt running animations, and [method is_playing] to check their state.
|
|
## [br][br]
|
|
## [b]Basic usage:[/b]
|
|
## [codeblock]
|
|
## TweenFX.shake(node)
|
|
## TweenFX.shake(node, 0.5, 20.0, 8)
|
|
## await TweenFX.shake(node).finished
|
|
## [/codeblock]
|
|
extends Node
|
|
const TweenManager = preload("res://addons/TweenFX/TweenManager.gd")
|
|
|
|
#region ENUM
|
|
## Some animations support [b]PlayState[/b] for interactive use.
|
|
## [b]ENTER[/b] and [b]EXIT[/b] are designed as pairs.[br]
|
|
## [i]Pass the target property explicitly so TweenFX always knows where to go regardless of the node's current state.
|
|
enum PlayState {
|
|
FULL, # play complete animation
|
|
ENTER, # animate to target state and hold
|
|
EXIT # animate back to original
|
|
}
|
|
|
|
enum Animations {
|
|
POP_IN,
|
|
POP_OUT,
|
|
PUNCH_IN,
|
|
PUNCH_OUT,
|
|
FADE_IN,
|
|
FADE_OUT,
|
|
DROP_IN,
|
|
DROP_OUT,
|
|
JUMP_SCARE,
|
|
SPIN,
|
|
SKEW,
|
|
VANISH,
|
|
SHAKE,
|
|
PULSATE,
|
|
JITTER,
|
|
JELLY,
|
|
FLIP,
|
|
HOP,
|
|
BLINK,
|
|
SQUASH,
|
|
STRETCH,
|
|
SNAP,
|
|
COLOR_CYCLE,
|
|
HEARTBEAT,
|
|
SWING,
|
|
CHARGE_UP,
|
|
RICOCHET,
|
|
GLITCH,
|
|
SPOTLIGHT,
|
|
WAVE_DISTORT,
|
|
WIGGLE,
|
|
FLOAT_BOB,
|
|
GLOW_PULSE,
|
|
TWIST,
|
|
ROTATE_HOP,
|
|
EXPLODE,
|
|
BLACK_HOLE,
|
|
MELT,
|
|
TV_SHUTDOWN,
|
|
MAD_HELICO,
|
|
SPIN_BOUNCE,
|
|
IDLE_RUBBER,
|
|
BUBBLE_ASCEND,
|
|
CREEP_OUT,
|
|
RUBBER_BAND,
|
|
FIDGET,
|
|
DEFLATE,
|
|
DRUNK,
|
|
IMPACT_LAND,
|
|
BREATHE,
|
|
SWAY,
|
|
FLICKER,
|
|
CRITICAL_HIT,
|
|
UPGRADE,
|
|
FOLD_IN,
|
|
FOLD_OUT,
|
|
ALARM,
|
|
POINT,
|
|
TADA,
|
|
GHOST,
|
|
ATTRACT,
|
|
ORBIT,
|
|
PRESS,
|
|
PRESS_ROTATE,
|
|
MAGNETIC_PULL,
|
|
HEADSHAKE
|
|
}
|
|
|
|
enum AnimationType {
|
|
ONE_SHOT,
|
|
LOOPING
|
|
}
|
|
|
|
enum NodeRequirement {
|
|
CANVAS_ITEM,
|
|
NODE_2D
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DICT
|
|
const ANIMATION_TYPES: Dictionary = {
|
|
Animations.POP_IN: AnimationType.ONE_SHOT,
|
|
Animations.POP_OUT: AnimationType.ONE_SHOT,
|
|
Animations.PUNCH_IN: AnimationType.ONE_SHOT,
|
|
Animations.PUNCH_OUT: AnimationType.ONE_SHOT,
|
|
Animations.FADE_IN: AnimationType.ONE_SHOT,
|
|
Animations.FADE_OUT: AnimationType.ONE_SHOT,
|
|
Animations.DROP_IN: AnimationType.ONE_SHOT,
|
|
Animations.DROP_OUT: AnimationType.ONE_SHOT,
|
|
Animations.JUMP_SCARE: AnimationType.ONE_SHOT,
|
|
Animations.SPIN: AnimationType.ONE_SHOT,
|
|
Animations.SKEW: AnimationType.ONE_SHOT,
|
|
Animations.VANISH: AnimationType.ONE_SHOT,
|
|
Animations.SHAKE: AnimationType.ONE_SHOT,
|
|
Animations.PULSATE: AnimationType.ONE_SHOT,
|
|
Animations.JITTER: AnimationType.ONE_SHOT,
|
|
Animations.JELLY: AnimationType.ONE_SHOT,
|
|
Animations.FLIP: AnimationType.ONE_SHOT,
|
|
Animations.HOP: AnimationType.ONE_SHOT,
|
|
Animations.BLINK: AnimationType.ONE_SHOT,
|
|
Animations.SQUASH: AnimationType.ONE_SHOT,
|
|
Animations.STRETCH: AnimationType.ONE_SHOT,
|
|
Animations.SNAP: AnimationType.ONE_SHOT,
|
|
Animations.CHARGE_UP: AnimationType.ONE_SHOT,
|
|
Animations.RICOCHET: AnimationType.ONE_SHOT,
|
|
Animations.GLITCH: AnimationType.ONE_SHOT,
|
|
Animations.SPOTLIGHT: AnimationType.ONE_SHOT,
|
|
Animations.TWIST: AnimationType.ONE_SHOT,
|
|
Animations.EXPLODE: AnimationType.ONE_SHOT,
|
|
Animations.BLACK_HOLE: AnimationType.ONE_SHOT,
|
|
Animations.TV_SHUTDOWN: AnimationType.ONE_SHOT,
|
|
Animations.CREEP_OUT: AnimationType.ONE_SHOT,
|
|
Animations.RUBBER_BAND: AnimationType.ONE_SHOT,
|
|
Animations.FIDGET: AnimationType.ONE_SHOT,
|
|
Animations.DEFLATE: AnimationType.ONE_SHOT,
|
|
Animations.DRUNK: AnimationType.ONE_SHOT,
|
|
Animations.IMPACT_LAND: AnimationType.ONE_SHOT,
|
|
Animations.CRITICAL_HIT: AnimationType.ONE_SHOT,
|
|
Animations.UPGRADE: AnimationType.ONE_SHOT,
|
|
Animations.FOLD_IN: AnimationType.ONE_SHOT,
|
|
Animations.FOLD_OUT: AnimationType.ONE_SHOT,
|
|
Animations.POINT: AnimationType.ONE_SHOT,
|
|
Animations.TADA: AnimationType.ONE_SHOT,
|
|
Animations.PRESS: AnimationType.ONE_SHOT,
|
|
Animations.PRESS_ROTATE: AnimationType.ONE_SHOT,
|
|
Animations.MAGNETIC_PULL: AnimationType.ONE_SHOT,
|
|
Animations.HEADSHAKE: AnimationType.ONE_SHOT,
|
|
|
|
Animations.COLOR_CYCLE: AnimationType.LOOPING,
|
|
Animations.HEARTBEAT: AnimationType.LOOPING,
|
|
Animations.SWING: AnimationType.LOOPING,
|
|
Animations.WAVE_DISTORT: AnimationType.LOOPING,
|
|
Animations.WIGGLE: AnimationType.LOOPING,
|
|
Animations.FLOAT_BOB: AnimationType.LOOPING,
|
|
Animations.GLOW_PULSE: AnimationType.LOOPING,
|
|
Animations.ROTATE_HOP: AnimationType.LOOPING,
|
|
Animations.SPIN_BOUNCE: AnimationType.LOOPING,
|
|
Animations.MAD_HELICO: AnimationType.LOOPING,
|
|
Animations.MELT: AnimationType.LOOPING,
|
|
Animations.IDLE_RUBBER: AnimationType.LOOPING,
|
|
Animations.BUBBLE_ASCEND: AnimationType.LOOPING,
|
|
Animations.BREATHE: AnimationType.LOOPING,
|
|
Animations.SWAY: AnimationType.LOOPING,
|
|
Animations.FLICKER: AnimationType.LOOPING,
|
|
Animations.ALARM: AnimationType.LOOPING,
|
|
Animations.GHOST: AnimationType.LOOPING,
|
|
Animations.ATTRACT: AnimationType.LOOPING,
|
|
Animations.ORBIT: AnimationType.LOOPING,
|
|
}
|
|
|
|
const ANIMATION_REQUIREMENTS: Dictionary = {
|
|
Animations.WAVE_DISTORT: NodeRequirement.NODE_2D,
|
|
Animations.SKEW: NodeRequirement.NODE_2D,
|
|
}
|
|
#endregion
|
|
|
|
#region LOGIC
|
|
## Stops a specific animation running on the node.
|
|
func stop(node: CanvasItem, anim: Animations) -> void:
|
|
TweenManager.stop(node, anim)
|
|
|
|
## Stops all animations currently running on the node.
|
|
func stop_all(node: CanvasItem) -> void:
|
|
TweenManager.stop_all(node)
|
|
|
|
## Returns true if the given animation is currently playing on the node.
|
|
func is_playing(node: CanvasItem, anim: Animations) -> bool:
|
|
return TweenManager.is_playing(node, anim)
|
|
|
|
## Returns true if the animation loops indefinitely until stopped.
|
|
func is_looping_type(anim: Animations) -> bool:
|
|
return ANIMATION_TYPES.get(anim, AnimationType.ONE_SHOT) == AnimationType.LOOPING
|
|
|
|
## Returns true if the animation requires a Node2D target instead of CanvasItem.
|
|
func requires_node2d(anim: Animations) -> bool:
|
|
return ANIMATION_REQUIREMENTS.get(anim, NodeRequirement.CANVAS_ITEM) == NodeRequirement.NODE_2D
|
|
#endregion
|
|
|
|
#region TWEEN ANIMATIONS
|
|
|
|
#region Looping
|
|
## Goes through the colors.
|
|
func color_cycle(node: CanvasItem, duration: float = 3.0, saturation: float = 1.0, value: float = 1.0) -> Tween:
|
|
TweenManager.stop(node, Animations.COLOR_CYCLE)
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.set_ease(Tween.EASE_IN_OUT)
|
|
tween.set_trans(Tween.TRANS_SINE)
|
|
var colors = [
|
|
Color.from_hsv(0.0, saturation, value),
|
|
Color.from_hsv(0.17, saturation, value),
|
|
Color.from_hsv(0.33, saturation, value),
|
|
Color.from_hsv(0.5, saturation, value),
|
|
Color.from_hsv(0.67, saturation, value),
|
|
Color.from_hsv(0.83, saturation, value),
|
|
Color.from_hsv(1.0, saturation, value),
|
|
]
|
|
for color in colors:
|
|
tween.tween_property(node, "modulate", color, duration / colors.size())
|
|
TweenManager.track(node, Animations.COLOR_CYCLE, tween)
|
|
return tween
|
|
|
|
## Simulates a heartbeat with two rhythmic pulses.
|
|
func heartbeat(node: CanvasItem, duration: float = 1.0, strength: float = 0.2) -> Tween:
|
|
TweenManager.stop(node, Animations.HEARTBEAT)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "scale", original_scale * (1 + strength), 0.15).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, 0.15).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "scale", original_scale, 0.1)
|
|
tween.tween_property(node, "scale", original_scale * (1 + strength * 0.6), 0.1).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, 0.1).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "scale", original_scale, duration - 0.6)
|
|
TweenManager.track(node, Animations.HEARTBEAT, tween)
|
|
return tween
|
|
|
|
## Rotates the node back and forth like a pendulum.
|
|
func swing(node: CanvasItem, duration: float = 1.0, angle: float = 30.0) -> Tween:
|
|
TweenManager.stop(node, Animations.SWING)
|
|
var original_rotation : float = node.rotation_degrees
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.set_ease(Tween.EASE_IN_OUT)
|
|
tween.set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation + angle, duration * 0.5)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation - angle, duration)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation, duration * 0.5)
|
|
TweenManager.track(node, Animations.SWING, tween)
|
|
return tween
|
|
|
|
## Applies a wave-like distortion effect to the node.
|
|
func wave_distort(node: Node2D, duration: float = 1.0, amplitude: float = 0.1) -> Tween:
|
|
TweenManager.stop(node, Animations.WAVE_DISTORT)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.set_ease(Tween.EASE_IN_OUT)
|
|
tween.set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale:x", original_scale.x * (1 + amplitude), duration * 0.25)
|
|
tween.parallel().tween_property(node, "scale:y", original_scale.y * (1 - amplitude/2), duration * 0.25)
|
|
tween.parallel().tween_property(node, "skew", amplitude/2, duration * 0.25)
|
|
tween.tween_property(node, "scale:x", original_scale.x * (1 - amplitude/2), duration * 0.25)
|
|
tween.parallel().tween_property(node, "scale:y", original_scale.y * (1 + amplitude), duration * 0.25)
|
|
tween.parallel().tween_property(node, "skew", -amplitude/2, duration * 0.25)
|
|
tween.tween_property(node, "scale:x", original_scale.x * (1 - amplitude), duration * 0.25)
|
|
tween.parallel().tween_property(node, "scale:y", original_scale.y * (1 - amplitude/2), duration * 0.25)
|
|
tween.parallel().tween_property(node, "skew", amplitude/2, duration * 0.25)
|
|
tween.tween_property(node, "scale:x", original_scale.x, duration * 0.25)
|
|
tween.parallel().tween_property(node, "scale:y", original_scale.y, duration * 0.25)
|
|
tween.parallel().tween_property(node, "skew", 0.0, duration * 0.25)
|
|
TweenManager.track(node, Animations.WAVE_DISTORT, tween)
|
|
return tween
|
|
|
|
## Slightly rotates the node back and forth.
|
|
func wiggle(node: CanvasItem, duration: float = 1.2) -> Tween:
|
|
TweenManager.stop(node, Animations.WIGGLE)
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "rotation_degrees", 5.0, duration * 0.25).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", -5.0, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", 0.0, duration * 0.25).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.WIGGLE, tween)
|
|
return tween
|
|
|
|
## Makes the node float up and down in a looping motion.
|
|
func float_bob(node: CanvasItem, duration: float = 2.0, height: float = 5.0) -> Tween:
|
|
TweenManager.stop(node, Animations.FLOAT_BOB)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "position:y", original_pos.y - height, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "position:y", original_pos.y + height, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.FLOAT_BOB, tween)
|
|
return tween
|
|
|
|
## Gently pulses the node's scale and opacity in a loop.
|
|
func glow_pulse(node: CanvasItem, duration: float = 1.2, scale_amt: float = 0.05, alpha_amt: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.GLOW_PULSE)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_alpha: float = node.modulate.a
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + scale_amt), duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.parallel().tween_property(node, "modulate:a", original_alpha * (1.0 - alpha_amt), duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.parallel().tween_property(node, "modulate:a", original_alpha, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.GLOW_PULSE, tween)
|
|
return tween
|
|
|
|
## Rotates a bit while doing a mini-hop. Good for idle feedback.
|
|
func rotate_hop(node: CanvasItem, duration: float = 0.4, angle: float = 15.0, height: float = 10.0) -> Tween:
|
|
TweenManager.stop(node, Animations.ROTATE_HOP)
|
|
var start_pos = node.position
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "rotation_degrees", angle, duration * 0.25)
|
|
tween.parallel().tween_property(node, "position:y", start_pos.y - height, duration * 0.25)
|
|
tween.tween_property(node, "rotation_degrees", -angle, duration * 0.25)
|
|
tween.parallel().tween_property(node, "position:y", start_pos.y + height, duration * 0.25)
|
|
tween.tween_property(node, "rotation_degrees", 0, duration * 0.25)
|
|
tween.parallel().tween_property(node, "position:y", start_pos.y, duration * 0.25)
|
|
TweenManager.track(node, Animations.ROTATE_HOP, tween)
|
|
return tween
|
|
|
|
## Pure entropy: spin, random jitter, squash-stretch bounce.
|
|
func spin_bounce(node: CanvasItem, duration: float = 0.6, bounce_scale: float = 0.2, spin_speed: float = 180.0) -> Tween:
|
|
TweenManager.stop(node, Animations.SPIN_BOUNCE)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.parallel().tween_property(node, "rotation_degrees", node.rotation_degrees + spin_speed, duration).set_trans(Tween.TRANS_LINEAR)
|
|
tween.parallel().tween_property(node, "scale", original_scale * Vector2(1.0 + randf_range(-bounce_scale, bounce_scale), 1.0 + randf_range(-bounce_scale, bounce_scale)), duration * 0.5)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5)
|
|
TweenManager.track(node, Animations.SPIN_BOUNCE, tween)
|
|
return tween
|
|
|
|
## Makes the object look like it's trying to fly off in a panic.
|
|
func mad_helico(node: CanvasItem, duration: float = 0.6, spin_speed: float = 1080.0, bob_height: float = 5.0) -> Tween:
|
|
TweenManager.stop(node, Animations.MAD_HELICO)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.parallel().tween_property(node, "rotation_degrees", node.rotation_degrees + spin_speed, duration).set_trans(Tween.TRANS_LINEAR)
|
|
tween.tween_property(node, "position:y", original_pos.y - bob_height, duration * 0.3).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "position:y", original_pos.y + bob_height, duration * 0.3).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "position:y", original_pos.y, duration * 0.3).set_trans(Tween.TRANS_SINE)
|
|
TweenManager.track(node, Animations.MAD_HELICO, tween)
|
|
return tween
|
|
|
|
## Makes objects drip down slowly and squash — like they're melting.
|
|
func melt(node: CanvasItem, duration: float = 2.0, melt_distance: float = 20.0) -> Tween:
|
|
TweenManager.stop(node, Animations.MELT)
|
|
var original_pos: Vector2 = node.position
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.parallel().tween_property(node, "position:y", original_pos.y + melt_distance, duration * 0.5).set_trans(Tween.TRANS_LINEAR)
|
|
tween.parallel().tween_property(node, "scale:y", original_scale.y * 1.3, duration * 0.5)
|
|
tween.tween_property(node, "position:y", original_pos.y, duration * 0.5)
|
|
tween.tween_property(node, "scale:y", original_scale.y, duration * 0.5)
|
|
TweenManager.track(node, Animations.MELT, tween)
|
|
return tween
|
|
|
|
## Gives the object rubbery springy movement.
|
|
func idle_rubber(node: CanvasItem, duration: float = 0.6, strength: float = 0.1) -> Tween:
|
|
TweenManager.stop(node, Animations.IDLE_RUBBER)
|
|
var original_pos: Vector2 = node.position
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "position:x", original_pos.x + 6, duration * 0.3)
|
|
tween.parallel().tween_property(node, "scale", original_scale * Vector2(1.1, 0.9), duration * 0.3)
|
|
tween.tween_property(node, "position:x", original_pos.x - 6, duration * 0.3)
|
|
tween.parallel().tween_property(node, "scale", original_scale * Vector2(0.9, 1.1), duration * 0.3)
|
|
tween.tween_property(node, "position:x", original_pos.x, duration * 0.3)
|
|
tween.parallel().tween_property(node, "scale", original_scale, duration * 0.3)
|
|
TweenManager.track(node, Animations.IDLE_RUBBER, tween)
|
|
return tween
|
|
|
|
## Floats upward with a bit of distortion.
|
|
func bubble_ascend(node: CanvasItem, duration: float = 2.0, height: float = 15.0) -> Tween:
|
|
TweenManager.stop(node, Animations.BUBBLE_ASCEND)
|
|
var original_pos: Vector2 = node.position
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "position:y", original_pos.y - height, duration * 0.6).set_trans(Tween.TRANS_SINE)
|
|
tween.parallel().tween_property(node, "scale:y", original_scale.y * 0.95, duration * 0.3)
|
|
tween.tween_property(node, "position:y", original_pos.y, duration * 0.4).set_trans(Tween.TRANS_SINE)
|
|
tween.parallel().tween_property(node, "scale", original_scale, duration * 0.2)
|
|
TweenManager.track(node, Animations.BUBBLE_ASCEND, tween)
|
|
return tween
|
|
|
|
## Very subtle slow scale pulse. Good for alive/idle state.
|
|
func breathe(node: CanvasItem, duration: float = 3.0, strength: float = 0.1) -> Tween:
|
|
TweenManager.stop(node, Animations.BREATHE)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + strength), duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.BREATHE, tween)
|
|
return tween
|
|
|
|
## Slowly fades to low alpha and back, ethereal feel.
|
|
func ghost(node: CanvasItem, duration: float = 2.0, min_alpha: float = 0.2) -> Tween:
|
|
TweenManager.stop(node, Animations.GHOST)
|
|
var original_alpha: float = node.modulate.a
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "modulate:a", min_alpha, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "modulate:a", original_alpha, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.GHOST, tween)
|
|
return tween
|
|
|
|
## Grows slightly then pulses to draw attention.
|
|
func attract(node: CanvasItem, duration: float = 1.2, strength: float = 0.12) -> Tween:
|
|
TweenManager.stop(node, Animations.ATTRACT)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + strength), duration * 0.3).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + strength * 0.5), duration * 0.2).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + strength), duration * 0.2).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.3).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.ATTRACT, tween)
|
|
return tween
|
|
|
|
## Orbits the node in a circle around its original position.
|
|
func orbit(node: CanvasItem, duration: float = 2.0, radius: float = 30.0, start_angle: float = 0.0) -> Tween:
|
|
TweenManager.stop(node, Animations.ORBIT)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
var steps = 60
|
|
for i in range(steps + 1):
|
|
var angle = start_angle + (float(i) / steps) * TAU
|
|
var offset = Vector2(cos(angle), sin(angle)) * radius
|
|
tween.tween_property(node, "position", original_pos + offset, duration / steps).set_trans(Tween.TRANS_LINEAR)
|
|
TweenManager.track(node, Animations.ORBIT, tween)
|
|
return tween
|
|
|
|
## Gentle organic sway like a plant in wind.
|
|
func sway(node: CanvasItem, duration: float = 2.0, angle: float = 8.0) -> Tween:
|
|
TweenManager.stop(node, Animations.SWAY)
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "rotation_degrees", angle, duration * 0.4).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", -angle * 0.6, duration * 0.4).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", angle * 0.3, duration * 0.1).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", 0.0, duration * 0.1).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.SWAY, tween)
|
|
return tween
|
|
|
|
## Random opacity flicker like a candle or broken light.
|
|
func flicker(node: CanvasItem, duration: float = 0.08, min_alpha: float = 0.4, steps: int = 8) -> Tween:
|
|
TweenManager.stop(node, Animations.FLICKER)
|
|
var original_alpha: float = node.modulate.a
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
for i in range(steps):
|
|
var alpha = randf_range(min_alpha, original_alpha)
|
|
tween.tween_property(node, "modulate:a", alpha, duration * randf_range(0.5, 1.5))
|
|
TweenManager.track(node, Animations.FLICKER, tween)
|
|
return tween
|
|
|
|
## Rapid red flash loop, urgent warning.
|
|
func alarm(node: CanvasItem, duration: float = 0.3, color : Color = Color(2.0, 0.2, 0.2, 1.0)) -> Tween:
|
|
TweenManager.stop(node, Animations.ALARM)
|
|
var original_color: Color = node.modulate
|
|
var tween := node.create_tween()
|
|
tween.set_loops()
|
|
tween.tween_property(node, "modulate", color, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "modulate", original_color, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.ALARM, tween)
|
|
return tween
|
|
#endregion
|
|
|
|
#region One-Shot
|
|
## Object fades into the dark and shrinks away.
|
|
func creep_out(node: CanvasItem, duration: float = 1.0) -> Tween:
|
|
TweenManager.stop(node, Animations.CREEP_OUT)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "modulate", Color(0.2, 0.2, 0.2, 1), duration * 0.5)
|
|
tween.tween_property(node, "scale", original_scale * 0.8, duration * 0.5)
|
|
tween.tween_property(node, "modulate:a", 0.0, duration * 0.3)
|
|
TweenManager.track(node, Animations.CREEP_OUT, tween)
|
|
return tween
|
|
|
|
## Makes the object look like it's being shut down like a cartoon TV.
|
|
func tv_shutdown(node: CanvasItem, duration: float = 0.5) -> Tween:
|
|
TweenManager.stop(node, Animations.TV_SHUTDOWN)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.3, 0.7), duration * 0.25).set_trans(Tween.TRANS_ELASTIC)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(0.7, 1.3), duration * 0.25).set_trans(Tween.TRANS_ELASTIC)
|
|
tween.tween_property(node, "scale", Vector2.ZERO, duration * 0.2).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.TV_SHUTDOWN, tween)
|
|
return tween
|
|
|
|
## Spins and collapses into nothingness.
|
|
func black_hole(node: CanvasItem, duration: float = 0.8) -> Tween:
|
|
TweenManager.stop(node, Animations.BLACK_HOLE)
|
|
var tween := node.create_tween()
|
|
tween.parallel().tween_property(node, "scale", Vector2.ZERO, duration).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_IN)
|
|
tween.parallel().tween_property(node, "modulate:a", 0.0, duration)
|
|
tween.parallel().tween_property(node, "rotation_degrees", node.rotation_degrees + 720, duration)
|
|
TweenManager.track(node, Animations.BLACK_HOLE, tween)
|
|
return tween
|
|
|
|
## Boom!
|
|
func explode(node: CanvasItem, duration: float = 0.4, scale_amt: float = 1.8) -> Tween:
|
|
TweenManager.stop(node, Animations.EXPLODE)
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", Vector2.ONE * scale_amt, duration * 0.3).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "rotation_degrees", randf_range(-10, 10), duration * 0.2)
|
|
tween.tween_property(node, "scale", Vector2.ONE * 0.5, duration * 0.4).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_IN)
|
|
tween.parallel().tween_property(node, "modulate:a", 0.0, duration * 0.4).set_trans(Tween.TRANS_LINEAR)
|
|
TweenManager.track(node, Animations.EXPLODE, tween)
|
|
return tween
|
|
|
|
## Builds up energy visually before a release.
|
|
func charge_up(node: CanvasItem, duration: float = 1.0) -> Tween:
|
|
TweenManager.stop(node, Animations.CHARGE_UP)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_color: Color = node.modulate
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * 0.8, duration * 0.4).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
tween.parallel().tween_property(node, "modulate", Color(1.5, 1.5, 0.5, 1.0), duration * 0.4).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
for i in range(3):
|
|
var pulse_scale = 0.05 * (i + 1)
|
|
tween.tween_property(node, "scale", original_scale * (0.8 + pulse_scale), duration * 0.1)
|
|
tween.tween_property(node, "scale", original_scale * 0.8, duration * 0.1)
|
|
tween.tween_property(node, "scale", original_scale * 1.5, duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate", Color(2.0, 2.0, 1.0, 1.0), duration * 0.2)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.2).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate", original_color, duration * 0.2).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.CHARGE_UP, tween)
|
|
return tween
|
|
|
|
## Shrinks and wobbles like it took a hit.
|
|
func punch_out(node: CanvasItem, duration: float = 0.5, min_scale: float = 0.5) -> Tween:
|
|
TweenManager.stop(node, Animations.PUNCH_OUT)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * min_scale, duration * 0.2).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
for i in range(3):
|
|
var jitter = Vector2(randf_range(-1, 1), randf_range(-1, 1)) * 5.0
|
|
tween.tween_property(node, "position", original_pos + jitter, duration * 0.1)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "position", original_pos, duration * 0.1)
|
|
TweenManager.track(node, Animations.PUNCH_OUT, tween)
|
|
return tween
|
|
|
|
## Bounces around as if hitting walls.
|
|
func ricochet(node: CanvasItem, duration: float = 0.8, strength: float = 30.0, bounces: int = 4) -> Tween:
|
|
TweenManager.stop(node, Animations.RICOCHET)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
for i in range(bounces):
|
|
var random_dir = Vector2(randf_range(-1, 1), randf_range(-1, 1)).normalized()
|
|
var target_pos = original_pos + random_dir * strength
|
|
tween.tween_property(node, "position", target_pos, (duration / bounces) * 0.8).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "position", original_pos, duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.RICOCHET, tween)
|
|
return tween
|
|
|
|
## Simulates a digital glitch with color and position jitter.
|
|
func glitch(node: CanvasItem, duration: float = 1.0, intensity: float = 10.0, frames: int = 12) -> Tween:
|
|
TweenManager.stop(node, Animations.GLITCH)
|
|
var original_pos: Vector2 = node.position
|
|
var original_color: Color = node.modulate
|
|
var tween := node.create_tween()
|
|
for i in range(frames):
|
|
var offset = Vector2(randf_range(-1, 1), randf_range(-1, 1)) * intensity
|
|
var color_shift = Color(randf_range(0.8, 1.2), randf_range(0.8, 1.2), randf_range(0.8, 1.2), 1.0)
|
|
var glitch_time = duration / frames * 0.5
|
|
tween.tween_property(node, "position", original_pos + offset, glitch_time)
|
|
tween.parallel().tween_property(node, "modulate", color_shift, glitch_time)
|
|
tween.tween_property(node, "position", original_pos, glitch_time)
|
|
tween.parallel().tween_property(node, "modulate", original_color, glitch_time)
|
|
tween.tween_property(node, "position", original_pos, 0.1)
|
|
tween.parallel().tween_property(node, "modulate", original_color, 0.1)
|
|
TweenManager.track(node, Animations.GLITCH, tween)
|
|
return tween
|
|
|
|
## A fast zoom and shake for a startling impact.
|
|
func jump_scare(node: CanvasItem, duration: float = 0.4, intensity: float = 1.3) -> Tween:
|
|
TweenManager.stop(node, Animations.JUMP_SCARE)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * intensity, duration * 0.3).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
for i in range(6):
|
|
var shake_offset = Vector2(randf_range(-1, 1), randf_range(-1, 1)).normalized() * 10.0
|
|
tween.parallel().tween_property(node, "position", original_pos + shake_offset, duration * 0.05)
|
|
tween.tween_property(node, "position", original_pos, duration * 0.05)
|
|
tween.parallel().tween_property(node, "scale", original_scale, duration * 0.7).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.JUMP_SCARE, tween)
|
|
return tween
|
|
|
|
## Highlights the node with a glowing effect. Supports PlayState for hold/release.
|
|
func spotlight(node: CanvasItem, duration: float = 1.0, glow: Color = Color(1.5, 1.5, 1.5, 1.0), state: PlayState = PlayState.FULL, use_self_modulate : bool = false) -> Tween:
|
|
TweenManager.stop(node, Animations.SPOTLIGHT)
|
|
var original_color: Color = node.modulate if not use_self_modulate else node.self_modulate
|
|
var node_modulate_property: String = "modulate" if not use_self_modulate else "self_modulate"
|
|
var tween := node.create_tween()
|
|
match state:
|
|
PlayState.ENTER:
|
|
tween.tween_property(node, node_modulate_property, glow, duration * 0.3).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
PlayState.EXIT:
|
|
tween.tween_property(node, node_modulate_property, glow, duration * 0.3).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
PlayState.FULL:
|
|
tween.tween_property(node, node_modulate_property, glow, duration * 0.3).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, node_modulate_property, glow, duration * 0.4)
|
|
tween.tween_property(node, node_modulate_property, original_color, duration * 0.3).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.SPOTLIGHT, tween)
|
|
return tween
|
|
|
|
## Flips the node along the given axis.
|
|
func flip(node: CanvasItem, duration: float = 0.4, axis: String = "x", flips: int = 1) -> Tween:
|
|
TweenManager.stop(node, Animations.FLIP)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
for i in range(flips):
|
|
tween.tween_property(node, "scale:" + axis, -original_scale[axis], duration * 0.5 / flips).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "scale:" + axis, original_scale[axis], duration * 0.5 / flips).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.FLIP, tween)
|
|
return tween
|
|
|
|
## Makes the node hop in a given direction and land back.
|
|
func hop(node: CanvasItem, duration: float = 0.4, height: float = 20.0, direction: Vector2 = Vector2.UP) -> Tween:
|
|
TweenManager.stop(node, Animations.HOP)
|
|
var original_pos: Vector2 = node.position
|
|
var offset = direction.normalized() * height
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "position", original_pos + offset, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "position", original_pos, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.HOP, tween)
|
|
return tween
|
|
|
|
## Rapidly blinks the node's opacity.
|
|
func blink(node: CanvasItem, duration: float = 0.1, times: int = 3, from: float = 0.0, to: float = 1.0) -> Tween:
|
|
TweenManager.stop(node, Animations.BLINK)
|
|
var tween := node.create_tween()
|
|
for i in range(times):
|
|
tween.tween_property(node, "modulate:a", from, duration)
|
|
tween.tween_property(node, "modulate:a", to, duration)
|
|
TweenManager.track(node, Animations.BLINK, tween)
|
|
return tween
|
|
|
|
## Squashes the node along one axis and stretches the other, then returns.
|
|
func squash(node: CanvasItem, duration: float = 0.2, amount: float = 0.3, horizontal: bool = true) -> Tween:
|
|
TweenManager.stop(node, Animations.SQUASH)
|
|
var original_scale: Vector2 = node.scale
|
|
var target = Vector2(original_scale.x * (1 + amount), original_scale.y * (1 - amount)) if horizontal else Vector2(original_scale.x * (1 - amount), original_scale.y * (1 + amount))
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", target, duration * 0.5).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5).set_trans(Tween.TRANS_SINE)
|
|
TweenManager.track(node, Animations.SQUASH, tween)
|
|
return tween
|
|
|
|
## Stretches the node vertically, then returns to original scale.
|
|
func stretch(node: CanvasItem, duration: float = 0.2, amount: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.STRETCH)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", Vector2(original_scale.x * (1 - amount), original_scale.y * (1 + amount)), duration * 0.5).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5).set_trans(Tween.TRANS_SINE)
|
|
TweenManager.track(node, Animations.STRETCH, tween)
|
|
return tween
|
|
|
|
## Scales the node up quickly, then resets. Supports PlayState for hold/release.
|
|
func snap(node: CanvasItem, duration: float = 0.1, scale: Vector2 = Vector2(1.3, 1.3), state: PlayState = PlayState.FULL) -> Tween:
|
|
TweenManager.stop(node, Animations.SNAP)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
match state:
|
|
PlayState.ENTER:
|
|
tween.tween_property(node, "scale", scale, duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
PlayState.EXIT:
|
|
tween.tween_property(node, "scale", scale, duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN)
|
|
PlayState.FULL:
|
|
tween.tween_property(node, "scale", scale, duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.SNAP, tween)
|
|
return tween
|
|
|
|
## Quickly flashes the node's opacity.
|
|
func flash(node: CanvasItem, duration: float = 0.1, flashes: int = 3) -> Tween:
|
|
TweenManager.stop(node, Animations.BLINK)
|
|
var tween := node.create_tween()
|
|
for i in range(flashes):
|
|
tween.tween_property(node, "modulate", Color(1, 1, 1, 0), duration)
|
|
tween.tween_property(node, "modulate", Color(1, 1, 1, 1), duration)
|
|
TweenManager.track(node, Animations.BLINK, tween)
|
|
return tween
|
|
|
|
## Fades the node in from transparent.
|
|
func fade_in(node: CanvasItem, duration: float = 0.5) -> Tween:
|
|
TweenManager.stop(node, Animations.FADE_IN)
|
|
node.modulate.a = 0.0
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "modulate:a", 1.0, duration).set_trans(Tween.TRANS_LINEAR).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.FADE_IN, tween)
|
|
return tween
|
|
|
|
## Fades the node out to transparent.
|
|
func fade_out(node: CanvasItem, duration: float = 0.5) -> Tween:
|
|
TweenManager.stop(node, Animations.FADE_OUT)
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "modulate:a", 0.0, duration).set_trans(Tween.TRANS_LINEAR).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.FADE_OUT, tween)
|
|
return tween
|
|
|
|
## Twists the node in place with a quick rotation.
|
|
func twist(node: CanvasItem, duration: float = 0.4, angle: float = 30.0) -> Tween:
|
|
TweenManager.stop(node, Animations.TWIST)
|
|
var start = node.rotation_degrees
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "rotation_degrees", start + angle, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "rotation_degrees", start, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.TWIST, tween)
|
|
return tween
|
|
|
|
## Expands and contracts the node once.
|
|
func pulsate(node: CanvasItem, duration: float = 0.5, scale_factor: float = 1.2) -> Tween:
|
|
TweenManager.stop(node, Animations.PULSATE)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * scale_factor, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
TweenManager.track(node, Animations.PULSATE, tween)
|
|
return tween
|
|
|
|
## Rapid nervous jitter with rotation and scale micro-variations.
|
|
func jitter(node: CanvasItem, duration: float = 0.5, amount: float = 5.0, times: int = 8) -> Tween:
|
|
TweenManager.stop(node, Animations.JITTER)
|
|
var original_position : Vector2 = node.position
|
|
var original_rotation : float = node.rotation_degrees
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
for i in range(times):
|
|
var offset = Vector2(randf_range(-amount, amount), randf_range(-amount, amount))
|
|
var rot = randf_range(-3.0, 3.0)
|
|
var scl = original_scale * Vector2(randf_range(0.95, 1.05), randf_range(0.95, 1.05))
|
|
tween.tween_property(node, "position", original_position + offset, duration / times)
|
|
tween.parallel().tween_property(node, "rotation_degrees", original_rotation + rot, duration / times)
|
|
tween.parallel().tween_property(node, "scale", scl, duration / times)
|
|
tween.tween_property(node, "position", original_position, 0.05)
|
|
tween.parallel().tween_property(node, "rotation_degrees", original_rotation, 0.05)
|
|
tween.parallel().tween_property(node, "scale", original_scale, 0.05)
|
|
TweenManager.track(node, Animations.JITTER, tween)
|
|
return tween
|
|
|
|
## Alternates between squash and stretch repeatedly, like jelly wobbling.
|
|
func jelly(node: CanvasItem, duration: float = 0.6, amount: float = 0.3, cycles: int = 2) -> Tween:
|
|
TweenManager.stop(node, Animations.JELLY)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
for i in range(cycles):
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.0 + amount, 1.0 - amount), duration * 0.25 / cycles).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.0 - amount, 1.0 + amount), duration * 0.25 / cycles).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.2).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.JELLY, tween)
|
|
return tween
|
|
|
|
## Rotates the node one or more full revolutions.
|
|
func spin(node: CanvasItem, duration: float = 0.5, revolutions: float = 1.0, clockwise: bool = true) -> Tween:
|
|
TweenManager.stop(node, Animations.SPIN)
|
|
var original_rotation : float = node.rotation_degrees
|
|
var direction = 1.0 if clockwise else -1.0
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "rotation_degrees", original_rotation + 360.0 * revolutions * direction, duration).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation, 0.0)
|
|
TweenManager.track(node, Animations.SPIN, tween)
|
|
return tween
|
|
|
|
## Scales in from zero with a slight overshoot.
|
|
func pop_in(node: CanvasItem, duration: float = 0.3, overshoot: float = 0.1) -> Tween:
|
|
TweenManager.stop(node, Animations.POP_IN)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_alpha := node.modulate.a
|
|
node.scale = Vector2.ZERO
|
|
node.modulate.a = 0.0
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + overshoot), duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.33)
|
|
tween.parallel().tween_property(node, "modulate:a", original_alpha, duration * 0.66)
|
|
TweenManager.track(node, Animations.POP_IN, tween)
|
|
return tween
|
|
|
|
## Scales out to zero with a slight pull in before vanishing.
|
|
func pop_out(node: CanvasItem, duration: float = 0.3, overshoot: float = 0.1) -> Tween:
|
|
TweenManager.stop(node, Animations.POP_OUT)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * (1.0 + overshoot), duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", Vector2.ZERO, duration * 0.8).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN)
|
|
tween.parallel().tween_property(node, "modulate:a", 0.0, duration * 0.8)
|
|
TweenManager.track(node, Animations.POP_OUT, tween)
|
|
return tween
|
|
|
|
## Skews the node along X and Y axes temporarily.
|
|
func skew(node: Node2D, duration: float = 0.3, skew_x: float = 0.5, skew_y: float = 0.5) -> Tween:
|
|
TweenManager.stop(node, Animations.SKEW)
|
|
var original_scale: Vector2 = node.scale
|
|
var target_scale := original_scale * Vector2(1.0 + skew_x, 1.0 + skew_y)
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", target_scale, duration).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN_OUT).set_delay(duration)
|
|
TweenManager.track(node, Animations.SKEW, tween)
|
|
return tween
|
|
|
|
## Fades out and scales down the node.
|
|
func vanish(node: CanvasItem, duration: float = 0.4) -> Tween:
|
|
TweenManager.stop(node, Animations.VANISH)
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "modulate:a", 0.0, duration)
|
|
tween.parallel().tween_property(node, "scale", Vector2(0.0, 0.0), duration).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.VANISH, tween)
|
|
return tween
|
|
|
|
## Quickly scales the node in with a slight bounce.
|
|
func punch_in(node: CanvasItem, duration: float = 0.15, strength: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.PUNCH_IN)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * (1 + strength), duration).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.PUNCH_IN, tween)
|
|
return tween
|
|
|
|
## Moves the node back and forth rapidly.
|
|
## axis: Vector2.RIGHT for horizontal only, Vector2.DOWN for vertical only, Vector2.ONE for both.
|
|
func shake(node: CanvasItem, duration: float = 0.3, amount: float = 10.0, shakes: int = 5, axis: Vector2 = Vector2.ONE) -> Tween:
|
|
TweenManager.stop(node, Animations.SHAKE)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
for i in range(shakes):
|
|
var offset = Vector2(
|
|
randf_range(-amount, amount) * axis.x,
|
|
randf_range(-amount, amount) * axis.y
|
|
)
|
|
tween.tween_property(node, "position", original_pos + offset, duration / (shakes * 2))
|
|
tween.tween_property(node, "position", original_pos, duration / (shakes * 2))
|
|
TweenManager.track(node, Animations.SHAKE, tween)
|
|
return tween
|
|
|
|
## Drops the node from above into its position.
|
|
func drop_in(node: CanvasItem, duration: float = 0.5, drop_height: float = 100.0, scale_distort: Vector2 = Vector2(1.2, 0.8)) -> Tween:
|
|
TweenManager.stop(node, Animations.DROP_IN)
|
|
var original_pos: Vector2 = node.position
|
|
var original_scale: Vector2 = node.scale
|
|
var original_alpha: float = node.modulate.a
|
|
node.position = original_pos - Vector2(0, drop_height)
|
|
node.scale = scale_distort
|
|
node.modulate.a = 0.0
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "position", original_pos, duration).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "scale", original_scale, duration * 0.6).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate:a", original_alpha, duration * 0.4)
|
|
TweenManager.track(node, Animations.DROP_IN, tween)
|
|
return tween
|
|
|
|
## Drops the node downward and fades out.
|
|
func drop_out(node: CanvasItem, duration: float = 0.5, drop_height: float = 100.0) -> Tween:
|
|
TweenManager.stop(node, Animations.DROP_OUT)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "position", original_pos + Vector2(0, drop_height), duration).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
tween.parallel().tween_property(node, "modulate:a", 0.0, duration * 0.6)
|
|
TweenManager.track(node, Animations.DROP_OUT, tween)
|
|
return tween
|
|
|
|
## Stretches in one direction then snaps back with overshoot.
|
|
func rubber_band(node: CanvasItem, duration: float = 0.5, strength: float = 0.4) -> Tween:
|
|
TweenManager.stop(node, Animations.RUBBER_BAND)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.0 + strength, 1.0 - strength * 0.5), duration * 0.3).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(0.9, 1.1), duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.05, 0.97), duration * 0.2).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.3).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.RUBBER_BAND, tween)
|
|
return tween
|
|
|
|
## Rapidly cycles random rotations then snaps to original. Good for randomness feedback.
|
|
func fidget(node: CanvasItem, duration: float = 0.8, spins: int = 6) -> Tween:
|
|
TweenManager.stop(node, Animations.FIDGET)
|
|
var original_rotation : float = node.rotation_degrees
|
|
var tween := node.create_tween()
|
|
for i in range(spins):
|
|
var random_rot = randf_range(-25.0, 25.0)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation + random_rot, duration / spins).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation, duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.FIDGET, tween)
|
|
return tween
|
|
|
|
## Slowly shrinks Y while expanding X, like air leaving a balloon.
|
|
func deflate(node: CanvasItem, duration: float = 0.6) -> Tween:
|
|
TweenManager.stop(node, Animations.DEFLATE)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.3, 0.2), duration * 0.6).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.1, 0.15), duration * 0.2).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.4).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.DEFLATE, tween)
|
|
return tween
|
|
|
|
## Slow random wobble on position and rotation. Good for status effects.
|
|
func drunk(node: CanvasItem, duration: float = 0.8) -> Tween:
|
|
TweenManager.stop(node, Animations.DRUNK)
|
|
var original_pos: Vector2 = node.position
|
|
var original_rot = node.rotation_degrees
|
|
var tween := node.create_tween()
|
|
for i in range(4):
|
|
var offset = Vector2(randf_range(-8, 8), randf_range(-5, 5))
|
|
var rot = randf_range(-8, 8)
|
|
tween.tween_property(node, "position", original_pos + offset, duration * 0.25).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.parallel().tween_property(node, "rotation_degrees", original_rot + rot, duration * 0.25).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "position", original_pos, duration * 0.2).set_trans(Tween.TRANS_SINE)
|
|
tween.parallel().tween_property(node, "rotation_degrees", original_rot, duration * 0.2).set_trans(Tween.TRANS_SINE)
|
|
TweenManager.track(node, Animations.DRUNK, tween)
|
|
return tween
|
|
|
|
## Squash on hit, bounce up, then settle. Classic landing feel.
|
|
func impact_land(node: CanvasItem, duration: float = 0.5) -> Tween:
|
|
TweenManager.stop(node, Animations.IMPACT_LAND)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.4, 0.6), duration * 0.15).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(0.85, 1.2), duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "position:y", original_pos.y - 12, duration * 0.2)
|
|
tween.tween_property(node, "scale", original_scale * Vector2(1.05, 0.97), duration * 0.15).set_trans(Tween.TRANS_SINE)
|
|
tween.parallel().tween_property(node, "position:y", original_pos.y, duration * 0.15)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.2).set_trans(Tween.TRANS_SINE)
|
|
TweenManager.track(node, Animations.IMPACT_LAND, tween)
|
|
return tween
|
|
|
|
## Big scale punch with chromatic color shift. Classic critical hit feedback.
|
|
func critical_hit(node: CanvasItem, duration: float = 0.5, color: Color = Color(2.0, 0.3, 0.3, 1.0), scale_amount: float = 1.6) -> Tween:
|
|
TweenManager.stop(node, Animations.CRITICAL_HIT)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_color: Color = node.modulate
|
|
var secondary_color = Color(color.r, clampf(color.g + 0.8, 0.0, 2.0), 0.2, 1.0)
|
|
var tween := node.create_tween()
|
|
if scale_amount != 1.0:
|
|
tween.tween_property(node, "scale", original_scale * scale_amount, duration * 0.1).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate", color, duration * 0.1)
|
|
tween.tween_property(node, "scale", original_scale * 0.85, duration * 0.15).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
tween.parallel().tween_property(node, "modulate", secondary_color, duration * 0.15)
|
|
tween.tween_property(node, "scale", original_scale * scale_amount * 0.69, duration * 0.1).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.3).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate", original_color, duration * 0.3)
|
|
else:
|
|
tween.tween_property(node, "modulate", color, duration * 0.1)
|
|
tween.tween_property(node, "modulate", secondary_color, duration * 0.15)
|
|
tween.tween_property(node, "modulate", original_color, duration * 0.3)
|
|
TweenManager.track(node, Animations.CRITICAL_HIT, tween)
|
|
return tween
|
|
|
|
## Scale up, glow, then settle. Celebratory upgrade feedback.
|
|
func upgrade(node: CanvasItem, duration: float = 0.8, glow: Color = Color(2.0, 1.8, 0.2, 1.0), scale_amount: float = 1.5) -> Tween:
|
|
TweenManager.stop(node, Animations.UPGRADE)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_color: Color = node.modulate
|
|
var secondary_color : Color = Color(glow.r * 0.9, glow.g * 0.9, glow.b + 0.3, 1.0)
|
|
var tween := node.create_tween()
|
|
if scale_amount != 1.0:
|
|
tween.tween_property(node, "scale", original_scale * 0.8, duration * 0.1).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "scale", original_scale * scale_amount, duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate", glow, duration * 0.2)
|
|
tween.tween_property(node, "scale", original_scale * (scale_amount * 0.8), duration * 0.15).set_trans(Tween.TRANS_SINE)
|
|
tween.parallel().tween_property(node, "modulate", secondary_color, duration * 0.15)
|
|
tween.tween_property(node, "scale", original_scale * (scale_amount * 0.9), duration * 0.1).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.3).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "modulate", original_color, duration * 0.3)
|
|
else:
|
|
tween.tween_property(node, "modulate", glow, duration * 0.2)
|
|
tween.tween_property(node, "modulate", secondary_color, duration * 0.15)
|
|
tween.tween_property(node, "modulate", original_color, duration * 0.3)
|
|
TweenManager.track(node, Animations.UPGRADE, tween)
|
|
return tween
|
|
|
|
## Unfolds the node by scaling Y from 0 to full size.
|
|
func fold_in(node: CanvasItem, duration: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.FOLD_IN)
|
|
var original_scale: Vector2 = node.scale
|
|
node.scale.y = 0.0
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale:y", original_scale.y, duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.FOLD_IN, tween)
|
|
return tween
|
|
|
|
## Folds the node away by scaling Y to 0.
|
|
func fold_out(node: CanvasItem, duration: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.FOLD_OUT)
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale:y", 0.0, duration).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN)
|
|
TweenManager.track(node, Animations.FOLD_OUT, tween)
|
|
return tween
|
|
|
|
## Quick nudge in one direction then back, like pointing at something.
|
|
func point(node: CanvasItem, duration: float = 0.5, direction: Vector2 = Vector2.RIGHT, amount: float = 24.0) -> Tween:
|
|
TweenManager.stop(node, Animations.POINT)
|
|
var original_pos: Vector2 = node.position
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "position", original_pos + direction.normalized() * amount, duration * 0.3).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "position", original_pos, duration * 0.7).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.POINT, tween)
|
|
return tween
|
|
|
|
## Scale up, slight rotation, then settle. Celebratory reveal.
|
|
func tada(node: CanvasItem, duration: float = 0.6) -> Tween:
|
|
TweenManager.stop(node, Animations.TADA)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_rotation : float = node.rotation_degrees
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * 0.8, duration * 0.1).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "scale", original_scale * 1.4, duration * 0.2).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.parallel().tween_property(node, "rotation_degrees", original_rotation - 8.0, duration * 0.2)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation + 8.0, duration * 0.15).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation, duration * 0.15).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.3).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.TADA, tween)
|
|
return tween
|
|
|
|
#endregion
|
|
|
|
#region NEW
|
|
## Squash on press, bounce back with overshoot, then settle.
|
|
func press(node: CanvasItem, duration: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.PRESS)
|
|
var original_scale: Vector2 = node.scale
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "scale", original_scale * 0.8, duration * 0.24).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale * 1.1, duration * 0.45).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.30).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.PRESS, tween)
|
|
return tween
|
|
|
|
## Scale + rotation wiggle on press.
|
|
func press_rotate(node: CanvasItem, duration: float = 0.3) -> Tween:
|
|
TweenManager.stop(node, Animations.PRESS_ROTATE)
|
|
var original_scale: Vector2 = node.scale
|
|
var original_rotation : float = node.rotation
|
|
var tween := node.create_tween()
|
|
tween.set_parallel(true)
|
|
tween.tween_property(node, "scale", original_scale * 0.85, duration * 0.33).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
tween.tween_property(node, "scale", original_scale, duration * 0.66).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT).set_delay(duration * 0.33)
|
|
tween.tween_property(node, "rotation", original_rotation + deg_to_rad(5), duration * 0.33).set_trans(Tween.TRANS_SINE)
|
|
tween.tween_property(node, "rotation", original_rotation - deg_to_rad(5), duration * 0.33).set_trans(Tween.TRANS_SINE).set_delay(duration * 0.33)
|
|
tween.tween_property(node, "rotation", original_rotation, duration * 0.33).set_trans(Tween.TRANS_SINE).set_delay(duration * 0.66)
|
|
TweenManager.track(node, Animations.PRESS_ROTATE, tween)
|
|
return tween
|
|
|
|
## Moves toward a target point and snaps back. Magnetic pull feel.
|
|
func magnetic_pull(node: CanvasItem, duration: float = 0.5, target: Vector2 = Vector2.ZERO, strength: float = 0.4) -> Tween:
|
|
TweenManager.stop(node, Animations.MAGNETIC_PULL)
|
|
var original_pos: Vector2 = node.position
|
|
var pull_pos = original_pos.lerp(target, strength)
|
|
var tween := node.create_tween()
|
|
tween.tween_property(node, "position", pull_pos, duration * 0.4).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN)
|
|
tween.tween_property(node, "position", original_pos, duration * 0.6).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.MAGNETIC_PULL, tween)
|
|
return tween
|
|
|
|
## Shake left-right — denial, wrong answer, access refused, refusal/no gesture.
|
|
func headshake(node: CanvasItem, duration: float = 0.5, amount: float = 8.0, times: int = 3) -> Tween:
|
|
TweenManager.stop(node, Animations.HEADSHAKE)
|
|
var original_rotation : float = node.rotation_degrees
|
|
var tween := node.create_tween()
|
|
for i in range(times):
|
|
var dir = 1.0 if i % 2 == 0 else -1.0
|
|
tween.tween_property(node, "rotation_degrees", original_rotation + amount * dir, duration * 0.3 / times).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
tween.tween_property(node, "rotation_degrees", original_rotation, duration * 0.25).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
|
TweenManager.track(node, Animations.HEADSHAKE, tween)
|
|
return tween
|
|
#endregion
|
|
|
|
#endregion
|