今天学习unity的各种坐标,以及相互转换。

一.认识坐标系

1.世界坐标

世界坐标:顾名思义,就是物体在全局世界空间内的坐标。

获取:transform.position

2.局部坐标

局部坐标:物体相对于父物体的坐标,以父物体为基准。unity的Inspector视图里的坐标就是相对于父物体的坐标,也就是局部坐标。

获取:transform.localPosition

3.屏幕坐标

屏幕坐标:ScreenPoint,以像素为单位的2D坐标,与屏幕分辨率有关,原点在屏幕左下角,即左下角坐标为Vector2(0,0),右上角为Vector2(Screen.width,Screen.height)。

以分辨率为1920*1080来说,屏幕左下角坐标为Vector2(0,0),右上角为Vector2(1920,1080)。

获取:对于3D物体来说,获取屏幕坐标需要将世界坐标转换为屏幕坐标,见坐标转换部分。对于2D的UI来说,Overlay模式下,它本身就不在空间内,它的屏幕坐标就是自己的世界坐标,即transform.position。但如果是带有相机的渲染模式,获取屏幕坐标同样需要将世界坐标转换为屏幕坐标。

4.视口坐标(又叫观察坐标)

视口坐标:ViewportPoint,是归一化坐标,与屏幕分辨率无关,将屏幕的长和宽视为整体1,原点依然是在左下角,坐标为Vector2(0,0),右上角为Vector2(1,1)。

获取:需要将世界坐标转换为视口坐标,见坐标转换部分。

5.UI坐标

UI坐标:原点位置由Canvas或指定UI或父UI的Rect Transform和锚点设置决定,不是固定的屏幕左下角。

获取:对于直系父物体来说,自身的UI坐标是anchoredPosition,可以直接通过RectTransform.anchoredPosition获取,也就是说,anchoredPosition获得的是自己相对直系父物体的UI位置;那对于其他UI,则需要将自己的屏幕坐标转换为相对其他UI的UI坐标,见坐标转换部分。

二.坐标转换

os:终于上代码了~

1.世界坐标与局部坐标

这里只写了位置互相转换,同样的,还有方向和缩放的相互转换,这里就不过多说了

    /// <summary>
    /// 局部坐标→世界坐标,获取物体的子物体在世界的位置,比如获取机器上某个零件的世界坐标
    /// </summary>
    ///<param name="localPos">零件的相对位置</param>
    private void LocalToWorld(Vector3 localPos)
    {
        Vector3 worldPos = transform.TransformPoint(localPos);
        //为什么不直接用transform.position?
        //直接用就只能获取到这个零件本身在哪(世界里),而TransformPoint(localPos)可以获取机器身上任何一个特定点在哪(世界里)
        Debug.Log("打印零件的世界坐标" + worldPos);
    }
    /// <summary>
    /// 世界坐标→局部坐标,获取世界的某个点在物体的什么位置,比如获取某个路牌在我家的什么位置
    /// </summary>
    /// <param name="worldPos">路牌的世界位置</param>
    private void WorldToLocal(Vector3 worldPos)
    {
        Vector3 localPos = transform.InverseTransformPoint(worldPos);//是上面的逆运算
        //为什么不直接用transform.LocalPosition?
        //同理,InverseTransformPoint(worldPos);可以获取任何物体在自己的什么位置;而transform.LocalPosition只能获取自己在父物体的位置
        Debug.Log("打印路牌相对于我的局部坐标" + localPos);
    }

2.世界坐标与屏幕坐标


    /// <summary>
    ///  世界 → 屏幕坐标
    /// </summary>
    /// <param name="worldPos">世界坐标</param>
    private void WorldToScreen(Vector3 worldPos)
    {          
        Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
    }
 /// <summary>
 /// 屏幕 → 世界坐标 ,需要深度,深度代表世界坐标的Z值位置,代表物体在3D空间的远近(近大远小)
 /// 深度获取:通过射线获取碰撞点深度;通过指定距离;等等
 /// </summary>
 /// <param name="depth">深度</param>
    private void ScreenToWorld(float depth)
    {
        Vector3 mousePos = Input.mousePosition;
        Vector3 worldPos = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, depth));
    }

3.世界坐标与视口坐标

    /// <summary>
    /// 世界 → 视口坐标
    /// </summary>
    /// <param name="worldPos">世界坐标</param>
    private void WorldToView(Vector3 worldPos)
    {
        Vector3 viewportPos = Camera.main.WorldToViewportPoint(worldPos);
    }
    /// <summary>
    /// 视口 → 世界坐标,需要深度值,深度代表世界坐标的Z值位置,代表物体在3D空间的远近(近大远小)
    /// 比如depth=10,代表世界位置的Z方向位置为距离摄像机前10米处
    /// </summary>
    /// <param name="depth"></param>
    private void ViewToWorld(float depth)
    {
        //例1:获取屏幕中心的世界坐标(摄像机前10米处)
        Vector3 centerWorldPos = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 10f));
        //例2:获取屏幕右上角的世界坐标(摄像机前5米处)
        Vector3 topRightWorld = Camera.main.ViewportToWorldPoint(new Vector3(1f, 1f, 5f));
    }

4.屏幕坐标与视口坐标

    /// <summary>
    /// 屏幕 → 视口坐标
    /// </summary>
    /// <param name="worldPos"></param>
    private void ScreenToView(Vector3 screenPos)
    {
        Vector3 viewportPos = Camera.main.ScreenToViewportPoint(screenPos);
    }
    /// <summary>
    /// 视口 → 屏幕坐标
    /// </summary>
    /// <param name="viewportPos"></param>
    private void ViewToScreen(Vector3 viewportPos)
    {
        Vector3 screenPos = Camera.main.ViewportToScreenPoint(viewportPos);
    }

5.屏幕坐标与UI坐标

如果要从世界坐标转UI坐标,也需要先将其转为屏幕坐标,再转为UI坐标;从UI坐标转为世界坐标同理,都要经过屏幕坐标去转换。

    /// <summary>
    /// 将屏幕坐标转换为相对于谁的UI坐标
    /// </summary>
    //if里面返回的是bool值,表示是否转换成功,在UI平面内
    void ScreenToUI(RectTransform canvasRect,Vector2 screenPos,Camera camera)
    {
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
       canvasRect,     // 目标RectTransform,相对于谁的UI
       screenPos,      // 屏幕坐标
       camera,                // Camera(摄像机渲染模式为Overlay模式时,此值为null)
       out Vector2 uiPos  //得到的UI坐标
   ))
        {
            Debug.Log(uiPos);
        }
    }

    /// <summary>
    /// 将UI坐标转为屏幕坐标,Overlay模式的话可以直接用transform.position获取屏幕坐标
    /// </summary>
    /// <param name="camera">ui相机,</param>
    /// <param name="uiPos">UI坐标</param>
    void UIToScreen(Camera camera, Vector2 uiPos)
    {
        transform.GetComponent<RectTransform>().anchoredPosition = uiPos;//这一步必须要有!!!
        //1.计算了uiPos(理论坐标)
        // 2. 必须把 targetUI 移动到这个位置(本例中目标UI就是自己)
        // 3. 然后才能用 targetUI.position 获取实际位置
        // 4. 再用 RectTransformUtility.WorldToScreenPoint 转换这个实际位置
        Vector3 screenPos = RectTransformUtility.WorldToScreenPoint(camera, transform.position);
        Debug.Log("UI坐标转换屏幕坐标:" + screenPos);
    }

6.射线投影相关

// 屏幕坐标 → 射线,从屏幕上的一个点向3D世界发射一条射线,用于检测这个"屏幕像素"在3D世界中指向什么物体。
Ray ray = camera.ScreenPointToRay(screenPos);
// 视口坐标 → 射线,与上面不同的是,用归一化坐标发射射线
Ray ray = camera.ViewportPointToRay(viewportPos);

三.小知识

1.什么情况下用到的是屏幕坐标?

获取鼠标/输入位置时,从鼠标位置发出射线检测时(比如点击3D物体),截图等需要直接处理屏幕像素位置时。

2.什么情况用到的是UI坐标?

创建UI时,UI跟随物体时,UI交互检测时,UI拖拽时等。

以上就是全部内容,如果有不对或者改进的地方请速速告诉我,我改,我改/(ㄒoㄒ)/~~

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐