Motion provides generic, sampleable motion functions for procedural
"juice": continuous idle motion (floating, wobble, breathing) that you read
every frame and add to a node.
Use it where a Tween does not fit: a Tween animates a punctual A → B move,
while Motion gives you a value to sample forever in _process.
It is inspired by the motion functions of 2D BOY's Boy framework (the engine
behind World of Goo): SmoothTransitionFunction and SinFunction2D.
Sample a function each frame with an accumulating time t and add it to a base
value:
var basePosition: Vector2
var t := 0.0
func _ready():
basePosition = position
func _process(delta):
t += delta
position = basePosition + Motion.bob(t, 8.0, 0.5)
scale = Vector2.ONE * Motion.breathe(t, 0.05, 0.8)
rotation = deg_to_rad(Motion.wobbleDeg(t, 4.0, 1.2))frequency is in Hz (cycles per second), phase in radians. Offsetting the
phase per node desynchronizes a crowd of objects so they do not pulse in unison.
Motion.oscillate(t, amplitude, frequency, phase = 0.0) -> float1D sine oscillation around 0, within[-amplitude, amplitude].Motion.oscillate2D(t, amplitude: Vector2, frequency: Vector2, phase = Vector2.ZERO) -> Vector2Independent oscillation per axis (Boy'sSinFunction2D). Combine axes for circular / figure-eight motion.
Motion.bob(t, amplitude, frequency, phase = 0.0) -> Vector2Vertical floating offset (idle bob).Motion.sway(t, amplitude, frequency, phase = 0.0) -> Vector2Horizontal swaying offset.Motion.breathe(t, amplitude, frequency, base = 1.0, phase = 0.0) -> floatPulsing scalar aroundbase(breathing scale):base ± amplitude.Motion.wobbleDeg(t, amplitudeDeg, frequency, phase = 0.0) -> floatRotation wobble in degrees, within[-amplitudeDeg, amplitudeDeg].
-
Motion.smooth(x, x0, x1, y0, y1) -> floatSmooth cosine transition fromy0toy1asxgoes fromx0tox1(Boy'sSmoothTransitionFunction).xis clamped to[x0, x1], so it is a handy ease for mapping any input range to an eased output:# fade an alpha as a value crosses [0, 100] modulate.a = Motion.smooth(distance, 0.0, 100.0, 1.0, 0.0)