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