缩放与平移

拖动平移,滚轮以指针为中心缩放而不是屏幕中心,这只是在 Camera2D.zoom 之上加两行数学。

上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。

用到的 Godot 类

Camera2DInputEventMouseButtonLine2D

代码

scripts/main.gd
extends Node

const CELL: float = 120.0
const HALF: Vector2 = Vector2(1200, 960)
const MIN_ZOOM: Vector2 = Vector2(0.35, 0.35)
const MAX_ZOOM: Vector2 = Vector2(3.0, 3.0)

var camera: Camera2D
var readout: Label
var elapsed: float = 0.0
var manual: bool = false


func _ready() -> void:
	_add_background()
	_add_grid()
	_add_landmarks()

	camera = Camera2D.new()
	add_child(camera)
	camera.make_current()

	_add_hud()

	print('drag with the left button to pan, use the wheel to zoom at the pointer')


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

	if not manual:
		camera.position = Vector2(sin(elapsed * 0.5) * 520.0, sin(elapsed * 0.31) * 320.0)
		camera.zoom = Vector2.ONE * (0.95 + 0.45 * sin(elapsed * 0.7))

	readout.text = 'zoom %.2f      camera %d, %d' % [camera.zoom.x, camera.position.x, camera.position.y]


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseButton and event.pressed:
		var button: InputEventMouseButton = event as InputEventMouseButton

		if button.button_index == MOUSE_BUTTON_WHEEL_UP:
			_zoom_at(button.position, 1.12)
		elif button.button_index == MOUSE_BUTTON_WHEEL_DOWN:
			_zoom_at(button.position, 1.0 / 1.12)
	elif event is InputEventMouseMotion:
		var motion: InputEventMouseMotion = event as InputEventMouseMotion

		if motion.button_mask & MOUSE_BUTTON_MASK_LEFT:
			manual = true
			camera.position -= motion.relative / camera.zoom


func _zoom_at(screen_point: Vector2, factor: float) -> void:
	manual = true

	var view: Vector2 = get_viewport().get_visible_rect().size
	var before: Vector2 = camera.position + (screen_point - view * 0.5) / camera.zoom

	camera.zoom = (camera.zoom * factor).clamp(MIN_ZOOM, MAX_ZOOM)
	camera.position = before - (screen_point - view * 0.5) / camera.zoom


func _add_grid() -> void:
	var x: float = -HALF.x

	while x <= HALF.x:
		var accent: bool = is_zero_approx(x)
		_add_line(Vector2(x, -HALF.y), Vector2(x, HALF.y), 6.0 if accent else 2.0, Color('#8da5f3') if accent else Color('#2a2f3d'))
		x += CELL

	var y: float = -HALF.y

	while y <= HALF.y:
		var accent: bool = is_zero_approx(y)
		_add_line(Vector2(-HALF.x, y), Vector2(HALF.x, y), 6.0 if accent else 2.0, Color('#8da5f3') if accent else Color('#2a2f3d'))
		y += CELL


func _add_landmarks() -> void:
	var colors: Array[Color] = [Color('#8eef97'), Color('#fc7f7f'), Color('#f3d48d'), Color('#8da5f3')]
	var rng: RandomNumberGenerator = RandomNumberGenerator.new()
	rng.seed = 11

	for row in 8:
		for column in 10:
			var size: float = rng.randf_range(34, 78)

			var block: Polygon2D = Polygon2D.new()
			block.polygon = PackedVector2Array([Vector2(-size, -size) * 0.5, Vector2(size, -size) * 0.5, Vector2(size, size) * 0.5, Vector2(-size, size) * 0.5])
			block.color = colors[(row + column) % colors.size()]
			block.position = Vector2(-1080 + column * 240, -840 + row * 240)
			block.rotation = rng.randf_range(-0.4, 0.4)
			add_child(block)

	var origin: Label = Label.new()
	origin.text = '0, 0'
	origin.add_theme_font_size_override('font_size', 28)
	origin.add_theme_color_override('font_color', Color('#8da5f3'))
	origin.position = Vector2(14, 6)
	add_child(origin)


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 _add_hud() -> void:
	var hud: CanvasLayer = CanvasLayer.new()
	add_child(hud)

	var title: Label = Label.new()
	title.text = 'drag to pan, wheel to zoom at the pointer'
	title.add_theme_font_size_override('font_size', 22)
	title.add_theme_color_override('font_color', Color('#8eef97'))
	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, 66)
	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)
	# ColorRect defaults to MOUSE_FILTER_STOP and would eat the click before _unhandled_input
	background.mouse_filter = Control.MOUSE_FILTER_IGNORE
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

更多相机示例