├── .gitattributes
├── Sprite.jpg
├── default_env.tres
├── GCC2D.tscn
├── .gitignore
├── GDTIM-4.x-v2_1_2
├── CustomInputEvents
│ ├── InputEventScreenCancel.gd
│ ├── InputEventSingleScreenTap.gd
│ ├── InputEventMultiScreenTap.gd
│ ├── InputEventMultiScreenLongPress.gd
│ ├── InputEventSingleScreenDrag.gd
│ ├── InputEventSingleScreenSwipe.gd
│ ├── InputEventMultiScreenDrag.gd
│ ├── InputEventMultiScreenSwipe.gd
│ ├── InputEventSingleScreenTouch.gd
│ ├── InputEventScreenTwist.gd
│ ├── InputEventScreenPinch.gd
│ └── InputEventSingleScreenLongPress.gd
├── Util.gd
├── LICENSE
├── RawGesture.gd
├── README.md
└── InputManager.gd
├── Demo.tscn
├── .github
└── FUNDING.yml
├── project.godot
├── Sprite.jpg.import
├── LICENSE
├── README.md
├── GCC2D.gd
└── export_presets.cfg
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Normalize EOL for all files that Git considers text files.
2 | * text=auto eol=lf
3 |
--------------------------------------------------------------------------------
/Sprite.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Federico-Ciuffardi/GestureControlledCamera2D/HEAD/Sprite.jpg
--------------------------------------------------------------------------------
/default_env.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Environment" load_steps=2 format=3 uid="uid://bf6o1x6iugov8"]
2 |
3 | [sub_resource type="Sky" id="1"]
4 |
5 | [resource]
6 | background_mode = 2
7 | sky = SubResource("1")
8 |
--------------------------------------------------------------------------------
/GCC2D.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=2]
2 |
3 | [ext_resource path="res://GCC2D.gd" type="Script" id=1]
4 |
5 | [node name="GCC2D" type="Camera2D"]
6 | ignore_rotation =true # reversed "rotating" for Camera2D
7 | current = true
8 | script = ExtResource( 1 )
9 | movement_gesture = 1
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Godot 4+ specific ignores
2 | .godot/
3 |
4 | # Godot-specific ignores
5 | .import/
6 | export.cfg
7 | export_presets.cfg
8 |
9 | # Imported translations (automatically generated from CSV files)
10 | *.translation
11 |
12 | # Mono-specific ignores
13 | .mono/
14 | data_*/
15 | mono_crash.*.json
16 |
17 | # System/tool-specific ignores
18 | .directory
19 | *~
20 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventScreenCancel.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventScreenCancel
2 | extends InputEventAction
3 |
4 | var raw_gesture : RawGesture
5 | var event : InputEvent
6 |
7 | func _init(_raw_gesture : RawGesture, _event : InputEvent) -> void:
8 | raw_gesture = _raw_gesture
9 | event = _event
10 |
11 | func as_string() -> String:
12 | return "gesture canceled"
13 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventSingleScreenTap.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventSingleScreenTap
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var raw_gesture : RawGesture
6 |
7 | func _init(_raw_gesture : RawGesture = null) -> void:
8 | raw_gesture = _raw_gesture
9 | if raw_gesture:
10 | position = raw_gesture.presses.values()[0].position
11 |
12 |
13 | func as_string() -> String:
14 | return "position=" + str(position)
15 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/Util.gd:
--------------------------------------------------------------------------------
1 | const SEC_IN_USEC : int = 1000000
2 |
3 | static func map_callv(i_es : Array, f : String, vargs : Array) -> Array:
4 | var o_es : Array = []
5 | for e in i_es: o_es.append(e.callv(f,vargs))
6 | return o_es
7 |
8 | # Precondition:
9 | # * !arr.empty()
10 | static func centroid(es : Array):
11 | var sum = es[0]
12 | for i in range(1,es.size()):
13 | sum += es[i]
14 | return sum / es.size()
15 |
16 | static func now() -> float:
17 | return float(Time.get_ticks_usec())/SEC_IN_USEC
18 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventMultiScreenTap.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventMultiScreenTap
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var fingers : int
6 | var raw_gesture : RawGesture
7 |
8 | func _init(_raw_gesture : RawGesture = null) -> void:
9 | raw_gesture = _raw_gesture
10 | if raw_gesture:
11 | fingers = raw_gesture.size()
12 | position = raw_gesture.centroid("presses", "position")
13 |
14 | func as_string() -> String:
15 | return "position=" + str(position) + "|fingers=" + str(fingers)
16 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventMultiScreenLongPress.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventMultiScreenLongPress
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var fingers : int
6 | var raw_gesture : RawGesture
7 |
8 | func _init(_raw_gesture : RawGesture = null) -> void:
9 | raw_gesture = _raw_gesture
10 | if raw_gesture:
11 | fingers = raw_gesture.size()
12 | position = raw_gesture.centroid("presses", "position")
13 |
14 | func as_string() -> String:
15 | return "position=" + str(position) + "|fingers=" + str(fingers)
16 |
--------------------------------------------------------------------------------
/Demo.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=3 format=3 uid="uid://bywicuxckrdfc"]
2 |
3 | [ext_resource type="Texture2D" uid="uid://cvjxijew13y74" path="res://Sprite.jpg" id="1_dy1uf"]
4 | [ext_resource type="PackedScene" path="res://GCC2D.tscn" id="2"]
5 |
6 | [node name="Node2D" type="Node2D"]
7 |
8 | [node name="Sprite2D" type="Sprite2D" parent="."]
9 | texture = ExtResource("1_dy1uf")
10 |
11 | [node name="GCC2D" parent="." instance=ExtResource("2")]
12 | ignore_rotation = false
13 | limit_left = -1250
14 | limit_top = -600
15 | limit_right = 1250
16 | limit_bottom = 600
17 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventSingleScreenDrag.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventSingleScreenDrag
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var relative : Vector2
6 | var raw_gesture : RawGesture
7 |
8 | func _init(_raw_gesture : RawGesture = null) -> void:
9 | raw_gesture = _raw_gesture
10 | if raw_gesture:
11 | var dragEvent = raw_gesture.drags.values()[0]
12 | position = dragEvent.position
13 | relative = dragEvent.relative
14 |
15 | func as_string():
16 | return "position=" + str(position) + "|relative=" + str(relative)
17 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventSingleScreenSwipe.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventSingleScreenSwipe
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var relative : Vector2
6 | var raw_gesture : RawGesture
7 |
8 | func _init(_raw_gesture : RawGesture = null) -> void:
9 | raw_gesture = _raw_gesture
10 | if raw_gesture:
11 | position = raw_gesture.presses[0].position
12 | relative = raw_gesture.releases[0].position - position
13 |
14 |
15 | func as_string() -> String:
16 | return "position=" + str(position) + "|relative=" + str(relative)
17 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventMultiScreenDrag.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventMultiScreenDrag
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var relative : Vector2
6 | var fingers : int
7 | var raw_gesture : RawGesture
8 |
9 | func _init(_raw_gesture : RawGesture = null, event : InputEventScreenDrag = null) -> void:
10 | raw_gesture = _raw_gesture
11 | if raw_gesture:
12 | fingers = raw_gesture.size()
13 | position = raw_gesture.centroid("drags", "position")
14 | relative = event.relative/fingers
15 |
16 | func as_string() -> String:
17 | return "position=" + str(position) + "|relative=" + str(relative) + "|fingers=" + str(fingers)
18 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventMultiScreenSwipe.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventMultiScreenSwipe
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var relative : Vector2
6 | var fingers : int
7 | var raw_gesture : RawGesture
8 |
9 | func _init(_raw_gesture : RawGesture = null) -> void:
10 | raw_gesture = _raw_gesture
11 | if raw_gesture:
12 | fingers = raw_gesture.size()
13 | position = raw_gesture.centroid("presses", "position")
14 | relative = raw_gesture.centroid("releases", "position") - position
15 |
16 | func as_string() -> String:
17 | return "position=" + str(position) + "|relative=" + str(relative) + "|fingers=" + str(fingers)
18 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: ['federico-ciuffardi']
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://www.paypal.com/donate?hosted_button_id=8Y6RJVLLSABGC']
13 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventSingleScreenTouch.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventSingleScreenTouch
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var canceled : bool
6 | var raw_gesture : RawGesture
7 |
8 | func _init(_raw_gesture : RawGesture = null) -> void:
9 | raw_gesture = _raw_gesture
10 | if raw_gesture:
11 | pressed = raw_gesture.releases.is_empty()
12 | if pressed:
13 | position = raw_gesture.presses.values()[0].position
14 | else:
15 | position = raw_gesture.releases.values()[0].position
16 | canceled = raw_gesture.size() > 1
17 |
18 | func as_string() -> String:
19 | return "position=" + str(position) + "|pressed=" + str(pressed) + "|canceled=" + str(canceled)
20 |
--------------------------------------------------------------------------------
/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=5
10 |
11 | [application]
12 |
13 | config/name="GestureControlledCamera2D"
14 | run/main_scene="res://Demo.tscn"
15 | config/features=PackedStringArray("4.0")
16 |
17 | [autoload]
18 |
19 | InputManager="*res://GDTIM-4.x-v2_1_2/InputManager.gd"
20 |
21 | [input_devices]
22 |
23 | pointing/emulate_touch_from_mouse=true
24 |
25 | [rendering]
26 |
27 | textures/vram_compression/import_etc2_astc=true
28 | environment/defaults/default_environment="res://default_env.tres"
29 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventScreenTwist.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventScreenTwist
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var relative : float
6 | var fingers : int
7 | var raw_gesture : RawGesture
8 |
9 | func _init(_raw_gesture : RawGesture = null, event : InputEventScreenDrag = null) -> void:
10 | raw_gesture = _raw_gesture
11 | if raw_gesture:
12 | fingers = raw_gesture.drags.size()
13 | position = raw_gesture.centroid("drags", "position")
14 |
15 | var centroid_relative_position = event.position - position
16 | relative = centroid_relative_position.angle_to(centroid_relative_position + event.relative)/fingers
17 |
18 | func as_string() -> String:
19 | return "position=" + str(position) + "|relative=" + str(relative) + "|fingers=" + str(fingers)
20 |
--------------------------------------------------------------------------------
/Sprite.jpg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cvjxijew13y74"
6 | path="res://.godot/imported/Sprite.jpg-306cbf6b765bbdaedd3643d633e176f2.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://Sprite.jpg"
14 | dest_files=["res://.godot/imported/Sprite.jpg-306cbf6b765bbdaedd3643d633e176f2.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventScreenPinch.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventScreenPinch
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var relative : float
6 | var distance : float
7 | var fingers : int
8 | var raw_gesture : RawGesture
9 |
10 | func _init(_raw_gesture : RawGesture = null, event : InputEventScreenDrag = null) -> void:
11 | raw_gesture = _raw_gesture
12 | if raw_gesture:
13 | fingers = raw_gesture.drags.size()
14 | position = raw_gesture.centroid("drags", "position")
15 |
16 | distance = 0
17 | for drag in raw_gesture.drags.values():
18 | var centroid_relative_position = drag.position - position
19 | distance += centroid_relative_position.length()
20 |
21 | var centroid_relative_position = event.position - position
22 | relative = ((centroid_relative_position + event.relative).length() - centroid_relative_position.length())
23 |
24 |
25 | func as_string() -> String:
26 | return "position=" + str(position) + "|relative=" + str(relative) +"|distance ="+str(distance) + "|fingers=" + str(fingers)
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Federico Ciuffardi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Federico Ciuffardi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/CustomInputEvents/InputEventSingleScreenLongPress.gd:
--------------------------------------------------------------------------------
1 | class_name InputEventSingleScreenLongPress
2 | extends InputEventAction
3 |
4 | var position : Vector2
5 | var raw_gesture : RawGesture
6 |
7 | func _init(_raw_gesture : RawGesture = null) -> void:
8 | raw_gesture = _raw_gesture
9 | if raw_gesture:
10 | if !raw_gesture.presses.has(0):
11 | print("RAW GESTURE:\n" + raw_gesture.as_text())
12 | var linear_event_history = raw_gesture.get_linear_event_history()
13 | var history = "\nHISTORY:\n"
14 | for e in linear_event_history:
15 | if e is RawGesture.Drag:
16 | history += "D | "
17 | else:
18 | history += "T | "
19 | history += e.as_text()
20 | history +="\n"
21 | print(history)
22 | var error_msg="Hello! we are trying to fix this bug.\nTo help us please copy the output and comment it (attached as a file) in the following issue: https://github.com/Federico-Ciuffardi/GodotTouchInputManager/issues/20\nAlso, if you can, include in that comment what version of Godot you are using, what platform you are running on, and what you were doing when the error occurred.\nThanks!"
23 | print(error_msg)
24 | position = raw_gesture.presses.values()[0].position
25 |
26 |
27 | func as_string() -> String:
28 | return "position=" + str(position)
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # GestureControlledCamera2D
4 | A Camera2D node controlled through gestures. It's also an example of how to use the [Godot Touch Input Manager](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager).
5 |
6 | ## Demo
7 | 
8 |
9 | ## How to use
10 | ### 1 - Seting up [Godot Touch Input Manager](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager)
11 | * Dowload the latest release from https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/releases
12 | * Extract the downloaded *.zip* file somewhere in you project
13 | * Locate the extracted `InputManager.gd`, and [Autoload](https://docs.godotengine.org/en/3.4/tutorials/scripting/singletons_autoload.html) it.
14 |
15 | ### 2 - Using GestureControlledCamera2D
16 | * Dowload the latest release from https://github.com/Federico-Ciuffardi/GestureControlledCamera2D/releases
17 | * Extract the downloaded *.zip* file somewhere in you project
18 | * Add the extracted GestureControlledCamera2D node (GCC2D.tscn) to the scene and make sure to set `Current` to `On`
19 | * Customize the [script variables](#script-variables) to your liking (optional)
20 |
21 | Check out the [example](https://github.com/Federico-Ciuffardi/GestureControlledCamera2D/releases/download/v1.1.0/GestureControlledCamera2D-Example.zip)!
22 |
23 | ## Script variables
24 |
25 | | Name | Description |
26 | |------------------|-----------------------------------------------------|
27 | | Max Zoom | The camera will not zoom in any further than this |
28 | | Min Zoom | The browser will not zoom out any further than this |
29 | | Zoom Gesture | The gesture that will control the camera zoom |
30 | | Rotation gesture | The gesture that will control the camera rotation |
31 | | Movement Gesture | The gesture that will control the camera movement |
32 |
33 | ## Versioning
34 | Using [SemVer](http://semver.org/) for versioning. For the versions available, see the [releases](https://github.com/Federico-Ciuffardi/GestureControlledCamera2D/releases)
35 |
36 | ## Authors
37 | * Federico Ciuffardi
38 |
39 | Feel free to append yourself here if you've made contributions.
40 |
41 | ## Note
42 | Thank you for checking out this repository, you can send all your questions and feedback to Federico.Ciuffardi@outlook.com.
43 |
44 | If you are up to contribute on some way please contact me :)
45 |
--------------------------------------------------------------------------------
/GCC2D.gd:
--------------------------------------------------------------------------------
1 | extends Camera2D
2 |
3 | # Configuration
4 | @export var MAX_ZOOM: float = 4
5 | @export var MIN_ZOOM: float = 0.16
6 |
7 | enum ZOOM_GESTURE { DISABLED , PINCH }
8 | enum ROTATE_GESTURE { DISABLED , TWIST }
9 | enum MOVEMENT_GESTURE { DISABLED, SINGLE_DRAG, MULTI_DRAG }
10 |
11 | @export var zoom_gesture : ZOOM_GESTURE = ZOOM_GESTURE.PINCH
12 | @export var rotation_gesture : ROTATE_GESTURE = ROTATE_GESTURE.TWIST
13 | @export var movement_gesture : MOVEMENT_GESTURE = MOVEMENT_GESTURE.SINGLE_DRAG
14 |
15 | var effective_limit_left = -10000000
16 | var effective_limit_right = 10000000
17 | var effective_limit_top = -10000000
18 | var effective_limit_bottom = 10000000
19 |
20 | func set_camera_position(p):
21 | var camera_limits;
22 | var camera_size = get_camera_size()/zoom
23 |
24 | if anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT:
25 | camera_limits = [
26 | Vector2(),
27 | Vector2(camera_size.x, 0),
28 | Vector2(0, camera_size.y),
29 | Vector2(camera_size.x, camera_size.y),
30 | Vector2(camera_size.x, 0),
31 | ]
32 |
33 | elif anchor_mode == ANCHOR_MODE_DRAG_CENTER:
34 | camera_limits = [
35 | Vector2(-camera_size.x/2, -camera_size.y/2),
36 | Vector2( camera_size.x/2, -camera_size.y/2),
37 | Vector2( camera_size.x/2, camera_size.y/2),
38 | Vector2(-camera_size.x/2, camera_size.y/2),
39 | ]
40 |
41 | for i in camera_limits.size():
42 | camera_limits[i] = camera_limits[i].rotated(rotation);
43 |
44 | for camera_limit in camera_limits:
45 | if(p.x > effective_limit_right - camera_limit.x):
46 | p.x = effective_limit_right - camera_limit.x
47 |
48 | if(p.y > effective_limit_bottom - camera_limit.y):
49 | p.y = effective_limit_bottom - camera_limit.y
50 |
51 | if(p.x < effective_limit_left - camera_limit.x):
52 | p.x = effective_limit_left - camera_limit.x
53 |
54 | if(p.y < effective_limit_top - camera_limit.y):
55 | p.y = effective_limit_top - camera_limit.y
56 |
57 | for camera_limit in camera_limits:
58 | if((p.x > effective_limit_right - camera_limit.x) or
59 | (p.y > effective_limit_bottom - camera_limit.y) or
60 | (p.x < effective_limit_left - camera_limit.x) or
61 | (p.y < effective_limit_top - camera_limit.y)):
62 | return false;
63 |
64 | position = p
65 |
66 | return true
67 |
68 |
69 | func _unhandled_input(e):
70 | if (e is InputEventMultiScreenDrag and movement_gesture == 2
71 | or e is InputEventSingleScreenDrag and movement_gesture == 1):
72 | _move(e)
73 | elif e is InputEventScreenTwist and rotation_gesture == 1:
74 | _rotate(e)
75 | elif e is InputEventScreenPinch and zoom_gesture == 1:
76 | _zoom(e)
77 |
78 | # Given a a position on the camera returns to the corresponding global position
79 | func camera2global(position):
80 | var camera_center = global_position
81 | var from_camera_center_pos = position - get_camera_center_offset()
82 | return camera_center + (from_camera_center_pos/zoom).rotated(rotation)
83 |
84 | func _move(event):
85 | set_camera_position(position - (event.relative/zoom).rotated(rotation))
86 |
87 | func _zoom(event):
88 | var li = event.distance
89 | var lf = event.distance - event.relative
90 |
91 | var zi = zoom.x
92 | var zf = (li*zi)/lf
93 | var zd = zf - zi
94 |
95 | if zf <= MIN_ZOOM and sign(zd) < 0:
96 | zf = MIN_ZOOM
97 | zd = zf - zi
98 | elif zf >= MAX_ZOOM and sign(zd) > 0:
99 | zf = MAX_ZOOM
100 | zd = zf - zi
101 |
102 | zoom = zf*Vector2.ONE
103 |
104 | var from_camera_center_pos = event.position - get_camera_center_offset()
105 | var relative = (from_camera_center_pos*zd) / (zi*zf)
106 | if(!set_camera_position(position + relative.rotated(rotation))):
107 | zoom = zi*Vector2.ONE
108 |
109 | func _rotate(event):
110 | if ignore_rotation: return
111 |
112 | var fccp = event.position - get_camera_center_offset()
113 | var fccp_op_rot = -fccp.rotated(event.relative)
114 |
115 | rotation -= event.relative
116 |
117 | if(!set_camera_position(position - ((fccp_op_rot + fccp)/zoom).rotated(rotation-event.relative))):
118 | rotation += event.relative
119 |
120 | func get_camera_center_offset():
121 | if anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT:
122 | return Vector2.ZERO
123 | elif anchor_mode == ANCHOR_MODE_DRAG_CENTER:
124 | return get_camera_size()/2
125 |
126 | func get_camera_size():
127 | return get_viewport().get_visible_rect().size
128 |
129 | func _ready():
130 | var limit_left_tmp = effective_limit_left
131 | effective_limit_left = limit_left
132 | limit_left = limit_left_tmp
133 |
134 | var limit_right_tmp = effective_limit_right
135 | effective_limit_right = limit_right
136 | limit_right = limit_right_tmp
137 |
138 | var limit_top_tmp = effective_limit_top
139 | effective_limit_top = limit_top
140 | limit_top = limit_top_tmp
141 |
142 | var limit_bottom_tmp = effective_limit_bottom
143 | effective_limit_bottom = limit_bottom
144 | limit_bottom = limit_bottom_tmp
145 |
146 | set_camera_position(position)
147 |
--------------------------------------------------------------------------------
/export_presets.cfg:
--------------------------------------------------------------------------------
1 | [preset.0]
2 |
3 | name="Android"
4 | platform="Android"
5 | runnable=true
6 | custom_features=""
7 | export_filter="all_resources"
8 | include_filter=""
9 | exclude_filter=""
10 | export_path="../../GestureControlledCamera.apk"
11 | patch_list=PoolStringArray( )
12 | script_export_mode=1
13 | script_encryption_key=""
14 |
15 | [preset.0.options]
16 |
17 | graphics/32_bits_framebuffer=true
18 | xr_features/xr_mode=0
19 | xr_features/degrees_of_freedom=0
20 | xr_features/hand_tracking=0
21 | one_click_deploy/clear_previous_install=false
22 | custom_template/debug=""
23 | custom_template/release=""
24 | custom_template/use_custom_build=false
25 | command_line/extra_args=""
26 | version/code=1
27 | version/name="1.0"
28 | package/unique_name="com.FedericoCiuffardi.GestureControlledCamera"
29 | package/name="GestureControlledCamera"
30 | package/signed=true
31 | screen/immersive_mode=true
32 | screen/orientation=0
33 | screen/support_small=true
34 | screen/support_normal=true
35 | screen/support_large=true
36 | screen/support_xlarge=true
37 | screen/opengl_debug=false
38 | launcher_icons/main_192x192=""
39 | launcher_icons/adaptive_foreground_432x432=""
40 | launcher_icons/adaptive_background_432x432=""
41 | keystore/debug=""
42 | keystore/debug_user=""
43 | keystore/debug_password=""
44 | keystore/release=""
45 | keystore/release_user=""
46 | keystore/release_password=""
47 | apk_expansion/enable=false
48 | apk_expansion/SALT=""
49 | apk_expansion/public_key=""
50 | architectures/armeabi-v7a=true
51 | architectures/arm64-v8a=true
52 | architectures/x86=false
53 | architectures/x86_64=false
54 | permissions/custom_permissions=PoolStringArray( )
55 | permissions/access_checkin_properties=false
56 | permissions/access_coarse_location=false
57 | permissions/access_fine_location=false
58 | permissions/access_location_extra_commands=false
59 | permissions/access_mock_location=false
60 | permissions/access_network_state=false
61 | permissions/access_surface_flinger=false
62 | permissions/access_wifi_state=false
63 | permissions/account_manager=false
64 | permissions/add_voicemail=false
65 | permissions/authenticate_accounts=false
66 | permissions/battery_stats=false
67 | permissions/bind_accessibility_service=false
68 | permissions/bind_appwidget=false
69 | permissions/bind_device_admin=false
70 | permissions/bind_input_method=false
71 | permissions/bind_nfc_service=false
72 | permissions/bind_notification_listener_service=false
73 | permissions/bind_print_service=false
74 | permissions/bind_remoteviews=false
75 | permissions/bind_text_service=false
76 | permissions/bind_vpn_service=false
77 | permissions/bind_wallpaper=false
78 | permissions/bluetooth=false
79 | permissions/bluetooth_admin=false
80 | permissions/bluetooth_privileged=false
81 | permissions/brick=false
82 | permissions/broadcast_package_removed=false
83 | permissions/broadcast_sms=false
84 | permissions/broadcast_sticky=false
85 | permissions/broadcast_wap_push=false
86 | permissions/call_phone=false
87 | permissions/call_privileged=false
88 | permissions/camera=false
89 | permissions/capture_audio_output=false
90 | permissions/capture_secure_video_output=false
91 | permissions/capture_video_output=false
92 | permissions/change_component_enabled_state=false
93 | permissions/change_configuration=false
94 | permissions/change_network_state=false
95 | permissions/change_wifi_multicast_state=false
96 | permissions/change_wifi_state=false
97 | permissions/clear_app_cache=false
98 | permissions/clear_app_user_data=false
99 | permissions/control_location_updates=false
100 | permissions/delete_cache_files=false
101 | permissions/delete_packages=false
102 | permissions/device_power=false
103 | permissions/diagnostic=false
104 | permissions/disable_keyguard=false
105 | permissions/dump=false
106 | permissions/expand_status_bar=false
107 | permissions/factory_test=false
108 | permissions/flashlight=false
109 | permissions/force_back=false
110 | permissions/get_accounts=false
111 | permissions/get_package_size=false
112 | permissions/get_tasks=false
113 | permissions/get_top_activity_info=false
114 | permissions/global_search=false
115 | permissions/hardware_test=false
116 | permissions/inject_events=false
117 | permissions/install_location_provider=false
118 | permissions/install_packages=false
119 | permissions/install_shortcut=false
120 | permissions/internal_system_window=false
121 | permissions/internet=false
122 | permissions/kill_background_processes=false
123 | permissions/location_hardware=false
124 | permissions/manage_accounts=false
125 | permissions/manage_app_tokens=false
126 | permissions/manage_documents=false
127 | permissions/master_clear=false
128 | permissions/media_content_control=false
129 | permissions/modify_audio_settings=false
130 | permissions/modify_phone_state=false
131 | permissions/mount_format_filesystems=false
132 | permissions/mount_unmount_filesystems=false
133 | permissions/nfc=false
134 | permissions/persistent_activity=false
135 | permissions/process_outgoing_calls=false
136 | permissions/read_calendar=false
137 | permissions/read_call_log=false
138 | permissions/read_contacts=false
139 | permissions/read_external_storage=false
140 | permissions/read_frame_buffer=false
141 | permissions/read_history_bookmarks=false
142 | permissions/read_input_state=false
143 | permissions/read_logs=false
144 | permissions/read_phone_state=false
145 | permissions/read_profile=false
146 | permissions/read_sms=false
147 | permissions/read_social_stream=false
148 | permissions/read_sync_settings=false
149 | permissions/read_sync_stats=false
150 | permissions/read_user_dictionary=false
151 | permissions/reboot=false
152 | permissions/receive_boot_completed=false
153 | permissions/receive_mms=false
154 | permissions/receive_sms=false
155 | permissions/receive_wap_push=false
156 | permissions/record_audio=false
157 | permissions/reorder_tasks=false
158 | permissions/restart_packages=false
159 | permissions/send_respond_via_message=false
160 | permissions/send_sms=false
161 | permissions/set_activity_watcher=false
162 | permissions/set_alarm=false
163 | permissions/set_always_finish=false
164 | permissions/set_animation_scale=false
165 | permissions/set_debug_app=false
166 | permissions/set_orientation=false
167 | permissions/set_pointer_speed=false
168 | permissions/set_preferred_applications=false
169 | permissions/set_process_limit=false
170 | permissions/set_time=false
171 | permissions/set_time_zone=false
172 | permissions/set_wallpaper=false
173 | permissions/set_wallpaper_hints=false
174 | permissions/signal_persistent_processes=false
175 | permissions/status_bar=false
176 | permissions/subscribed_feeds_read=false
177 | permissions/subscribed_feeds_write=false
178 | permissions/system_alert_window=false
179 | permissions/transmit_ir=false
180 | permissions/uninstall_shortcut=false
181 | permissions/update_device_stats=false
182 | permissions/use_credentials=false
183 | permissions/use_sip=false
184 | permissions/vibrate=false
185 | permissions/wake_lock=false
186 | permissions/write_apn_settings=false
187 | permissions/write_calendar=false
188 | permissions/write_call_log=false
189 | permissions/write_contacts=false
190 | permissions/write_external_storage=false
191 | permissions/write_gservices=false
192 | permissions/write_history_bookmarks=false
193 | permissions/write_profile=false
194 | permissions/write_secure_settings=false
195 | permissions/write_settings=false
196 | permissions/write_sms=false
197 | permissions/write_social_stream=false
198 | permissions/write_sync_settings=false
199 | permissions/write_user_dictionary=false
200 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/RawGesture.gd:
--------------------------------------------------------------------------------
1 | # warning-ignore-all:return_value_discarded
2 |
3 | extends InputEventAction
4 | class_name RawGesture
5 |
6 | #########
7 | # Const #
8 | #########
9 |
10 | const Util : GDScript = preload("Util.gd")
11 |
12 | ###########
13 | # Classes #
14 | ###########
15 |
16 | class Event:
17 | var time : float = -1 # (secs)
18 | var index : int = -1
19 | func as_string() -> String:
20 | return "ind: " + str(index) + " | time: " + str(time)
21 |
22 | class Touch:
23 | extends Event
24 | var position : Vector2 = Vector2.ZERO
25 | var pressed : bool
26 | func as_string() -> String:
27 | return super.as_string() + " | pos: " + str(position) + " | pressed: " + str(pressed)
28 |
29 |
30 | class Drag:
31 | extends Event
32 | var position : Vector2 = Vector2.ZERO
33 | var relative : Vector2 = Vector2.ZERO
34 | var velocity : Vector2 = Vector2.ZERO
35 |
36 | func as_string() -> String:
37 | return super.as_string() + " | pos: " + str(position) + " | relative: " + str(relative)
38 |
39 |
40 | #############
41 | # Variables #
42 | #############
43 |
44 | var presses : Dictionary # Touch
45 | var releases : Dictionary # Touch
46 | var drags : Dictionary # Drag
47 | var history : Dictionary # Array of events
48 |
49 | var active_touches : int = 0
50 |
51 | var start_time : float = -1 # (secs)
52 | var elapsed_time : float = -1 # (secs)
53 |
54 | #############
55 | # Functions #
56 | #############
57 |
58 | func size() -> int:
59 | return presses.size()
60 |
61 | func centroid(events_name : String , property_name : String):
62 | var arr : Array = get(events_name).values()
63 | arr = Util.map_callv(arr , "get", [property_name])
64 | return Util.centroid(arr)
65 |
66 | func get_ends() -> Dictionary:
67 | var ends : Dictionary = {}
68 |
69 | for i in presses:
70 | ends[i] = presses[i].position
71 |
72 | for i in drags:
73 | ends[i] = drags[i].position
74 |
75 | for i in releases:
76 | ends[i] = releases[i].position
77 |
78 | return ends
79 |
80 | # Check for gesture consistency
81 | func is_consistent(diff_limit : float, length_limit : float = -1) -> bool:
82 | if length_limit == -1: length_limit = length_limit
83 |
84 | var ends : Dictionary = get_ends()
85 |
86 | var ends_centroid : Vector2 = Util.centroid(ends.values())
87 | var starts_centroid : Vector2 = centroid("presses", "position")
88 |
89 | var valid : bool = true
90 | for i in ends:
91 | var start_relative_position : Vector2 = presses[i].position - starts_centroid
92 | var end_relative_position : Vector2 = ends[i] - ends_centroid
93 |
94 | valid = start_relative_position.length() < length_limit and \
95 | end_relative_position.length() < length_limit and \
96 | (end_relative_position - start_relative_position).length() < diff_limit
97 |
98 | if !valid:
99 | break
100 |
101 | return valid
102 |
103 | func rollback_relative(time : float) -> Array:
104 | return rollback_absolute(start_time+elapsed_time - time)
105 |
106 | func rollback_absolute(time : float) -> Array:
107 | var discarded_events : Array = []
108 | var rg : RawGesture = copy()
109 |
110 | var latest_event_id : Array = rg.latest_event_id(time)
111 | while !latest_event_id.is_empty():
112 | var latest_index : int = latest_event_id[0]
113 | var latest_type : String = latest_event_id[1]
114 | var latest_event = rg.history[latest_index][latest_type].pop_back()
115 | discarded_events.append(latest_event)
116 | if latest_type == "presses":
117 | rg.active_touches -= 1
118 | elif latest_type == "releases":
119 | rg.active_touches += 1
120 | if rg.history[latest_index][latest_type].is_empty():
121 | rg.history[latest_index].erase(latest_type)
122 | if rg.history[latest_index].is_empty():
123 | rg.history.erase(latest_index)
124 | latest_event_id = rg.latest_event_id(time)
125 |
126 | for index in rg.presses.keys():
127 | if rg.history.has(index):
128 | if rg.history[index].has("presses"):
129 | var presses_history: Array = rg.history[index]["presses"]
130 | rg.presses[index] = presses_history.back()
131 | else:
132 | rg.presses.erase(index)
133 |
134 | if rg.history[index].has("releases"):
135 | var releases_history : Array = rg.history[index]["releases"]
136 | # !releases_history.empty() -> rg.presses.has(index) (touch precedes a release)
137 | if releases_history.back().time < rg.presses[index].time:
138 | rg.releases.erase(index)
139 | else:
140 | rg.releases[index] = releases_history.back()
141 | else:
142 | rg.releases.erase(index)
143 |
144 | if rg.history[index].has("drags"):
145 | var drags_history : Array = rg.history[index]["drags"]
146 | # rg.releases.has(index) -> rg.releases[index].time >= rg.presses[index].time ->
147 | # rg.releases[index] >= drags_history.back().time (drag should needs a new touch after the release)
148 | if rg.releases.has(index):
149 | rg.drags.erase(index)
150 | else:
151 | rg.drags[index] = drags_history.back()
152 | else:
153 | rg.drags.erase(index)
154 | else:
155 | rg.presses.erase(index)
156 | rg.releases.erase(index)
157 | rg.drags.erase(index)
158 |
159 | return [rg, discarded_events]
160 |
161 | func get_linear_event_history():
162 | return rollback_absolute(0)[1]
163 |
164 | func copy() -> RawGesture:
165 | var rg : RawGesture = get_script().new()
166 | rg.presses = presses.duplicate(true)
167 | rg.releases = releases.duplicate(true)
168 | rg.drags = drags.duplicate(true)
169 | rg.history = history.duplicate(true)
170 | rg.active_touches = active_touches
171 | rg.start_time = start_time
172 | rg.elapsed_time = elapsed_time
173 | return rg
174 |
175 | func latest_event_id(latest_time : float = -1) -> Array:
176 | var res : Array = []
177 | for index in history:
178 | for type in history[index]:
179 | var event_time = history[index][type].back().time
180 | if event_time >= latest_time:
181 | res = [index, type]
182 | latest_time = event_time
183 | return res
184 |
185 | func as_string() -> String:
186 | var txt = "presses: "
187 | for e in presses.values():
188 | txt += "\n" + e.as_string()
189 | txt += "\ndrags: "
190 | for e in drags.values():
191 | txt += "\n" + e.as_string()
192 | txt += "\nreleases: "
193 | for e in releases.values():
194 | txt += "\n" + e.as_string()
195 | return txt
196 |
197 | func _update_screen_drag(event : InputEventScreenDrag, time : float = -1) -> void:
198 | if time < 0:
199 | time = Util.now()
200 | var drag : Drag = Drag.new()
201 | drag.position = event.position
202 | drag.relative = event.relative
203 | drag.velocity = event.velocity
204 | drag.index = event.index
205 | drag.time = time
206 | _add_history(event.index, "drags", drag)
207 | drags[event.index] = drag
208 | elapsed_time = time - start_time
209 |
210 | func _update_screen_touch(event : InputEventScreenTouch, time : float = -1) -> void:
211 | if time < 0:
212 | time = Util.now()
213 | var touch : Touch = Touch.new()
214 | touch.position = event.position
215 | touch.pressed = event.pressed
216 | touch.index = event.index
217 | touch.time = time
218 | if event.pressed:
219 | _add_history(event.index, "presses", touch)
220 | presses[event.index] = touch
221 | active_touches += 1
222 | releases.erase(event.index)
223 | drags.erase(event.index)
224 | if active_touches == 1:
225 | start_time = time
226 | else:
227 | _add_history(event.index, "releases", touch)
228 | releases[event.index] = touch
229 | active_touches -= 1
230 | drags.erase(event.index)
231 | elapsed_time = time - start_time
232 |
233 | func _add_history(index : int, type : String, value) -> void:
234 | if !history.has(index):
235 | history[index] = {}
236 | if !history[index].has(type):
237 | history[index][type] = []
238 | history[index][type].append(value)
239 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Godot Touch Input Manager
4 | Godot Touch Input Manager (GDTIM) is an asset that improves touch input support (includes [new gestures](#supported-gestures)) in the Godot game engine. You just need to autoload a script and it will start analyzing the touch input. When a gesture is detected a Custom Input Event corresponding to the detected gesture will be created and [fed up](https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-parse-input-event) to the Godot built in Input Event system so it triggers functions like [`_input(InputEvent event)`](https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-method-input). There is also a signal for each gesture if you prefer using signals to the aforementioned.
5 |
6 | There are two active PRs that add some GDTIM gestures as native Godot events, one for [version 3.x](https://github.com/godotengine/godot/pull/37754) and one for [version 4.x](https://github.com/godotengine/godot/pull/39055), if you are interested, please show your support there.
7 |
8 | ## Table of contents
9 | * [How to use](#how-to-use)
10 | * [Examples](#examples)
11 | * [Documentation](#documentation)
12 | * [FAQ](#faq)
13 |
14 | ## How to use
15 | * Download the latest release from https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/releases
16 | * Extract the downloaded *.zip* file somewhere in you project
17 | * Locate the extracted `InputManager.gd`, and [Autoload](https://docs.godotengine.org/en/3.4/tutorials/scripting/singletons_autoload.html) it.
18 | * Done! Now you can use GDTIM [signals and Custom Input Events](#supported-gestures).
19 |
20 | ## Examples
21 | ### [GodotTouchInputManager-Demo](https://github.com/Federico-Ciuffardi/GodotTouchInputManager-Demo)
22 | 
23 | ### [GestureControlledCamera2D](https://github.com/Federico-Ciuffardi/GestureControlledCamera2D)
24 | 
25 |
26 | ## Documentation
27 |
28 | * [Supported gestures](#supported-gestures)
29 | * [Gesture emulation](#gesture-emulation)
30 | * [Configuration](#configuration)
31 |
32 | ### Supported gestures
33 |
34 | | Gesture name | Signal | Custom input event / Signal arg | Description |
35 | |----------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|
36 | | Single finger touch | single_touch | [InputEventSingleScreenTouch](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenTouch) | Touch with a single finger |
37 | | Single finger tap | single_tap | [InputEventSingleScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenTap) | Fast press and release with a single finger |
38 | | Single finger long press | single_long_press | [InputEventSingleScreenLongPress](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenLongPress) | Press and hold with a single finger |
39 | | Single finger drag | single_drag | [InputEventSingleScreenDrag](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenDrag) | Drag with a single finger |
40 | | Single finger swipe | single_swipe | [InputEventSingleScreenSwipe](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenSwipe) | Fast drag and release with a single finger |
41 | | Multiple finger tap | multi_tap | [InputEventMultiScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenTap) | Fast press and release with multiple fingers |
42 | | Multiple finger long press | multi_long_press | [InputEventMultiScreenLongPress](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenLongPress) | Press and hold with multiple fingers |
43 | | Multiple finger drag | multi_drag | [InputEventMultiScreenDrag](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenDrag) | Drag with multiple fingers (same direction) |
44 | | Multiple finger swipe | multi_swipe | [InputEventMultiScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenTap) | Fast drag and release with multiple fingers |
45 | | Pinch | pinch | [InputEventScreenPinch](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventScreenPinch) | Drag with multiple fingers (inward/outward) |
46 | | Twist | twist | [InputEventScreenTwist](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventScreenTwist) | Drag with multiple fingers (rotate) |
47 | | Raw gesture | raw_gesture | [RawGesture](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/RawGesture) | Raw gesture state
48 |
49 | When one of these gestures is detected a Custom Input Event corresponding to the detected gesture will be created and [fed up](https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-parse-input-event) to the Godot built in Input Event system so it triggers functions like [`_input(InputEvent event)`](https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-method-input).
50 |
51 | ### Gesture emulation
52 |
53 | The gestures can be triggered by named [input actions](https://docs.godotengine.org/en/stable/tutorials/inputs/input_examples.html#inputmap) with specific names. If the input
54 | action does not exists there is a default event that will trigger the gesture.
55 |
56 | The following table shows the default event and the names of the input actions
57 | that will trigger each of the gestures that can be emulated.
58 |
59 | | Gesture name | Input action name | Default event |
60 | |------------------------------------|-------------------------|---------------|
61 | | Single touch | single_touch | **\*** |
62 | | Multiple touch (2 fingers) | multi_touch | Middle click |
63 | | Pinch (outward) | pinch_outward | Scroll up |
64 | | Pinch (inward) | pinch_inward | Scroll down |
65 | | Twist | twist | Right click |
66 | | Single finger swipe (up) | single_swipe_up | w |
67 | | Single finger swipe (up-right) | single_swipe_up_right | e |
68 | | Single finger swipe (right) | single_swipe_right | d |
69 | | Single finger swipe (down-right) | single_swipe_down_right | c |
70 | | Single finger swipe (down) | single_swipe_down | x |
71 | | Single finger swipe (down-left) | single_swipe_down_left | z |
72 | | Single finger swipe (left) | single_swipe_left | a |
73 | | Single finger swipe (left-up) | single_swipe_up_left | q |
74 | | Multiple finger swipe (up) | multi_swipe_up | i |
75 | | Multiple finger swipe (up-right) | multi_swipe_up_right | o |
76 | | Multiple finger swipe (right) | multi_swipe_right | l |
77 | | Multiple finger swipe (down-right) | multi_swipe_down_right | . |
78 | | Multiple finger swipe (down) | multi_swipe_down | , |
79 | | Multiple finger swipe (down-left) | multi_swipe_down_left | m |
80 | | Multiple finger swipe (left) | multi_swipe_left | j |
81 | | Multiple finger swipe (left-up) | multi_swipe_up_left | u |
82 |
83 | **\*** There are two options to enable single finger gestures:
84 | 1. Go to **Project > Project Settings > General > Input Devices > Pointing**
85 | and turn on *Emulate Touch From Mouse* to emulate a single finger touch with
86 | the left click.
87 | 2. Go to **Project > Project Settings > General > Input Devices > Pointing**
88 | and turn off both *Emulate Touch From Mouse* and *Emulate Mouse From Touch*.
89 | Then set an input action called `single_touch`.
90 |
91 | ## Configuration
92 |
93 | These are located in the first lines of [InputManager.gd](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/blob/master/InputManager.gd), to change them modify the
94 | values on the script.
95 |
96 | | Name | Default value | Description |
97 | |--------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
98 | | DEFAULT_BINDIGS | true | Enable or disable default events for [gesture emulation](#gesture-emulation) |
99 | | DEBUG | false | Enable or disable debug information |
100 | | DRAG_STARTUP_TIME | 0.2 | Seconds from the first native drag event to the first [single finger drag](#gestures-supported) custom event |
101 | | FINGER_SIZE | 100.0 | The distance between the fingers must be less than `fingers*FINGER_SIZE` pixels for the [multiple finger tap](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. Setting it to `INF` removes this restriction. |
102 | | MULTI_FINGER_RELEASE_THRESHOLD | 0.1 | All fingers must be released within `MULTI_FINGER_REALEASE_THRESHOLD` seconds before the gesture ends for the [multiple finger tap](#gestures-supported) and [multiple finger swipe](#gestures-supported) gestures to be recognized |
103 | | TAP_TIME_LIMIT | 0.2 | The time between the first press and the last release must be less than `TAP_TIME_LIMIT` seconds for the [single finger tap](#supported-gestures) and [multiple finger tap](#supported-gestures) gestures to be recognized |
104 | | TAP_DISTANCE_LIMIT | 25.0 | The centroid of the finger presses must differ less than `TAP_DISTANCE_LIMIT` pixels from the centroid of the finger releases for the [single finger tap](#supported-gestures) and [multiple finger tap](#supported-gestures) gestures to be recognized. |
105 | | SWIPE_TIME_LIMIT | 0.5 | The time between the first press and the last release must be less than `SWIPE_TIME_LIMIT` seconds for the [single finger swipe](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. |
106 | | SWIPE_DISTANCE_THRESHOLD | 200.0 | The centroid of the finger presses must differ by more than `SWIPE_DISTANCE_THRESHOLD` pixels from the centroid of the finger releases for the [single finger swipe](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. |
107 | | LONG_PRESS_TIME_THRESHOLD | 0.75 | The fingers must press for `LONG_PRESS_TIME_THRESHOLD` seconds for [single-finger long press](#gestures-supported) and [multi-finger long press](#gestures-supported) gestures to be recognized. |
108 | | LONG_PRESS_DISTANCE_LIMIT | 25.0 | The centroid of the finger presses must differ less than `LONG_PRESS_DISTANCE_LIMIT` pixels from the centroid of the fingers last positions for the [single finger long press](#supported-gestures) and [multiple finger long press](#supported-gestures) gestures to be recognized. |
109 |
110 | ## FAQ
111 | ### How can I get GDTIM to work when using control nodes?
112 |
113 | By default, the control nodes consume events and therefore GDTIM cannot analyze them. To prevent this, set `Mouse>Filter` to `Ignore` on control nodes as needed.
114 |
115 | 
116 |
117 | For more information see the [documentation](https://docs.godotengine.org/en/stable/classes/class_control.html#enum-control-mousefilter).
118 |
119 | ### GDTIM events don't trigger collisions, is there a way to fix it?
120 |
121 | Custom input events do not trigger collisions, at the moment the solution is to manually check for collisions between shapes and events. For more information and ideas on how to do this see [this issue](https://github.com/Federico-Ciuffardi/GodotTouchInputManager/issues/16).
122 |
123 | ## Versioning
124 | Using [SemVer](http://semver.org/) for versioning. For the versions available, see the [releases](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/releases).
125 |
126 | ## Authors
127 | * Federico Ciuffardi
128 |
129 | Feel free to append yourself here if you've made contributions.
130 |
131 | ## Note
132 | Thank you for checking out this repository, you can send all your questions and comments to Federico.Ciuffardi@outlook.com.
133 |
134 | If you are willing to contribute in any way, please contact me.
135 |
--------------------------------------------------------------------------------
/GDTIM-4.x-v2_1_2/InputManager.gd:
--------------------------------------------------------------------------------
1 | # warning-ignore-all:return_value_discarded
2 | # warning-ignore-all:unused_signal
3 |
4 | extends Node
5 |
6 | ##########
7 | # Config #
8 | ##########
9 |
10 | const DEFAULT_BINDIGS : bool = true
11 |
12 | const DEBUG : bool = false
13 |
14 | const DRAG_STARTUP_TIME : float = 0.02
15 |
16 | const FINGER_SIZE : float = 100.0
17 |
18 | const MULTI_FINGER_RELEASE_THRESHOLD : float = 0.1
19 |
20 | const TAP_TIME_LIMIT : float = 0.2
21 | const TAP_DISTANCE_LIMIT : float = 25.0
22 |
23 | const LONG_PRESS_TIME_THRESHOLD : float = 0.75
24 | const LONG_PRESS_DISTANCE_LIMIT : float = 25.0
25 |
26 | const SWIPE_TIME_LIMIT : float = 0.5
27 | const SWIPE_DISTANCE_THRESHOLD : float = 200.0
28 |
29 | #########
30 | # CONST #
31 | #########
32 |
33 | const Util : GDScript = preload("Util.gd")
34 |
35 | const swipe2dir : Dictionary = \
36 | {
37 | "swipe_up" : Vector2.UP,
38 | "swipe_up_right" : Vector2.UP + Vector2.RIGHT,
39 | "swipe_right" : Vector2.RIGHT,
40 | "swipe_down_right" : Vector2.DOWN + Vector2.RIGHT,
41 | "swipe_down" : Vector2.DOWN,
42 | "swipe_down_left" : Vector2.DOWN + Vector2.LEFT,
43 | "swipe_left" : Vector2.LEFT,
44 | "swipe_up_left" : Vector2.UP + Vector2.LEFT
45 | }
46 |
47 |
48 | ###########
49 | # Signals #
50 | ###########
51 |
52 | signal touch
53 | signal drag
54 | signal single_tap
55 | signal single_touch
56 | signal single_drag
57 | signal single_swipe
58 | signal single_long_press
59 | signal multi_drag
60 | signal multi_tap
61 | signal multi_swipe
62 | signal multi_long_press
63 | signal pinch
64 | signal twist
65 | signal raw_gesture
66 | signal cancel
67 | signal any_gesture
68 |
69 | ########
70 | # Enum #
71 | ########
72 |
73 | enum Gesture {PINCH, MULTI_DRAG, TWIST, SINGLE_DRAG, NONE}
74 |
75 | ########
76 | # Vars #
77 | ########
78 |
79 | var raw_gesture_data : RawGesture = RawGesture.new() # Current raw_gesture
80 |
81 | var _mouse_event_press_position : Vector2
82 | var _mouse_event : int = Gesture.NONE
83 |
84 |
85 | var _drag_startup_timer : Timer = Timer.new()
86 | var _long_press_timer : Timer = Timer.new()
87 |
88 | var _single_touch_cancelled : bool = false
89 | var _single_drag_enabled : bool = false
90 |
91 | #############
92 | # Functions #
93 | #############
94 |
95 | func _ready() -> void:
96 | _add_timer(_drag_startup_timer, "_on_drag_startup_timer_timeout")
97 | _add_timer(_long_press_timer, "_on_long_press_timer_timeout")
98 |
99 | if DEFAULT_BINDIGS:
100 | _set_default_action("multi_swipe_up" , _native_key_event(KEY_I))
101 | _set_default_action("multi_swipe_up_right" , _native_key_event(KEY_O))
102 | _set_default_action("multi_swipe_right" , _native_key_event(KEY_L))
103 | _set_default_action("multi_swipe_down_right", _native_key_event(KEY_PERIOD))
104 | _set_default_action("multi_swipe_down" , _native_key_event(KEY_COMMA))
105 | _set_default_action("multi_swipe_down_left" , _native_key_event(KEY_M))
106 | _set_default_action("multi_swipe_left" , _native_key_event(KEY_J))
107 | _set_default_action("multi_swipe_up_left" , _native_key_event(KEY_U))
108 |
109 | _set_default_action("single_swipe_up" , _native_key_event(KEY_W))
110 | _set_default_action("single_swipe_up_right" , _native_key_event(KEY_E))
111 | _set_default_action("single_swipe_right" , _native_key_event(KEY_D))
112 | _set_default_action("single_swipe_down_right", _native_key_event(KEY_C))
113 | _set_default_action("single_swipe_down" , _native_key_event(KEY_X))
114 | _set_default_action("single_swipe_down_left" , _native_key_event(KEY_Z))
115 | _set_default_action("single_swipe_left" , _native_key_event(KEY_A))
116 | _set_default_action("single_swipe_up_left" , _native_key_event(KEY_Q))
117 |
118 | # _set_default_action("single_touch" , _native_mouse_button_event(MOUSE_BUTTON_LEFT))
119 | _set_default_action("multi_touch" , _native_mouse_button_event(MOUSE_BUTTON_MIDDLE))
120 | # _set_default_action("pinch" , _native_mouse_button_event(MOUSE_BUTTON_RIGHT)) # TODO
121 | _set_default_action("pinch_outward" , _native_mouse_button_event(MOUSE_BUTTON_WHEEL_UP))
122 | _set_default_action("pinch_inward" , _native_mouse_button_event(MOUSE_BUTTON_WHEEL_DOWN))
123 | _set_default_action("twist" , _native_mouse_button_event(MOUSE_BUTTON_RIGHT))
124 | # _set_default_action("twist_clockwise" , _native_mouse_button_event(MOUSE_BUTTON_WHEEL_UP)) # TODO
125 | # _set_default_action("twist_counterclockwise" , _native_mouse_button_event(MOUSE_BUTTON_WHEEL_DOWN)) # TODO
126 |
127 | func _unhandled_input(event : InputEvent) -> void:
128 | if event is InputEventScreenDrag:
129 | _handle_screen_drag(event)
130 | elif event is InputEventScreenTouch:
131 | _handle_screen_touch(event)
132 | elif event is InputEventMouseMotion:
133 | _handle_mouse_motion(event)
134 | else:
135 | _handle_action(event)
136 |
137 | func _handle_mouse_motion(event : InputEventMouseMotion) -> void:
138 | if raw_gesture_data.size() == 1 and _mouse_event == Gesture.SINGLE_DRAG:
139 | _emit("drag", _native_drag_event(0, event.position, event.relative, event.velocity))
140 | elif raw_gesture_data.size() == 2 and _mouse_event == Gesture.MULTI_DRAG:
141 | var offset = Vector2(5,5)
142 | var e0 = _native_drag_event(0, event.position-offset, event.relative, event.velocity)
143 | raw_gesture_data._update_screen_drag(e0)
144 | var e1 = _native_drag_event(1, event.position+offset, event.relative, event.velocity)
145 | raw_gesture_data._update_screen_drag(e1)
146 | _emit("multi_drag", InputEventMultiScreenDrag.new(raw_gesture_data,e0))
147 | _emit("multi_drag", InputEventMultiScreenDrag.new(raw_gesture_data,e1))
148 | elif _mouse_event == Gesture.TWIST:
149 | var rel1 = event.position - _mouse_event_press_position
150 | var rel2 = rel1 + event.relative
151 | var twist_event = InputEventScreenTwist.new()
152 | twist_event.position = _mouse_event_press_position
153 | twist_event.relative = rel1.angle_to(rel2)
154 | twist_event.fingers = 2
155 | _emit("twist", twist_event)
156 |
157 | func _handle_screen_touch(event : InputEventScreenTouch) -> void:
158 | if event.index < 0:
159 | _emit("cancel", InputEventScreenCancel.new(raw_gesture_data, event))
160 | _end_gesture()
161 | return
162 |
163 | # ignore orphaned touch release events
164 | if !event.pressed and not event.index in raw_gesture_data.presses:
165 | return
166 |
167 | raw_gesture_data._update_screen_touch(event)
168 | _emit("raw_gesture", raw_gesture_data)
169 | var index : int = event.index
170 | if event.pressed:
171 | if raw_gesture_data.size() == 1: # First and only touch
172 | _long_press_timer.start(LONG_PRESS_TIME_THRESHOLD)
173 | _single_touch_cancelled = false
174 | _emit("single_touch", InputEventSingleScreenTouch.new(raw_gesture_data))
175 | elif !_single_touch_cancelled :
176 | _single_touch_cancelled = true
177 | _cancel_single_drag()
178 | _emit("single_touch", InputEventSingleScreenTouch.new(raw_gesture_data))
179 | else:
180 | var fingers : int = raw_gesture_data.size()
181 | if index == 0:
182 | _emit("single_touch", InputEventSingleScreenTouch.new(raw_gesture_data))
183 | if !_single_touch_cancelled:
184 | var distance : float = (raw_gesture_data.releases[0].position - raw_gesture_data.presses[0].position).length()
185 | if raw_gesture_data.elapsed_time < TAP_TIME_LIMIT and distance <= TAP_DISTANCE_LIMIT:
186 | _emit("single_tap", InputEventSingleScreenTap.new(raw_gesture_data))
187 | if raw_gesture_data.elapsed_time < SWIPE_TIME_LIMIT and distance > SWIPE_DISTANCE_THRESHOLD:
188 | _emit("single_swipe", InputEventSingleScreenSwipe.new(raw_gesture_data))
189 | if raw_gesture_data.active_touches == 0: # last finger released
190 | if _single_touch_cancelled:
191 | var distance : float = (raw_gesture_data.centroid("releases","position") - raw_gesture_data.centroid("presses","position")).length()
192 | if raw_gesture_data.elapsed_time < TAP_TIME_LIMIT and distance <= TAP_DISTANCE_LIMIT and\
193 | raw_gesture_data.is_consistent(TAP_DISTANCE_LIMIT, FINGER_SIZE*fingers) and\
194 | _released_together(raw_gesture_data, MULTI_FINGER_RELEASE_THRESHOLD):
195 | _emit("multi_tap", InputEventMultiScreenTap.new(raw_gesture_data))
196 | if raw_gesture_data.elapsed_time < SWIPE_TIME_LIMIT and distance > SWIPE_DISTANCE_THRESHOLD and\
197 | raw_gesture_data.is_consistent(FINGER_SIZE, FINGER_SIZE*fingers) and\
198 | _released_together(raw_gesture_data, MULTI_FINGER_RELEASE_THRESHOLD):
199 | _emit("multi_swipe", InputEventMultiScreenSwipe.new(raw_gesture_data))
200 | _end_gesture()
201 | _cancel_single_drag()
202 |
203 | func _handle_screen_drag(event : InputEventScreenDrag) -> void:
204 | if event.index < 0:
205 | _emit("cancel", InputEventScreenCancel.new(raw_gesture_data, event))
206 | _end_gesture()
207 | return
208 |
209 | raw_gesture_data._update_screen_drag(event)
210 | _emit("raw_gesture", raw_gesture_data)
211 | if raw_gesture_data.drags.size() > 1:
212 | _cancel_single_drag()
213 | var gesture : int = _identify_gesture(raw_gesture_data)
214 | if gesture == Gesture.PINCH:
215 | _emit("pinch", InputEventScreenPinch.new(raw_gesture_data, event))
216 | elif gesture == Gesture.MULTI_DRAG:
217 | _emit("multi_drag", InputEventMultiScreenDrag.new(raw_gesture_data, event))
218 | elif gesture == Gesture.TWIST:
219 | _emit("twist",InputEventScreenTwist.new(raw_gesture_data, event))
220 | else:
221 | if _single_drag_enabled:
222 | _emit("single_drag", InputEventSingleScreenDrag.new(raw_gesture_data))
223 | else:
224 | if _drag_startup_timer.is_stopped(): _drag_startup_timer.start(DRAG_STARTUP_TIME)
225 |
226 | func _handle_action(event : InputEvent) -> void:
227 | if InputMap.has_action("single_touch") and (event.is_action_pressed("single_touch") or event.is_action_released("single_touch")):
228 | _emit("touch", _native_touch_event(0, get_viewport().get_mouse_position(), event.pressed))
229 | if event.pressed:
230 | _mouse_event = Gesture.SINGLE_DRAG
231 | else:
232 | _mouse_event = Gesture.NONE
233 | elif InputMap.has_action("multi_touch") and (event.is_action_pressed("multi_touch") or event.is_action_released("multi_touch")):
234 | _emit("touch", _native_touch_event(0, get_viewport().get_mouse_position(), event.pressed))
235 | _emit("touch", _native_touch_event(1, get_viewport().get_mouse_position(), event.pressed))
236 | if event.pressed:
237 | _mouse_event = Gesture.MULTI_DRAG
238 | else:
239 | _mouse_event = Gesture.NONE
240 | elif InputMap.has_action("twist") and (event.is_action_pressed("twist") or event.is_action_released("twist")):
241 | _mouse_event_press_position = get_viewport().get_mouse_position()
242 | if event.pressed:
243 | _mouse_event = Gesture.TWIST
244 | else:
245 | _mouse_event = Gesture.NONE
246 | elif (InputMap.has_action("pinch_outward") and event.is_action_pressed("pinch_outward")) or (InputMap.has_action("pinch_inward") and event.is_action_pressed("pinch_inward")):
247 | var pinch_event = InputEventScreenPinch.new()
248 | pinch_event.fingers = 2
249 | pinch_event.position = get_viewport().get_mouse_position()
250 | pinch_event.distance = 400
251 | pinch_event.relative = 40
252 | if event.is_action_pressed("pinch_inward"):
253 | pinch_event.relative *= -1
254 | _emit("pinch", pinch_event)
255 | else:
256 | var swipe_emulation_dir : Vector2 = Vector2.ZERO
257 | var is_single_swipe : bool
258 | for swipe in swipe2dir:
259 | var dir = swipe2dir[swipe]
260 | if InputMap.has_action("single_"+swipe) and event.is_action_pressed("single_"+swipe):
261 | swipe_emulation_dir = dir
262 | is_single_swipe = true
263 | break
264 | if InputMap.has_action("multi_"+swipe) and event.is_action_pressed("multi_"+swipe):
265 | swipe_emulation_dir = dir
266 | is_single_swipe = false
267 | break
268 |
269 | if swipe_emulation_dir != Vector2.ZERO:
270 | var swipe_event
271 | if is_single_swipe:
272 | swipe_event = InputEventSingleScreenSwipe.new()
273 | else:
274 | swipe_event = InputEventMultiScreenSwipe.new()
275 | swipe_event.fingers = 2
276 | swipe_event.position = get_viewport().get_mouse_position()
277 | swipe_event.relative = swipe_emulation_dir*SWIPE_DISTANCE_THRESHOLD*2
278 | if is_single_swipe:
279 | _emit("single_swipe", swipe_event)
280 | else:
281 | _emit("multi_swipe", swipe_event)
282 |
283 | # Emits signal sig with the specified args
284 | func _emit(sig : String, val : InputEvent) -> void:
285 | if DEBUG: print(val.as_text())
286 | emit_signal("any_gesture", sig, val)
287 | emit_signal(sig, val)
288 | Input.parse_input_event(val)
289 |
290 |
291 | # Disables drag and stops the drag enabling timer
292 | func _cancel_single_drag() -> void:
293 | _single_drag_enabled = false
294 | _drag_startup_timer.stop()
295 |
296 | func _released_together(_raw_gesture_data : RawGesture, threshold : float) -> bool:
297 | _raw_gesture_data = _raw_gesture_data.rollback_relative(threshold)[0]
298 | return _raw_gesture_data.size() == _raw_gesture_data.active_touches
299 |
300 | # Checks if the gesture is pinch
301 | func _identify_gesture(_raw_gesture_data : RawGesture) -> int:
302 | var center : Vector2 = _raw_gesture_data.centroid("drags","position")
303 |
304 | var sector : int = -1
305 | for e in _raw_gesture_data.drags.values():
306 | var adjusted_position : Vector2 = center - e.position
307 | var raw_angle : float = fmod(adjusted_position.angle_to(e.relative) + (PI/4), TAU)
308 | var adjusted_angle : float = raw_angle if raw_angle >= 0 else raw_angle + TAU
309 | var e_sector : int = int(floor(adjusted_angle / (PI/2)))
310 | if sector == -1:
311 | sector = e_sector
312 | elif sector != e_sector:
313 | return Gesture.MULTI_DRAG
314 |
315 | if sector == 0 or sector == 2:
316 | return Gesture.PINCH
317 | else: # sector == 1 or sector == 3:
318 | return Gesture.TWIST
319 |
320 | func _on_drag_startup_timer_timeout() -> void:
321 | _single_drag_enabled = raw_gesture_data.drags.size() == 1
322 |
323 | func _on_long_press_timer_timeout() -> void:
324 | var ends_centroid : Vector2 = Util.centroid(raw_gesture_data.get_ends().values())
325 | var starts_centroid : Vector2 = raw_gesture_data.centroid("presses", "position")
326 | var distance : float = (ends_centroid - starts_centroid).length()
327 |
328 | if raw_gesture_data.releases.is_empty() and distance <= LONG_PRESS_DISTANCE_LIMIT and\
329 | raw_gesture_data.is_consistent(LONG_PRESS_DISTANCE_LIMIT, FINGER_SIZE*raw_gesture_data.size()):
330 | if _single_touch_cancelled:
331 | _emit("multi_long_press", InputEventMultiScreenLongPress.new(raw_gesture_data))
332 | else:
333 | _emit("single_long_press", InputEventSingleScreenLongPress.new(raw_gesture_data))
334 |
335 |
336 | func _end_gesture() -> void:
337 | _single_drag_enabled = false
338 | _long_press_timer.stop()
339 | raw_gesture_data = RawGesture.new()
340 |
341 | # create a native touch event
342 | func _native_touch_event(index : int, position : Vector2, pressed : bool) -> InputEventScreenTouch:
343 | var native_touch : InputEventScreenTouch = InputEventScreenTouch.new()
344 | native_touch.index = index
345 | native_touch.position = position
346 | native_touch.pressed = pressed
347 | return native_touch
348 |
349 | # create a native touch event
350 | func _native_drag_event(index : int, position : Vector2, relative : Vector2, velocity : Vector2) -> InputEventScreenDrag:
351 | var native_drag : InputEventScreenDrag = InputEventScreenDrag.new()
352 | native_drag.index = index
353 | native_drag.position = position
354 | native_drag.relative = relative
355 | native_drag.velocity = velocity
356 | return native_drag
357 |
358 | func _native_mouse_button_event(button : int) -> InputEventMouseButton:
359 | var ev = InputEventMouseButton.new()
360 | ev.button_index = button
361 | return ev
362 |
363 | func _native_key_event(key : int) -> InputEventKey:
364 | var ev = InputEventKey.new()
365 | ev.keycode = key
366 | return ev
367 |
368 | func _set_default_action(action : String, event : InputEvent) -> void:
369 | if !InputMap.has_action(action):
370 | InputMap.add_action(action)
371 | InputMap.action_add_event(action,event)
372 |
373 | # Macro to add a timer and connect it's timeout to func_name
374 | func _add_timer(timer : Timer, func_name : String) -> void:
375 | timer.one_shot = true
376 | if func_name:
377 | timer.connect("timeout", Callable(self, func_name))
378 | self.add_child(timer)
379 |
--------------------------------------------------------------------------------