Examples about (multi)touch events

This commit is contained in:
Pedro J. Estébanez
2017-12-07 03:42:48 +01:00
parent 0eec5d704a
commit b6db599fa1
12 changed files with 655 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
[gd_scene load_steps=3 format=1]
[sub_resource type="FixedMaterial" id=1]
flags/visible = true
flags/double_sided = false
flags/invert_faces = false
flags/unshaded = false
flags/on_top = false
flags/lightmap_on_uv2 = true
flags/colarray_is_srgb = true
params/blend_mode = 0
params/depth_draw = 1
params/line_width = 0.0
fixed_flags/use_alpha = false
fixed_flags/use_color_array = false
fixed_flags/use_point_size = false
fixed_flags/discard_alpha = false
fixed_flags/use_xy_normalmap = false
params/diffuse = Color( 0.259476, 0.699219, 0.379718, 1 )
params/specular = Color( 1, 1, 1, 1 )
params/emission = Color( 0, 0, 0, 1 )
params/specular_exp = 40
params/detail_mix = 1.0
params/normal_depth = 1
params/shader = 0
params/shader_param = 0.5
params/glow = 0
params/point_size = 1.0
uv_xform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
textures/diffuse_tc = 0
textures/detail_tc = 0
textures/specular_tc = 0
textures/emission_tc = 0
textures/specular_exp_tc = 0
textures/glow_tc = 0
textures/normal_tc = 0
textures/shade_param_tc = 0
[sub_resource type="Environment" id=2]
ambient_light/enabled = true
ambient_light/color = Color( 0.601563, 0.696429, 1, 1 )
ambient_light/energy = 0.5
fxaa/enabled = true
background/mode = 1
background/color = Color( 0, 0, 0, 1 )
background/energy = 1.0
background/scale = 1.0
background/glow = 0.0
background/canvas_max_layer = null
glow/enabled = false
glow/blur_passes = 1
glow/blur_scale = 1
glow/blur_strength = 1
glow/blur_blend_mode = null
glow/bloom = 0.0
glow/bloom_treshold = 0.5
dof_blur/enabled = false
dof_blur/blur_passes = 1
dof_blur/begin = 100.0
dof_blur/range = 10.0
hdr/enabled = false
hdr/tonemapper = 0
hdr/exposure = 0.4
hdr/white = 1.0
hdr/glow_treshold = 0.95
hdr/glow_scale = 0.2
hdr/min_luminance = 0.4
hdr/max_luminance = 8.0
hdr/exposure_adj_speed = 0.5
fog/enabled = false
fog/begin = 100.0
fog/begin_color = Color( 0, 0, 0, 1 )
fog/end_color = Color( 0, 0, 0, 1 )
fog/attenuation = 1.0
fog/bg = true
bcs/enabled = false
bcs/brightness = 1.0
bcs/contrast = 1.0
bcs/saturation = 1.0
srgb/enabled = false
[node name="Spatial" type="Spatial"]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
[node name="TestCube" type="TestCube" parent="."]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
transform/local = Transform( 0.707107, 0, -0.707107, -0.353553, 0.866025, -0.353553, 0.612372, 0.5, 0.612372, 0, 0, 0 )
layers = 1
geometry/visible = true
geometry/material_override = SubResource( 1 )
geometry/cast_shadow = 1
geometry/receive_shadows = true
geometry/range_begin = 0.0
geometry/range_end = 0.0
geometry/extra_cull_margin = 0.0
geometry/billboard = false
geometry/billboard_y = false
geometry/depth_scale = false
geometry/visible_in_all_rooms = false
geometry/use_baked_light = false
geometry/baked_light_tex_id = 0
[node name="Camera" type="Camera" parent="."]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
transform/local = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 7.09558 )
projection = 0
fov = 50.0
near = 0.1
far = 100.0
keep_aspect = 1
current = false
visible_layers = 1048575
environment = null
h_offset = 0.0
v_offset = 0.0
[node name="DirectionalLight" type="DirectionalLight" parent="."]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
transform/local = Transform( 0.926535, 0.11439, -0.358396, 0.199614, 0.658013, 0.726067, 0.318884, -0.744267, 0.586839, 0, 4.14991, 0 )
layers = 1
params/enabled = true
params/editor_only = false
params/bake_mode = 0
params/energy = 1.0
colors/diffuse = Color( 1, 1, 1, 1 )
colors/specular = Color( 1, 1, 1, 1 )
shadow/shadow = false
shadow/darkening = 0.0
shadow/z_offset = 0.05
shadow/z_slope_scale = 0.0
shadow/esm_multiplier = 60.0
shadow/blur_passes = 1.0
projector = null
operator = 0
shadow/mode = 0
shadow/max_distance = 0.0
shadow/split_weight = 0.5
shadow/zoffset_scale = 2.0
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
environment = SubResource( 2 )

View File

@@ -0,0 +1,133 @@
extends Control
export(NodePath) var target
export var min_scale = 0.1
export var max_scale = 3.0
export var one_finger_rot_x = true
export var one_finger_rot_y = true
export var two_fingers_rot_z = true
export var two_fingers_zoom = true
var base_state
var curr_state
var target_node
# We keep here a copy of the state before the number of fingers changed to avoid accumulation errors
var base_xform
func _ready():
base_state = {}
curr_state = {}
target_node = get_node(target)
set_process_unhandled_input(true)
# Using unhandled input since it's the method that has empirically proven to work the best
func _unhandled_input(event):
# We must start touching inside, but we can drag or unpress outside
if !(event.type == InputEvent.SCREEN_DRAG ||
(event.type == InputEvent.SCREEN_TOUCH && (!event.pressed || get_global_rect().has_point(Vector2(event.x, event.y))))):
return
var finger_count_changed = false
var finger_count = base_state.size()
if finger_count == 0:
# No fingers => Accept press
if event.type == InputEvent.SCREEN_TOUCH:
if event.pressed:
# A finger started touching
base_state = {
event.index: Vector2(event.x, event.y),
}
elif finger_count == 1:
# One finger => For rotating around X and Y
# Accept one more press, unpress or drag
if event.type == InputEvent.SCREEN_TOUCH:
if event.pressed:
# One more finger started touching
# Reset the base state to the only current and the new fingers
base_state = {
curr_state.keys()[0]: curr_state.values()[0],
event.index: Vector2(event.x, event.y),
}
else:
if base_state.has(event.index):
# Only touching finger released
base_state.clear()
elif event.type == InputEvent.SCREEN_DRAG:
if curr_state.has(event.index):
# Touching finger dragged
var unit_drag = _px2unit(base_state[base_state.keys()[0]] - Vector2(event.x, event.y))
if one_finger_rot_x:
target_node.global_rotate(Vector3(0, 1, 0), deg2rad(180.0 * unit_drag.x))
if one_finger_rot_y:
target_node.global_rotate(Vector3(1, 0, 0), deg2rad(180.0 * unit_drag.y))
# Since rotating around two axes, we have to reset the base constantly
curr_state[event.index] = Vector2(event.x, event.y)
base_state[event.index] = Vector2(event.x, event.y)
base_xform = target_node.get_transform()
elif finger_count == 2:
# Two fingers => To pinch-zoom and rotate around Z
# Accept unpress or drag
if event.type == InputEvent.SCREEN_TOUCH:
if !event.pressed && base_state.has(event.index):
# Some known touching finger released
# Remove released finger from the base state
base_state.erase(event.index)
# Reset the base state to the now only toyching finger
base_state = {
curr_state.keys()[0]: curr_state.values()[0],
}
elif event.type == InputEvent.SCREEN_DRAG:
if curr_state.has(event.index):
# Some known touching finger dragged
# Update
curr_state[event.index] = Vector2(event.x, event.y)
# Compute base and current inter-finger vectors
var base_segment = base_state[base_state.keys()[0]] - base_state[base_state.keys()[1]]
var new_segment = curr_state[curr_state.keys()[0]] - curr_state[curr_state.keys()[1]]
# Get the base scale from the base matrix
var base_scale = Vector3(base_xform.basis.x.x, base_xform.basis.y.y, base_xform.basis.z.z).length()
if two_fingers_zoom:
# Compute the new scale limiting it and taking into account the base scale
var new_scale = clamp(base_scale * (new_segment.length() / base_segment.length()), min_scale, max_scale) / base_scale
target_node.set_transform(base_xform.scaled(new_scale * Vector3(1, 1, 1)))
else:
target_node.set_transform(base_xform)
if two_fingers_rot_z:
# Apply rotation between base inter-finger vector and the current one
var rot = new_segment.angle_to(base_segment)
target_node.global_rotate(Vector3(0, 0, 1), rot)
# Finger count changed?
if base_state.size() != finger_count:
# Copy new base state to the current state
curr_state = {}
for idx in base_state.keys():
curr_state[idx] = base_state[idx]
# Remember the base transform
base_xform = target_node.get_transform()
# Converts a vector in pixels to a unitary magnitude,
# considering the number of pixels of the shorter axis is the unit
func _px2unit(v):
var shortest = min(get_size().x, get_size().y)
return v * (1.0 / shortest)

View File

@@ -0,0 +1,256 @@
[gd_scene load_steps=3 format=1]
[ext_resource path="res://GestureArea.gd" type="Script" id=1]
[ext_resource path="res://CubeScene.tscn" type="PackedScene" id=2]
[node name="VBoxContainer" type="VBoxContainer"]
anchor/right = 1
anchor/bottom = 1
focus/ignore_mouse = false
focus/stop_mouse = false
size_flags/horizontal = 2
size_flags/vertical = 2
margin/left = 0.0
margin/top = 0.0
margin/right = 0.0
margin/bottom = 0.0
alignment = 0
[node name="HBoxContainer" type="HBoxContainer" parent="."]
focus/ignore_mouse = false
focus/stop_mouse = false
size_flags/horizontal = 3
size_flags/vertical = 3
margin/left = 0.0
margin/top = 0.0
margin/right = 512.0
margin/bottom = 254.0
alignment = 0
[node name="GestureArea" type="Control" parent="HBoxContainer"]
focus/ignore_mouse = false
focus/stop_mouse = true
size_flags/horizontal = 3
size_flags/vertical = 3
margin/left = 0.0
margin/top = 0.0
margin/right = 254.0
margin/bottom = 254.0
script/script = ExtResource( 1 )
target = NodePath("Viewport/Spatial/TestCube")
min_scale = 0.1
max_scale = 3.0
one_finger_rot_x = true
one_finger_rot_y = false
two_fingers_rot_z = false
two_fingers_zoom = false
[node name="Viewport" type="Viewport" parent="HBoxContainer/GestureArea"]
rect = Rect2( 0, 0, 254, 254 )
own_world = true
world = null
transparent_bg = false
render_target/enabled = false
render_target/v_flip = false
render_target/clear_on_new_frame = true
render_target/filter = false
render_target/gen_mipmaps = false
render_target/update_mode = 2
audio_listener/enable_2d = false
audio_listener/enable_3d = false
physics/object_picking = false
gui/disable_input = false
[node name="Spatial" parent="HBoxContainer/GestureArea/Viewport" instance=ExtResource( 2 )]
[node name="Label" type="Label" parent="HBoxContainer/GestureArea"]
focus/ignore_mouse = true
focus/stop_mouse = true
size_flags/horizontal = 2
size_flags/vertical = 0
margin/left = 7.0
margin/top = 6.0
margin/right = 157.0
margin/bottom = 20.0
text = "One-finger rot around X"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
[node name="GestureArea_1" type="Control" parent="HBoxContainer"]
focus/ignore_mouse = false
focus/stop_mouse = true
size_flags/horizontal = 3
size_flags/vertical = 3
margin/left = 258.0
margin/top = 0.0
margin/right = 512.0
margin/bottom = 254.0
script/script = ExtResource( 1 )
target = NodePath("Viewport/Spatial/TestCube")
min_scale = 0.1
max_scale = 3.0
one_finger_rot_x = true
one_finger_rot_y = true
two_fingers_rot_z = false
two_fingers_zoom = false
[node name="Viewport" type="Viewport" parent="HBoxContainer/GestureArea_1"]
rect = Rect2( 0, 0, 254, 254 )
own_world = true
world = null
transparent_bg = false
render_target/enabled = false
render_target/v_flip = false
render_target/clear_on_new_frame = true
render_target/filter = false
render_target/gen_mipmaps = false
render_target/update_mode = 2
audio_listener/enable_2d = false
audio_listener/enable_3d = false
physics/object_picking = false
gui/disable_input = false
[node name="Spatial" parent="HBoxContainer/GestureArea_1/Viewport" instance=ExtResource( 2 )]
[node name="Label2" type="Label" parent="HBoxContainer/GestureArea_1"]
focus/ignore_mouse = true
focus/stop_mouse = true
size_flags/horizontal = 2
size_flags/vertical = 0
margin/left = 15.0
margin/top = 6.0
margin/right = 55.0
margin/bottom = 20.0
text = "One-finger rot around X and Y"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
[node name="HBoxContainer_1" type="HBoxContainer" parent="."]
focus/ignore_mouse = false
focus/stop_mouse = false
size_flags/horizontal = 3
size_flags/vertical = 3
margin/left = 0.0
margin/top = 258.0
margin/right = 512.0
margin/bottom = 512.0
alignment = 0
[node name="GestureArea" type="Control" parent="HBoxContainer_1"]
focus/ignore_mouse = false
focus/stop_mouse = true
size_flags/horizontal = 3
size_flags/vertical = 3
margin/left = 0.0
margin/top = 0.0
margin/right = 254.0
margin/bottom = 254.0
script/script = ExtResource( 1 )
target = NodePath("Viewport/Spatial/TestCube")
min_scale = 0.1
max_scale = 3.0
one_finger_rot_x = true
one_finger_rot_y = true
two_fingers_rot_z = true
two_fingers_zoom = false
[node name="Viewport" type="Viewport" parent="HBoxContainer_1/GestureArea"]
rect = Rect2( 0, 0, 254, 254 )
own_world = true
world = null
transparent_bg = false
render_target/enabled = false
render_target/v_flip = false
render_target/clear_on_new_frame = true
render_target/filter = false
render_target/gen_mipmaps = false
render_target/update_mode = 2
audio_listener/enable_2d = false
audio_listener/enable_3d = false
physics/object_picking = false
gui/disable_input = false
[node name="Spatial" parent="HBoxContainer_1/GestureArea/Viewport" instance=ExtResource( 2 )]
[node name="Label2" type="Label" parent="HBoxContainer_1/GestureArea"]
focus/ignore_mouse = true
focus/stop_mouse = true
size_flags/horizontal = 2
size_flags/vertical = 0
margin/left = 7.0
margin/top = 12.0
margin/right = 47.0
margin/bottom = 26.0
text = "One-finger X/Y rot + two-finger Z rot"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
[node name="GestureArea_1" type="Control" parent="HBoxContainer_1"]
focus/ignore_mouse = false
focus/stop_mouse = true
size_flags/horizontal = 3
size_flags/vertical = 3
margin/left = 258.0
margin/top = 0.0
margin/right = 512.0
margin/bottom = 254.0
script/script = ExtResource( 1 )
target = NodePath("Viewport/Spatial/TestCube")
min_scale = 0.1
max_scale = 3.0
one_finger_rot_x = true
one_finger_rot_y = true
two_fingers_rot_z = true
two_fingers_zoom = true
[node name="Viewport" type="Viewport" parent="HBoxContainer_1/GestureArea_1"]
rect = Rect2( 0, 0, 254, 254 )
own_world = true
world = null
transparent_bg = false
render_target/enabled = false
render_target/v_flip = false
render_target/clear_on_new_frame = true
render_target/filter = false
render_target/gen_mipmaps = false
render_target/update_mode = 2
audio_listener/enable_2d = false
audio_listener/enable_3d = false
physics/object_picking = false
gui/disable_input = false
[node name="Spatial" parent="HBoxContainer_1/GestureArea_1/Viewport" instance=ExtResource( 2 )]
[node name="Label3" type="Label" parent="HBoxContainer_1/GestureArea_1"]
focus/ignore_mouse = true
focus/stop_mouse = true
size_flags/horizontal = 2
size_flags/vertical = 0
margin/left = 15.0
margin/top = 12.0
margin/right = 55.0
margin/bottom = 26.0
text = "One-finger X/Y, two-finger Z + pinch"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1

View File

@@ -0,0 +1,18 @@
[application]
name="InteractiveCubes"
main_scene="res://Main.tscn"
icon="res://icon.png"
[display]
width=512
height=512
[physics_2d]
motion_fix_enabled=true
[render]
default_clear_color=#ff1d2232

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1 @@
gen_mipmaps=false

View File

@@ -0,0 +1,23 @@
extends Node2D
func _ready():
set_process(true)
func _process(delta):
# To keep redrawing on every frame
update()
func _draw():
# Get the touch helper singleton
var touch_helper = get_node("/root/TouchHelper")
# Draw every pointer as a circle
for ptr_id in touch_helper.state.keys():
var pos = touch_helper.state[ptr_id]
var color = _get_color_for_ptr_id(ptr_id)
color.a = 0.75
draw_circle(pos, 40.0, color)
# Just a way of getting different colors
func _get_color_for_ptr_id(id):
var x = (id % 7) + 1
return Color(float(bool(x & 1)), float(bool(x & 2)), float(bool(x & 4)))

View File

@@ -0,0 +1,10 @@
[gd_scene load_steps=2 format=1]
[ext_resource path="res://Main.gd" type="Script" id=1]
[node name="Main" type="Node2D"]
visibility/blend_mode = 1
script/script = ExtResource( 1 )

View File

@@ -0,0 +1,45 @@
# This will track the position of every pointer in its public `state` property, which is a
# Dictionary, in which each key is a pointer id (integer) and each value its position (Vector2).
# It works by listening to input events not handled by other means.
# It also remaps the pointer indices coming from the OS to the lowest available to be friendlier.
# It can be conveniently setup as a singleton.
extends Node
var state = {}
var _os2own = {}
func _ready():
set_process_unhandled_input(true)
func _unhandled_input(event):
if event.type == InputEvent.SCREEN_TOUCH:
if event.pressed:
# Down
if !_os2own.has(event.index): # Defensively discard index if already known
var ptr_id = _find_free_pointer_id()
state[ptr_id] = Vector2(event.x, event.y)
_os2own[event.index] = ptr_id
else:
# Up
if _os2own.has(event.index): # Defensively discard index if not known
var ptr_id = _os2own[event.index]
state.erase(ptr_id)
_os2own.erase(event.index)
return true
elif event.type == InputEvent.SCREEN_DRAG:
# Move
if _os2own.has(event.index): # Defensively discard index if not known
var ptr_id = _os2own[event.index]
state[ptr_id] = Vector2(event.x, event.y)
return true
return false
func _find_free_pointer_id():
var used = state.keys()
var i = 0
while i in used:
i += 1
return i

View File

@@ -0,0 +1,17 @@
[application]
name="TouchesView"
main_scene="res://Main.tscn"
icon="res://icon.png"
[autoload]
TouchHelper="*res://TouchHelper.gd"
[physics_2d]
motion_fix_enabled=true
[render]
default_clear_color=#ff1d2232

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
gen_mipmaps=false