Search Unity

ScreenToWorldPoint and ViewportToWorldPoint don't return the correct position

Discussion in 'Scripting' started by jensmcatanho, Nov 25, 2014.

  1. jensmcatanho

    jensmcatanho

    Joined:
    Jul 11, 2014
    Posts:
    21
    Hello, guys. I've been facing some problems using these two methods because neither of them are returning the correct position.

    For example, these two lines of code give me almost the same position represented on the screen below.
    Code (CSharp):
    1. transform.position = mainCamera.ScreenToWorldPoint (new Vector3 (Screen.width, Screen.height * 0.066f, 30.73041f));
    2. transform.position = mainCamera.ViewportToWorldPoint (new Vector3 (1, 0, 30.73041f));


    When it should be closer to the edge of the mainCamera.. And another example:
    Code (CSharp):
    1. topWall.center = new Vector2 (0f, mainCamera.ScreenToWorldPoint (new Vector3 (0f, Screen.height, 0f)).y + 0.5f);
    2. leftWall.center = new Vector2 (mainCamera.ScreenToWorldPoint (new Vector3 (0f, 0f, 0f)).x - 0.5f, 0f);
    3. rightWall.center = new Vector2 (mainCamera.ScreenToWorldPoint (new Vector3 (Screen.width, 0f, 0f)).x + 0.5f, 0f);
    It should give me positions outside of the mainCamera range but instead..



    Can someone help me, please?
     
  2. Stoven

    Stoven

    Joined:
    Jul 28, 2014
    Posts:
    171
    Try using this script on the Camera to see if it draws a rectangle around the Camera even if it isn't selected.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. namespace _AddedEffects
    6. {
    7.     [ExecuteInEditMode]
    8.     public class DrawCameraBounds : MonoBehaviour
    9.     {
    10.         private Camera ourCamera;
    11.         public Color ourColor = Color.green;
    12.  
    13.         void OnDrawGizmos()
    14.         {
    15.             if(ourCamera != null && enabled)
    16.             {
    17.                 Rect pixelRect = ourCamera.pixelRect;
    18.                 Gizmos.color = ourColor;
    19.  
    20.                 // the Screen coordinates map to the normal cartesian plane, starting from the bottom-left corner of the screen
    21.                 Vector2 bottomLeftPoint = ourCamera.ScreenToWorldPoint(new Vector2(0f, 0f));
    22.                 Vector2 bottomRightPoint = ourCamera.ScreenToWorldPoint(new Vector2(pixelRect.width, 0));
    23.                 Vector2 topLeftPoint = ourCamera.ScreenToWorldPoint(new Vector2(0f, pixelRect.height));
    24.                 Vector2 topRightPoint = ourCamera.ScreenToWorldPoint(new Vector2(pixelRect.width, pixelRect.height));
    25.  
    26.                 Gizmos.DrawLine(topLeftPoint, topRightPoint);
    27.                 Gizmos.DrawLine(topRightPoint, bottomRightPoint);
    28.                 Gizmos.DrawLine(bottomRightPoint, bottomLeftPoint);
    29.                 Gizmos.DrawLine(bottomLeftPoint, topLeftPoint);
    30.             }
    31.         }
    32.  
    33.         // Use this for initialization
    34.         void Start()
    35.         {
    36.             ourCamera = GetComponent<Camera>();
    37.         }
    38.     }
    39. }
    40.  
    Your first 2 lines of code wont be the same. The first line will always be higher than the 2nd line because you're multiplying the height by the value 0.066f. Both the Viewport and Screen points are relative to normal Cartesian coordinates, which means (0, 0) the origin of the Cartesian axis is the bottom-left of the Camera's view.

    What exactly are topWall, leftWall and rightWall references of?
     
  3. jensmcatanho

    jensmcatanho

    Joined:
    Jul 11, 2014
    Posts:
    21
    The scripted worked as it should.

    topWall, leftWall and rightWall are of type BoxCollider2D and they represent the limit of the camera sight so nothing would go out of the player's view.

    Yeah, I know that, but the problem is that if I change the first code to transform.position = mainCamera.ScreenToWorldPoint (new Vector2 (0f, 0f)); it doesn't put the game object at the bottom-left of the Camera's view.



    That's what happens when I put it on (0, 0).
     
  4. Stoven

    Stoven

    Joined:
    Jul 28, 2014
    Posts:
    171
    BoxCollider2D.center is a local-space coordinate relative to the object it resides upon. You're trying to assign a World Coordinate relative to the Camera's screen to a Component's member variable that specifies a position relative to the GameObject it resides upon.

    I don't understand why your transform is positioned incorrectly in the first scenario. Do you have multiple Cameras in the scene? mainCamera could be pointing to a different Camera than the one you're trying to position the GameObject relative to.

    It's hard to tell without knowing whats in your scene or what effects are being done on your GameObjects. I managed to get this script working for my camera without any issues using ScreenToWorldPoint

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. namespace CameraBoundaries
    5. {
    6.     [ExecuteInEditMode]
    7.     public class CameraWalls : MonoBehaviour
    8.     {
    9.         private Camera ourCamera;
    10.         public enum CameraWallSide {LEFT, RIGHT, TOP, BOTTOM}
    11.  
    12.         // We assume these BoxColliders are located externally from the Camera's GameObject
    13.         public BoxCollider2D leftWall;
    14.         public BoxCollider2D rightWall;
    15.         public BoxCollider2D topWall;
    16.  
    17.         // Use this for initialization
    18.         void Start()
    19.         {
    20.             ourCamera = GetComponent<Camera>();
    21.         }
    22.  
    23.         // Update is called once per frame
    24.         void Update()
    25.         {
    26.             if(ourCamera != null)
    27.             {
    28.                 if(leftWall != null)
    29.                 {
    30.                     SetCameraWall(ourCamera, leftWall, CameraWallSide.LEFT);
    31.                 }
    32.  
    33.                 if(rightWall != null)
    34.                 {
    35.                     SetCameraWall(ourCamera, rightWall, CameraWallSide.RIGHT);
    36.                 }
    37.  
    38.                 if(topWall != null)
    39.                 {
    40.                     SetCameraWall(ourCamera, topWall, CameraWallSide.TOP);
    41.                 }
    42.             }
    43.         }
    44.  
    45.         /// <summary>
    46.         /// Sets the wall's size and position to match the target Camera's specified side, stretching to fit the view area's width or height.
    47.         /// It is assumed that wall is on a different GameObject than that of the Camera
    48.         /// </summary>
    49.         public static void SetCameraWall(Camera cam, BoxCollider2D wall, CameraWallSide side)
    50.         {
    51.             Vector2 wallSidePosition = Vector2.zero;
    52.             Rect pixelRect = cam.pixelRect;
    53.    
    54.             if (side == CameraWallSide.LEFT || side == CameraWallSide.RIGHT)
    55.             {
    56.                 float wallThickness = wall.transform.localScale.x * wall.size.x;
    57.  
    58.                 if(side == CameraWallSide.LEFT)
    59.                 {
    60.                     Vector2 offset = (cam.transform.rotation * -Vector2.right) * (wallThickness / 2f);
    61.                     wallSidePosition = cam.ScreenToWorldPoint(new Vector2(0f, pixelRect.height / 2f)) + (Vector3)offset;
    62.                 }
    63.                 else
    64.                 {
    65.                     Vector2 offset = (cam.transform.rotation * Vector2.right) * (wallThickness / 2f);
    66.                     wallSidePosition = cam.ScreenToWorldPoint(new Vector2(pixelRect.width, pixelRect.height / 2f)) + (Vector3)offset;
    67.                 }
    68.  
    69.                 float wallStretch = (cam.orthographicSize * 2) / wall.transform.localScale.y;
    70.                 wall.size = new Vector2(wall.size.x, wallStretch);
    71.             }
    72.             else
    73.             {
    74.                 float wallThickness = wall.transform.localScale.y * wall.size.y;
    75.  
    76.                 if(side == CameraWallSide.TOP)
    77.                 {
    78.                     Vector2 offset = (cam.transform.rotation * Vector2.up) * (wallThickness / 2f);
    79.                     wallSidePosition = cam.ScreenToWorldPoint(new Vector2(pixelRect.width / 2f, pixelRect.height)) + (Vector3)offset;
    80.                 }
    81.                 else
    82.                 {
    83.                     // intentionally left out
    84.                 }
    85.  
    86.                 float wallStretch = (cam.orthographicSize * 2 * cam.aspect) / wall.transform.localScale.x;
    87.                 wall.size = new Vector2(wallStretch, wall.size.y);
    88.             }
    89.  
    90.             wall.transform.position = wallSidePosition;
    91.             wall.transform.rotation = cam.transform.rotation;
    92.         }
    93.     }
    94. }
    Note: This script also assumes the walls are on different GameObjects. I don't know if it will work with multiple Colliders on one GameObject, but I personally prefer to separate colliders to different GameObjects so its easier to manage them.
     
    Last edited: Nov 25, 2014
  5. jensmcatanho

    jensmcatanho

    Joined:
    Jul 11, 2014
    Posts:
    21
    The walls are on different GameObjects and there's only one camera on the scene, I really don't know what's causing this because I tested the same scripts on another project and the game worked as it should, but after I closed Unity and reopening it, the problem started again....
     
  6. jensmcatanho

    jensmcatanho

    Joined:
    Jul 11, 2014
    Posts:
    21
    Changing the cam.ScreenToWorldSize() statement to Camera.main.ScreenToWorldSize() solves the problem, but why does the first one works sometimes? It's a little confusing.
     
  7. Stoven

    Stoven

    Joined:
    Jul 28, 2014
    Posts:
    171
    Did you assign the Camera variable with a Camera attached to a prefab and not the one in the scene? Because maybe its referring to a Camera attached to a prefab. I've run into a similar problem with transforms as I assigned a reference of a script in the scene with a prefab, and then instantiated a clone of the prefab before running the scene. Suffice to say, the results were not what I initially expected until I did further reading on prefabs.

    In case that wasn't well understood, consider the following Scene Heirarchy

    -DummyCamera
    -TestObject

    A Camera is attached to DummyCamera.

    TestObject has the following script attached:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class _DummyScript2 : MonoBehaviour
    5. {
    6.  
    7.     public Camera camRef;
    8.  
    9.     // Use this for initialization
    10.     void Start()
    11.     {
    12.  
    13.     }
    14.  
    15.     // Update is called once per frame
    16.     void Update()
    17.     {
    18.         if(camRef != null)
    19.             Debug.Log(camRef.orthographicSize);
    20.     }
    21. }
    22.  
    I made a prefab of DummyCamera by dragging and dropping it into a directory, then deleted the one in the scene. I assigned camRef with the DummyCamera prefab, and then I dragged DummyCamera into the scene. Changing the DummyCamera's orthotgraphic size will not change whats printed to the console because camRef is pointing to a prefab instance created with the Scene the moment the Application starts playing. My assumption is that its hidden.

    Also if you change the script to this

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class _DummyScript2 : MonoBehaviour
    5. {
    6.  
    7.     public Camera camRef;
    8.  
    9.     // Use this for initialization
    10.     void Start()
    11.     {
    12.  
    13.     }
    14.  
    15.     // Update is called once per frame
    16.     void Update()
    17.     {
    18.         if(camRef != null)
    19.             Debug.Log("camRef ortho size: " + camRef.orthographicSize);
    20.  
    21.         Debug.Log("Camera count: " + Camera.allCamerasCount);
    22.     }
    23. }
    24.  
    You'll see that the Camera count is still one, even though the prefab instance with a Camera attached quite obviously exists.

    That's the only thing I can guess if you only have 1 camera shown in the Scene Hierarchy. I don't know how you're assigning mainCamera so I can only assume that this might be the problem.
     
    Last edited: Nov 26, 2014