Bob and float

Nine shapes on the same sine wave, each one a phase step ahead of the last. No Tween and no AnimationPlayer.

The editor above is running this project. Change a line and it reloads.

Godot classes used

Node2DLine2DTime

The code

scripts/main.gd
extends Node

const COLUMNS: int = 9
const AMPLITUDE: float = 130.0
const FREQUENCY: float = 1.7
const PHASE_STEP: float = 0.6

var bobs: Array[Node2D] = []
var row: float = 0.0


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size
	row = view.y * 0.55

	for i in COLUMNS:
		var column: float = view.x * (i + 1) / (COLUMNS + 1)

		var rail: Line2D = Line2D.new()
		rail.width = 2.0
		rail.default_color = Color('#2a2f3d')
		rail.points = PackedVector2Array([Vector2(column, row - AMPLITUDE), Vector2(column, row + AMPLITUDE)])
		add_child(rail)

		var bob: Node2D = Node2D.new()
		bob.position = Vector2(column, row)
		bob.add_child(_make_circle(26.0, Color('#8da5f3').lerp(Color('#8eef97'), float(i) / (COLUMNS - 1))))
		add_child(bob)
		bobs.append(bob)

	_add_label('sin(time * frequency + phase), one phase step per column', Vector2(40, 40))


func _process(_delta: float) -> void:
	var time: float = Time.get_ticks_msec() / 1000.0

	for i in bobs.size():
		var wave: float = sin(time * FREQUENCY + i * PHASE_STEP)
		bobs[i].position.y = row + wave * AMPLITUDE
		bobs[i].scale = Vector2.ONE * (1.0 + wave * 0.12)


func _add_label(text: String, at: Vector2) -> void:
	var label: Label = Label.new()
	label.text = text
	label.position = at
	label.add_theme_color_override('font_color', Color('#999999'))
	label.add_theme_font_size_override('font_size', 20)
	add_child(label)


func _make_circle(radius: float, color: Color) -> Polygon2D:
	var points: PackedVector2Array = PackedVector2Array()

	for i in 26:
		points.append(Vector2(cos(TAU * i / 26.0), sin(TAU * i / 26.0)) * radius)

	var circle: Polygon2D = Polygon2D.new()
	circle.polygon = points
	circle.color = color

	return circle


func _add_background() -> void:
	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

More Motion examples