Grid snap

A free position moving in a loop and the same position passed through snapped. The blue square only ever sits on a grid node.

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

Godot classes used

Polygon2DLine2DVector2

The code

scripts/main.gd
extends Node

const CELL: float = 80.0
const ORBIT_SPEED: float = 0.55

var free_marker: Polygon2D
var cell_marker: Line2D
var link: Line2D
var center: Vector2
var radius: Vector2
var elapsed: float = 0.0


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size
	center = view * 0.5
	radius = Vector2(view.x * 0.3, view.y * 0.28)

	_add_grid(view)

	cell_marker = _make_cell(Color('#8da5f3'))
	cell_marker.position = center
	add_child(cell_marker)

	link = Line2D.new()
	link.width = 3.0
	link.default_color = Color('#fc7f7f')
	add_child(link)

	free_marker = _make_circle(12.0, Color('#f3d48d'))
	free_marker.position = center
	add_child(free_marker)

	_add_label('the yellow dot moves freely, the blue box is that position through snapped(position, 80)', Vector2(40, 30), Color('#999999'))


func _process(delta: float) -> void:
	elapsed += delta

	var free_at: Vector2 = center + Vector2(cos(elapsed * ORBIT_SPEED), sin(elapsed * ORBIT_SPEED * 1.3)) * radius

	free_marker.position = free_at
	cell_marker.position = free_at.snapped(Vector2(CELL, CELL))
	link.points = PackedVector2Array([free_at, cell_marker.position])


func _add_grid(view: Vector2) -> void:
	var columns: int = int(view.x / CELL)
	var rows: int = int(view.y / CELL)

	for i in columns:
		_add_line(Vector2((i + 0.5) * CELL, 0), Vector2((i + 0.5) * CELL, view.y))

	for i in rows:
		_add_line(Vector2(0, (i + 0.5) * CELL), Vector2(view.x, (i + 0.5) * CELL))


func _add_line(from: Vector2, to: Vector2) -> void:
	var line: Line2D = Line2D.new()
	line.width = 2.0
	line.default_color = Color('#2a2f3d')
	line.points = PackedVector2Array([from, to])
	add_child(line)


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


func _make_cell(color: Color) -> Line2D:
	var half: float = CELL * 0.5

	var cell: Line2D = Line2D.new()
	cell.width = 4.0
	cell.default_color = color
	cell.points = PackedVector2Array(
		[
			Vector2(-half, -half),
			Vector2(half, -half),
			Vector2(half, half),
			Vector2(-half, half),
			Vector2(-half, -half),
		]
	)

	return cell


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

	for i in 24:
		points.append(Vector2(cos(TAU * i / 24.0), sin(TAU * i / 24.0)) * radius_px)

	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