Follow and limits

A Camera2D with position smoothing and the four limit properties. The yellow cross is the camera center, so you can watch it stop at the border while the player keeps walking.

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

Godot classes used

Camera2DLine2DPolygon2D

The code

scripts/main.gd
extends Node

const MAP: Vector2 = Vector2(2600, 1500)
const SPEED: float = 520.0

var player: Node2D
var camera: Camera2D
var crosshair: Node2D
var readout: Label
var angle: float = PI
var manual: bool = false


func _ready() -> void:
	_add_background()
	_add_map()

	player = _make_square(52.0, Color('#8eef97'))
	player.position = _orbit_point()
	add_child(player)

	camera = Camera2D.new()
	camera.position_smoothing_enabled = true
	camera.position_smoothing_speed = 4.0
	camera.limit_left = 0
	camera.limit_top = 0
	camera.limit_right = int(MAP.x)
	camera.limit_bottom = int(MAP.y)
	camera.limit_smoothed = true
	player.add_child(camera)
	camera.make_current()

	crosshair = _make_crosshair()
	add_child(crosshair)

	_add_hud()

	print('the green square walks the map on its own, arrows drive it, the camera stops at the border')


func _process(delta: float) -> void:
	var drive: Vector2 = Vector2(
		float(Input.is_key_pressed(KEY_RIGHT)) - float(Input.is_key_pressed(KEY_LEFT)),
		float(Input.is_key_pressed(KEY_DOWN)) - float(Input.is_key_pressed(KEY_UP))
	)

	if drive != Vector2.ZERO:
		manual = true

	if manual:
		player.position += drive.normalized() * SPEED * delta
		player.position = player.position.clamp(Vector2(60, 60), MAP - Vector2(60, 60))
	else:
		angle += delta * 0.6
		player.position = _orbit_point()

	var center: Vector2 = camera.get_screen_center_position()
	crosshair.position = center
	readout.text = 'player %d, %d      camera %d, %d' % [player.position.x, player.position.y, center.x, center.y]


func _orbit_point() -> Vector2:
	return MAP * 0.5 + Vector2(cos(angle) * MAP.x * 0.46, sin(angle) * MAP.y * 0.44)


func _add_map() -> void:
	var floor_rect: Polygon2D = Polygon2D.new()
	floor_rect.polygon = PackedVector2Array([Vector2.ZERO, Vector2(MAP.x, 0), MAP, Vector2(0, MAP.y)])
	floor_rect.color = Color('#12141a').lerp(Color('#2a2f3d'), 0.55)
	add_child(floor_rect)

	for x in range(200, int(MAP.x), 200):
		_add_line(Vector2(x, 0), Vector2(x, MAP.y), 2.0, Color('#2a2f3d'))

	for y in range(200, int(MAP.y), 200):
		_add_line(Vector2(0, y), Vector2(MAP.x, y), 2.0, Color('#2a2f3d'))

	var rng: RandomNumberGenerator = RandomNumberGenerator.new()
	rng.seed = 9

	for row in 4:
		for column in 6:
			var block: Node2D = _make_square(rng.randf_range(70, 140), Color('#8da5f3').lerp(Color('#12141a'), 0.35))
			block.position = Vector2(300 + column * 400, 260 + row * 340) + Vector2(rng.randf_range(-80, 80), rng.randf_range(-80, 80))
			add_child(block)

	var border: Line2D = Line2D.new()
	border.points = PackedVector2Array([Vector2(8, 8), Vector2(MAP.x - 8, 8), MAP - Vector2(8, 8), Vector2(8, MAP.y - 8), Vector2(8, 8)])
	border.width = 16.0
	border.default_color = Color('#fc7f7f')
	add_child(border)


func _add_line(from: Vector2, to: Vector2, width: float, color: Color) -> void:
	var line: Line2D = Line2D.new()
	line.points = PackedVector2Array([from, to])
	line.width = width
	line.default_color = color
	add_child(line)


func _make_square(size: float, color: Color) -> Node2D:
	var square: Polygon2D = Polygon2D.new()
	square.polygon = PackedVector2Array([Vector2(-size, -size) * 0.5, Vector2(size, -size) * 0.5, Vector2(size, size) * 0.5, Vector2(-size, size) * 0.5])
	square.color = color

	return square


func _make_crosshair() -> Node2D:
	var mark: Node2D = Node2D.new()

	for points in [PackedVector2Array([Vector2(-34, 0), Vector2(34, 0)]), PackedVector2Array([Vector2(0, -34), Vector2(0, 34)])]:
		var line: Line2D = Line2D.new()
		line.points = points
		line.width = 4.0
		line.default_color = Color('#f3d48d')
		mark.add_child(line)

	return mark


func _add_hud() -> void:
	var hud: CanvasLayer = CanvasLayer.new()
	add_child(hud)

	var title: Label = Label.new()
	title.text = 'green square: player          yellow cross: camera center'
	title.add_theme_font_size_override('font_size', 20)
	title.add_theme_color_override('font_color', Color('#8da5f3'))
	title.position = Vector2(40, 34)
	hud.add_child(title)

	readout = Label.new()
	readout.add_theme_font_size_override('font_size', 20)
	readout.add_theme_color_override('font_color', Color('#f3d48d'))
	readout.position = Vector2(40, 64)
	hud.add_child(readout)


func _add_background() -> void:
	var layer: CanvasLayer = CanvasLayer.new()
	layer.layer = -1
	add_child(layer)

	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	layer.add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

More Camera examples