how to make finger-tracking control?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By AndrewD

How can I get the player to “follow” the movements of the finger and repeat them, but if the finger is too fast, then the player flies to the finger at a certain speed?

(An example is “Sky Force 2014”)

:bust_in_silhouette: Reply From: njamster

Disclaimer: I never played “Sky Force 2014”, so this might or might not be what you want. But either way it should help you to understand the general approach.

Attach this script to a sprite representing your player (if you want to use something else, e.g. a KinematicBody2D you’ll need to adapt the script, but it should be fairly obvious where and none of the required changes are complicated):

extends Sprite

const SPEED = 300 # pixel per second
const MAX_TRACKING_DISTANCE = 400 # pixel

var screen_touch = false
var path = []

func _unhandled_input(event):
	if event is InputEventMouseButton and event.button_index == BUTTON_LEFT:
		screen_touch = event.pressed
		if screen_touch:
			add_to_path(event.global_position)
	elif event is InputEventMouseMotion and screen_touch:
		add_to_path(event.global_position)


func add_to_path(position):
    var distance = global_position.distance_to(position)
	if  distance > MAX_TRACKING_DISTANCE:
		path.clear()
	path.push_back(position)


func _physics_process(delta):
	if path.empty(): return
	var next_pos = path.front()

	var direction = global_position.direction_to(next_pos)
	var distance = global_position.distance_to(next_pos)
	var reach = SPEED * delta

	if distance > reach:
		# next_pos is out of reach in this frame
		# => move as far as possible towards it!
		global_position += direction * reach
	elif distance < reach:
		# next_pos is reachable in this frame
		# =>  move there and remove it from the path
		while distance < reach:
			global_position += direction * distance
			reach -= distance
			path.pop_front()

			# check if theres another position in the path
			# that we can already move towards
			if path.empty(): return
			next_pos = path.front()
			direction = global_position.direction_to(next_pos)
			distance = global_position.distance_to(next_pos)

I have incorrectly explained what I need, I’m sorry.
I need the player to simply move towards the finger, or repeat his movements from his position.
for example: the player is in position 4; 2 (X; Y) and the finger touched the screen at point 7; 5. The player does not move.
the finger moved 2 by X and 8 by Y. The player also moved from its place to 2 by X and 8 by Y like a finger.
it turns out that the finger is at 9; 13 and the player is at 6; 10.

AndrewD | 2020-03-31 11:09

Alright, I’m confused…

I need the player to simply move towards the finger, or repeat his movements from his position.

Which of the two options do you want?

for example: the player is in position 4; 2 (X; Y) and the finger touched the screen at point 7; 5. The player does not move.

According to your example, the player will not simply move towards the finger. In fact it will do nothing at all if the screen is touched for the first time?!

the finger moved 2 by X and 8 by Y. The player also moved from its place to 2 by X and 8 by Y like a finger.

So it’s simply mirroring the finger motion from it’s local point of view. Like my code does! If you don’t want to move the player on the first touch event, all you have to do is remove the first call to add_to_path in _unhandled_input.

njamster | 2020-03-31 18:29

Well, I understood how to configure the first control option . I just needed to give MAX_TRACKING_DISTANCE a small value.

And the second option I could not explain, because my English is very bad, and I wrote through Google translate (and still write). In any case, I uploaded a video on YouTube with an example of the second control option.

https://youtu.be/qz-Jr_GS1IY

AndrewD | 2020-04-01 06:38

Try this:

extends Sprite

const SPEED = 50 # pixel per second
const MAX_TRACKING_DISTANCE = 200 # pixel
const SMALLEST_REGISTERED_OFFSET = 1.0

var screen_touch = false
var move_by = Vector2.ZERO
var path = []

func _unhandled_input(event):
	if event is InputEventMouseButton and event.button_index == BUTTON_LEFT:
		screen_touch = event.pressed
	elif event is InputEventMouseMotion and screen_touch:
		var distance = global_position.distance_to(event.global_position)
		if distance <= MAX_TRACKING_DISTANCE:
			if event.relative.length() >= SMALLEST_REGISTERED_OFFSET:
				path.push_back(event.relative)
		else:
			path.clear()
			path.push_back(global_position.direction_to(event.global_position) * distance)
	else:
		path.clear()


func _physics_process(delta):
	if path.empty(): return
	var next_offset = path.front()

	var direction = next_offset.normalized()
	var distance = next_offset.length()
	var reach = SPEED * delta

	if distance > reach:
		global_position += direction * reach
		path[0] *= 1-reach/distance
	else:
		while distance <= reach:
			global_position += direction * distance
			reach -= distance
			path.pop_front()

			if path.empty(): return
			next_offset = path.front()
			direction = next_offset.normalized()
			distance = next_offset.length()

		if distance > reach:
			global_position += direction * reach
			path[0] *= 1-reach/distance

njamster | 2020-04-01 12:46

thank you very much!
But, for some reason, touches are not read on the ColorRect which I use as the background.
Why is this happening?

AndrewD | 2020-04-01 16:03

But, for some reason, touches are not read on the ColorRect which I use as the background.

In the inspector, set the mouse_filter-property of your ColorRect to “Ignore”. Otherwise it will consume the mouse-events before they reach your node.

njamster | 2020-04-01 17:29

and for some reason, the second option control works on the PC from the mouse, but does not work on the phone from the touch. At the same time, mouse emulation from touching is enabled.

AndrewD | 2020-04-02 06:53

works on the PC from the mouse, but does not work on the phone from the touch

I cannot help you with that, sorry. I’ve never exported any project to a phone so far. However, try replacing InputEventMouseButton with InputEventScreenTouch and InputEventMouseMotion with InputEventScreenDrag - maybe it helps.

njamster | 2020-04-02 09:59

I did it. But at the same time, in the function func _unhandled_input(event):, I had to replace event.global_position with event.position
and the line path.push_back (global_position.direction_to (event.global_position) * distance)
with path.push_back (position.direction_to (event.position) * distance).
Now the player just doesn’t move.

AndrewD | 2020-04-02 11:55