Toque e gestos

Arraste um card com um dedo e faça pinça com dois. O mesmo handler cai no botão do mouse e na roda, que é o caminho de um navegador no desktop.

O editor acima está rodando este projeto. Mude uma linha e ele recarrega.

Classes do Godot usadas

InputEventScreenTouchInputEventScreenDragInputEventMouseButtonPolygon2D

O código

scripts/main.gd
extends Node

const MIN_ZOOM: float = 0.4
const MAX_ZOOM: float = 3.2
const WHEEL_STEP: float = 1.12
const CORNERS: Array[Vector2] = [Vector2(1, 1), Vector2(-1, 1), Vector2(-1, -1), Vector2(1, -1)]

var card: Polygon2D
var readout: Label
var markers: Array[Polygon2D] = []
# touch index -> last position, the mouse fallback reuses index 0
var touches: Dictionary = {}
var zoom: float = 1.0
var pinch_span: float = 0.0
var pinch_zoom: float = 1.0


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

	var view: Vector2 = get_viewport().get_visible_rect().size

	card = _make_card(Vector2(120, 90), 26.0, Color('#8da5f3'))
	card.position = view * 0.5
	add_child(card)

	for i in 5:
		var marker: Polygon2D = _make_circle(22.0, Color(0.99, 0.83, 0.55, 0.75))
		marker.visible = false
		add_child(marker)
		markers.append(marker)

	add_child(_panel())

	print('drag the card to move it and pinch with two fingers to scale, the mouse drags and the wheel scales')


func _process(_delta: float) -> void:
	var view: Vector2 = get_viewport().get_visible_rect().size

	card.position = card.position.clamp(Vector2(70, 70), view - Vector2(70, 70))
	readout.text = 'touches %d      scale %.2f      card at %.0f, %.0f' % [touches.size(), zoom, card.position.x, card.position.y]


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventScreenTouch:
		var touch: InputEventScreenTouch = event

		if touch.pressed:
			_begin(touch.index, touch.position)
		else:
			_end(touch.index)
	elif event is InputEventScreenDrag:
		var drag: InputEventScreenDrag = event
		_move(drag.index, drag.position, drag.relative)
	elif event is InputEventMouseButton:
		_mouse_button(event as InputEventMouseButton)
	elif event is InputEventMouseMotion and touches.has(0):
		var motion: InputEventMouseMotion = event
		_move(0, motion.position, motion.relative)


# neither the desktop nor the web export synthesizes screen touches, so this branch is the one that runs there
func _mouse_button(event: InputEventMouseButton) -> void:
	if event.button_index == MOUSE_BUTTON_LEFT:
		if event.pressed:
			_begin(0, event.position)
		else:
			_end(0)
	elif event.pressed and event.button_index == MOUSE_BUTTON_WHEEL_UP:
		_apply_zoom(zoom * WHEEL_STEP)
	elif event.pressed and event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
		_apply_zoom(zoom / WHEEL_STEP)


func _begin(index: int, at: Vector2) -> void:
	touches[index] = at

	if touches.size() == 2:
		pinch_span = _span()
		pinch_zoom = zoom

	_refresh_markers()


func _end(index: int) -> void:
	touches.erase(index)
	pinch_span = 0.0
	_refresh_markers()


func _move(index: int, at: Vector2, relative: Vector2) -> void:
	if not touches.has(index):
		return

	touches[index] = at

	if touches.size() == 1:
		card.position += relative
	elif pinch_span > 1.0:
		_apply_zoom(pinch_zoom * _span() / pinch_span)

	_refresh_markers()


func _apply_zoom(value: float) -> void:
	zoom = clampf(value, MIN_ZOOM, MAX_ZOOM)
	card.scale = Vector2.ONE * zoom


func _span() -> float:
	var points: Array = touches.values()

	return (points[0] as Vector2).distance_to(points[1] as Vector2) if points.size() >= 2 else 0.0


func _refresh_markers() -> void:
	for marker in markers:
		marker.visible = false

	var slot: int = 0

	for index in touches:
		if slot >= markers.size():
			break

		markers[slot].position = touches[index]
		markers[slot].visible = true
		slot += 1


func _panel() -> PanelContainer:
	var style: StyleBoxFlat = StyleBoxFlat.new()
	style.bg_color = Color('#2a2f3d')
	style.set_corner_radius_all(14)
	style.set_content_margin_all(24)

	var panel: PanelContainer = PanelContainer.new()
	panel.position = Vector2(48, 48)
	panel.custom_minimum_size = Vector2(520, 0)
	panel.add_theme_stylebox_override('panel', style)
	panel.mouse_filter = Control.MOUSE_FILTER_IGNORE

	var column: VBoxContainer = VBoxContainer.new()
	column.add_theme_constant_override('separation', 10)
	column.mouse_filter = Control.MOUSE_FILTER_IGNORE
	panel.add_child(column)

	var title: Label = Label.new()
	title.text = 'Touch and gestures'
	title.add_theme_font_size_override('font_size', 22)
	column.add_child(title)

	for line in [
		'drag with one finger to move the card, InputEventScreenDrag',
		'pinch with two fingers to scale it, InputEventScreenTouch',
		'mouse fallback: hold the left button to drag, wheel to scale',
	]:
		var hint: Label = Label.new()
		hint.text = line
		hint.add_theme_color_override('font_color', Color('#9aa3b5'))
		column.add_child(hint)

	readout = Label.new()
	readout.add_theme_color_override('font_color', Color('#8eef97'))
	column.add_child(readout)

	return panel


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

	for i in CORNERS.size():
		var center: Vector2 = CORNERS[i] * (half - Vector2(radius, radius))

		for step in 7:
			var angle: float = TAU * (i + step / 6.0) / 4.0
			points.append(center + Vector2(cos(angle), sin(angle)) * radius)

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

	return shape


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

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

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

	return circle

Mais exemplos de Entrada