Touch CameraController


I was looking for some code that will move a camera with the finger, and use the pinch to zoom, in the web i found some snippet of code but the were lacking some functionality that I wanted

I hope this guide will help you understand the basic idea so you can tweak it to fit your game

(The full code snippet is at the end)

Basic setup

I needed an object to move around, and a camera for zooming. In my case I move the camera instead of the object, so all transformation are done to the camera. The same result could have been achieved applying the transform to the object with the sign inverted

Drag to move

Godot give us the InputEventScreenDrag that is launched when the application detect a drag movement on the screen

Knowing that in the func _unhandled_input(event) (you can use any input function provided) we can verify the type of event to be InputEventScreenDrag, in that case the attribute relative come in handy since it store the delta distance of this drag event. We just need to add that value to the object we want to drag and we are done. (in my case i remove it from the camera but the resulting effect is the same)

class_name CameraController extends Camera2D

func _unhandled_input(event):
	if event is InputEventScreenDrag:
		offset -= event.relative * zoom

Relative distance needs to be multiplied by the zoom level of the camera to have the drag consistent with the movement of the object

Pinch to zoom

For the zoom we need to do some more steps, in the web i found solution that were zooming toward the center of the screen, and they did not satisfy me enough, after some research and reading I got a grasp of what I needed to do

Step1 - Zoom

The first thing is to understand when I have two finger touching the screen. I used a dictionary to store how many finger are touching the screen, and store the position of the finger as the value, this is a cached position and will be used for computing the distance between event

var touch_points: Dictionary = {}

func _unhandled_input(event):
	if event is InputEventScreenTouch:
		_handle_touch(event)
	elif event is InputEventScreenDrag:
		_handle_drag(event)

func _handle_touch(event: InputEventScreenTouch):
	if event.pressed:
		touch_points[event.index] = event.position
	else:
		touch_points.erase(event.index)

func _handle_drag(event):
	if touch_points.size() == 1:
		offset -= event.relative * zoom
		touch_points[event.index] = event.position
	elif touch_points.size() == 2:
		# here we will focus on the zoom 

With this the drag should work like before, but if you use 2 finger it should stop.

Now lets focus on the zoom The core concept that I failed to grasp at the start was to imagine just one finger moving, with this you can better visualize what you want to happen

I computed the old_vector, that is the vector formed under the 2 finger before the screen drag is launched to do that we use the value stored in the point dictionary I computed the new_vector, using the pivot point (the finger that didn’t move) and the actual position of the event (finger that moved)

And use the ratio between this 2 vector length to increase the zoom level

At last store the new position of the finger in the dictionary for future computation

	elif touch_points.size() == 2:
		# Find the index of the not moving
		var pivot_index = 1 if event.index == 0 else 0

		# get the 3 point involved
		var pivot_point: Vector2 = touch_points[pivot_index]
		var old_point: Vector2 = touch_points[event.index]
		var new_point: Vector2 = event.position

		var old_vector: Vector2 = old_point - pivot_point
		var new_vector: Vector2 = new_point - pivot_point

		var delta_scale = new_vector.length() / old_vector.length()
		zoom *= delta_scale
		touch_points[event.index] = new_point

This will zoom right at the center of the camera, but I wanted to zoom in between the finger

Step2 - Repositioning

This is the part that I wasn’t able to implement like I wanted until I started to use the pivot_point and think at these transformation like one finger moving and the other still

Once I understood that the problem became much simpler and instead of moving the camera towards the center point between the 2 finger, I just needed to move the camera with the finger

The movement need to be halved since we want to move in a 1 to n ration where n is the finger count, we should not move at the same speed of the finger

		...
		zoom *= delta_scale
		touch_points[event.index] = new_point
		
		var drag_vector: Vector2 = event.relative
		offset -= drag_vector / 2 * zoom

If 2 finger move together to one direction this will be balanced by the 2 half will making it a full drag movement

Conclusion

This is the code I used to handle the zoom and drag with touch screen for the minesweeper, if you just want to copy paste and see if it work

class_name CameraController extends Camera2D

var touch_points: Dictionary = {}

func _unhandled_input(event):
	if event is InputEventScreenTouch:
		_handle_touch(event)
	elif event is InputEventScreenDrag:
		_handle_drag(event)

func _handle_touch(event: InputEventScreenTouch):
	if event.pressed:
		touch_points[event.index] = event.position
	else:
		touch_points.erase(event.index)

func _handle_drag(event: InputEventScreenDrag):
	if touch_points.size() == 1:
		offset -= event.relative / zoom.x
		touch_points[event.index] = event.position

	if touch_points.size() == 2:
		# Find the index of the not moving
		var pivot_index = 1 if event.index == 0 else 0

		# get the 3 point involved
		var pivot_point: Vector2 = touch_points[pivot_index]
		var old_point: Vector2 = touch_points[event.index]
		var new_point: Vector2 = event.position

		var old_vector: Vector2 = old_point - pivot_point
		var new_vector: Vector2 = new_point - pivot_point
		
		var delta_scale = new_vector.length() / old_vector.length()
		zoom *= delta_scale
		touch_points[event.index] = new_point
		
		var drag_vector: Vector2 = event.relative
		offset -= drag_vector / 2 * zoom

I will try and add the rotation in the future

Any suggestion is appreciated

Resource that I used: Making a Pixel Art Editor?? (pan/zoom touch controls in Godot 4) [pt. 1] Creating a Flexible Touch Screen Camera Controller In Godot | Basics With Godot Pinch Zoom and Panning in Godot for a Mobile Game*

Get Minesweeper

Download NowName your own price

Comments

Log in with itch.io to leave a comment.

Hey, thank you so much for sharing this code. It helped me so much on my projects. I had to change the last two lines of your code to get the movement to work smoothly when zooming though. For anyone else that wants to use this with a more smooth pan while they are pinching try these lines:

var drag_vector: Vector2 = event.relative / zoom.x

offset -= drag_vector / 2

Cheers, Dan

Thank you for the improvement.

Happy that it helped ;)