0
\$\begingroup\$

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;
        }
New contributor
Cooper Rutan is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
1
\$\begingroup\$

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.

enter image description here

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;
    }
}

enter image description here

| improve this answer | |
New contributor
Mika Notarnicola is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
  • \$\begingroup\$ Thank you, I might try using something similar in my code \$\endgroup\$ – Cooper Rutan Nov 21 at 23:59
  • \$\begingroup\$ Thank you! This is very useful for converting a point on the screen into a point in world space. \$\endgroup\$ – Cooper Rutan Nov 22 at 1:03
0
\$\begingroup\$

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;
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Cooper Rutan Nov 21 at 23:58
  • \$\begingroup\$ 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. \$\endgroup\$ – DMGregory Nov 22 at 0:22
  • \$\begingroup\$ 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? \$\endgroup\$ – Cooper Rutan Nov 22 at 0:25
  • \$\begingroup\$ Depth would be Vector3.Dot(positionOnBoard - camera.transform.position, camera.transform.forward) in general. \$\endgroup\$ – DMGregory Nov 22 at 0:26
  • \$\begingroup\$ OH! I like the idea of using a dot product! Thank you! \$\endgroup\$ – Cooper Rutan Nov 22 at 0:30

Your Answer

Cooper Rutan is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.