# Touch control pan 3D map

I'm working on recreating the board game "Hex" as a mobile game. I'm working on implementing zoom and pan features using two fingers. I feel like I managed to get the zoom to work, but the pan moves more than I want it to and I feel like it is because I'm missing something and must be somewhere in the conversion to World Space coordinates. if I could get some help it would be greatly appreciated.

For reference, oldMidpoint is initialized to 0 earlier on.

        Touch touchA = Input.GetTouch(0);
Touch touchB = Input.GetTouch(1);

Vector3 midpoint = (touchA.position + touchB.position) / 2;
if(midpoint != oldMidpoint)
{
if(oldMidpoint == Vector3.zero)
oldMidpoint = midpoint;

Vector3 moveDistance = oldMidpoint - midpoint;
moveDistance.y = 0;

Camera.main.transform.Translate(moveDistance, Space.World);

oldMidpoint = midpoint;
}


I believe what you're looking for is what I refer to as "Google Maps" style panning. This is where the pointer stays perfectly locked on the exact place in the world where you started the pan.

Like @DMGregory mentioned, you need to do everything in world space. You start by caching the pointer's position in world space when the drag begins. Then every frame while dragging, calculate a vector from the pointer's current world position back to its starting world position. Then offset the camera's world position by that amount. You can think of it as constantly moving the camera such that the cursor's world position stays fixed.

Here's a simple implementation. Obviously you won't use the mouse, but as long as you're panning based off of a single point (in your case the average of two touches) it should work just the same:

public class CameraPan : MonoBehaviour
{
private bool _isDragging;

private Vector3 _pointerWorldStart;

private static Vector3 GetPointerWorld()
{
var plane = new Plane(Vector3.up, Vector3.zero);
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
plane.Raycast(ray, out var enter);
var pointerWorldCurrent = ray.GetPoint(enter);
return pointerWorldCurrent;
}

private void Update()
{
if (Input.GetMouseButtonDown(0))
{
_pointerWorldStart = GetPointerWorld();
}
_isDragging = Input.GetMouseButton(0);

if (!_isDragging)
{
return;
}

var pointerWorldCurrent = GetPointerWorld();
transform.position += _pointerWorldStart - pointerWorldCurrent;
}
}


• Thank you, I might try using something similar in my code – Cooper Rutan Nov 21 '20 at 23:59
• Thank you! This is very useful for converting a point on the screen into a point in world space. – Cooper Rutan Nov 22 '20 at 1:03

Your moveDistance is measured in screen pixels. If you want to convert that to a world space unit, you need a function a bit like this:

float ScreenToWorldDistance(float screenDistance, Camera camera, float depth) {
float visibleHeightAtDepth;

if (camera.orthographic) {
visibleHeightAtDepth = 2f * camera.orthographicSize;
} else {
visibleHeightAtDepth = 2f * depth * Mathf.Tan(Mathf.Deg2Rad * camera.fieldOfView/2f);
}

return visibleHeightAtDepth * screenDistance/Screen.height;
}

• Thank you for your answer! I've implemented it and it seems to be working, what I don't quite understand is what "depth" is supposed to be and what "visibleheightAtDepth" is therefor supposed to represent. – Cooper Rutan Nov 21 '20 at 23:58
• Depth is the distance from your camera to to point on the map that you're dragging, in world space. Visible Height refers to how tall the camera's vertical field of view is that far out, since perspective cameras have a frustum that gets wider as distance from the camera increases. – DMGregory Nov 22 '20 at 0:22
• So, if my camera is pointed directly at the board and the board is at y=0, would depth be the y component of the camera's position? Or am I just completely misunderstanding what you are saying? I tried using the camera's y position, but it seemed to give me the opposite problem? – Cooper Rutan Nov 22 '20 at 0:25
• Depth would be Vector3.Dot(positionOnBoard - camera.transform.position, camera.transform.forward) in general. – DMGregory Nov 22 '20 at 0:26
• OH! I like the idea of using a dot product! Thank you! – Cooper Rutan Nov 22 '20 at 0:30