Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Issue with converting screen touch to tile map position when camera is rotated

Discussion in 'Scripting' started by ManjitSBedi, Dec 6, 2021.

  1. ManjitSBedi

    ManjitSBedi

    Joined:
    Mar 8, 2014
    Posts:
    58
    The issue is a touch point is not being mapped to a correct tile map position as Vector3Int. I am working on a scene that:
    - has a player controlled vehicle displayed over a hexagonal tile map using the Unity tile map package
    - using the newer Unity input manager to get a touch position on a finger up event
    - the player is moving a vehicle between the current position and tile map position with drag gesture from the player position to a tile position
    - the touch is converted to a world position:
    Code (CSharp):
    1. Vector3 ConvertTouchToWorldSpace(Vector2 screenPosition)
    2. {
    3.      Vector3 temp = screenPosition;
    4.      // Need to use a non zero value for the z pos for the conversion to work!
    5.      temp.z = -mainCamera.transform.position.z;
    6.      return mainCamera.ScreenToWorldPoint(temp);
    7. }
    - the world position is then mapped to a hexagonal tile map to get the tile position of the tile that the player has touched on:
    Code (CSharp):
    1. Vector3 touchPosition = ConvertTouchToWorldSpace(finger.screenPosition);
    2. Vector3Int targetCellPos = tilemap.WorldToCell(touchPosition);
    - there is a follow camera using Cinemachine in the scene which is updating the main camera transform
    - when there is a rotation on the camera and the touch position is further away from the virtual camera position on the x or y, the coordinate conversion is pointing to the wrong tile. In other words, the player is tapping further away from the player's current position this issue will happen.
    - when there is no rotation on the camera, the tile position is correct; note: I needed to change the distance on the Z to see the same position when the camera is rotated when testing the issue.

    cinemachine follow camera.png cinemachine follow camera with rotation.png
    Here is some debug captured when running the scene:
    • with rotation on camera, the z value goes negative
      • target pos (7.5, 0.4, -0.5), tile pos (0, 10, 0), tile
    • with no camera rotation
      • target pos (7.5, 0.6, 0.0), tile pos (1, 10, 0), tile Available

    How can I resolve this ? Am I doing some wrong in not dealing with the camera rotation somewhere?
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    While this may be true there's an actual deeper reason you can read about in the documentation.

    That Z component says "give me the world component that is Z units into the scene from this camera." You can see why it may work right now, but if the camera rotates another direction, +Z may no longer going to be where you expect.

    You can also raycast against tile colliders, and by using layers you can set it up to only hit what you want.

    Alternately you can raycast against a mathematical Plane object colocated with your tiles and see which tile is closest to the hit point.

    Raycasting, colliders, planes, Plane, etc:

    https://forum.unity.com/threads/hel...s-are-hitting-an-object.1058393/#post-6840296

    https://forum.unity.com/threads/kee...within-the-view-frustrum.936209/#post-6117173

    https://forum.unity.com/threads/ran...ear-polygon-collider-2d.1060082/#post-6851372
     
  3. ManjitSBedi

    ManjitSBedi

    Joined:
    Mar 8, 2014
    Posts:
    58
    Thanks for the fast reply; I did wonder if might need to try using raycast with a collider on the tiles in the tile map.
     
  4. ManjitSBedi

    ManjitSBedi

    Joined:
    Mar 8, 2014
    Posts:
    58
    I had been working on other tasks and have come back to this now. I have been reading up on using raycasts.

    Meanwhile here is some code that has been adapted from a post elsewhere
    https://gamedev.stackexchange.com/questions/175522/get-the-tile-in-unity-tile-map

    Code (CSharp):
    1.     /// <summary>
    2.     /// Perform a raycast in the z direction (forward).
    3.     /// </summary>
    4.     /// <param name="touchPos"></param>
    5.     private void RaycastUsingPlane(Vector3 touchPos)
    6.     {
    7.         // create a ray aimed forward using the camera direction
    8.         // TODO: There is not working when the camera is rotated on the x axis.
    9.         var direction = mainCamera.transform.forward;
    10.         Ray ray = new Ray(touchPos, direction);
    11.         var plane = new Plane(Vector3.back, Vector3.zero);
    12.         float hitDist;
    13.         plane.Raycast(ray, out hitDist);
    14.         var point = ray.GetPoint(hitDist);
    15.         var tilePos = tilemap.WorldToCell(point);
    16.  
    17.         var tile = tilemap.GetTile(tilePos);
    18.         if (tile != null)
    19.         {
    20.             var text = $"RaycastUsingPlane, Pos {tilePos}, Tile '{tile.name}'";
    21.             debugText.text = text;
    22.             var adjustedPos = tilemap.CellToWorld(tilePos);
    23.             quad.transform.position = adjustedPos;
    24.         }
    25.     }
    26.  
    I updated the code to use a different raycast & this might be the solution; I need to do more testing

    Code (CSharp):
    1.     private void RaycastUsingScreenPoint(Vector2 touchPos)
    2.     {
    3.         float hit;
    4.         var plane = new Plane(Vector3.back, Vector3.zero);
    5.  
    6.         Ray ray = mainCamera.ScreenPointToRay(new Vector3(touchPos.x, touchPos.y, 0f));
    7.         if (plane.Raycast(ray, out hit))
    8.         {
    9.             var point = ray.GetPoint(hit);
    10.             var tilePos = tilemap.WorldToCell(point);
    11.  
    12.             var tile = tilemap.GetTile(tilePos);
    13.             if (tile != null)
    14.             {
    15.                 var text = $"RaycastUsingPlane, Pos {tilePos}, Tile '{tile.name}'";
    16.                 debugText.text = text;
    17.                 var adjustedPos = tilemap.CellToWorld(tilePos);
    18.                 quad.transform.position = adjustedPos;
    19.             }
    20.         }
    21.     }
     
    Last edited: Dec 24, 2021