三坐标介绍

Unity 中有三种坐标。

  • screen space 也就是屏幕的坐标,由像素点来定义,左下角是 (0,0),右上角是 (pixelWidth,pixelHeight),The z position is in world units from the camera.

    可以通过 Screen.width/Screen.height 来获取最大宽度/长度的像素值,原则上如果一个主相机的视野铺满了屏幕,比如主相机,那么 Camera.pixelWidth == Screen.width 并且 Camera.pixelHeight == Screen.height, 但是巧的是我做实验的时候这个代码是在写 OnDrawGizmos() 里面的,这四个值就不是两两相等,将 Scene 面板放大或者缩小 Screen.height/width 会跟着变,很奇怪,但在 OnGui() 中写的话,就是两两相等的。

  • viewport space 视口坐标是标准的和相对于相机的。相机的左下角为(0,0)点,右上角为(1,1)点,Z的位置是以相机的世界单位来衡量的。有的图也会叫 viewport space 为 GUI Space。

  • world space 世界坐标,都懂,Transform.position。

  • 其实还有第四种,也就是 local space,是物体相对于父物体的坐标偏移。

  • 还有第五种,也就是在 OnGui() 中使用的坐标,值得注意的是这个坐标左上角是 (0,0),而右下角是 (Screen.width,Screen.height)。

坐标转化 API

  • 世界坐标 → 屏幕坐标:camera.WorldToScreenPoint(transform.position); 这样可以将世界坐标转换为屏幕坐标。其中 camera 为场景中的 camera 对象。
  • 屏幕坐标 → 视口坐标:camera.ScreenToViewportPoint(Input.GetTouch(0).position); 这样可以将屏幕坐标转换为视口坐标。其中 camera 为场景中的 camera 对象。
  • 视口坐标 → 屏幕坐标:camera.ViewportToScreenPoint();
  • 视口坐标 → 世界坐标:camera.ViewportToWorldPoint();

在世界坐标画出屏幕坐标的边界

下面的代码用于实现将 Screen Space 的边界在 World Space 中画出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 文件名:ThreeSpaceTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ThreeSpaceTest : MonoBehaviour
{
private Camera mainCam; // = Camera.main.GetComponent<Camera>(); // 不能在这里赋值
private void Awake()
{
mainCam = Camera.main.GetComponent<Camera>();
}
void OnDrawGizmos()
{
Vector3 p = mainCam.ScreenToWorldPoint(new Vector3(0, 0, Camera.main.GetComponent<Camera>().nearClipPlane));
Vector3 p1 = mainCam.ScreenToWorldPoint(new Vector3(0, mainCam.pixelHeight,Camera.main.GetComponent<Camera>().nearClipPlane));
Vector3 p2 = mainCam.ScreenToWorldPoint(new Vector3(mainCam.pixelWidth, mainCam.pixelHeight, Camera.main.GetComponent<Camera>().nearClipPlane));
Vector3 p3= mainCam.ScreenToWorldPoint(new Vector3(mainCam.pixelWidth, 0, Camera.main.GetComponent<Camera>().nearClipPlane));
Gizmos.color = Color.red;

// 这里的 4 个值不是两两相等的,并且拖动 Scene 面板,Screen 的两个值会跟着变
Debug.Log(mainCam.pixelWidth + " " + Screen.width + " " + mainCam.pixelHeight + " " + Screen.height);

// 如果将主相机调成正交模式的话,4 个球会正好出现在相机的较近切面的四个角上
Gizmos.DrawSphere(p, 1.0f);
Gizmos.DrawSphere(p1, 1.0f);
Gizmos.DrawSphere(p2, 1.0f);
Gizmos.DrawSphere(p3, 1.0f);
}
private void OnGUI()
{
// 四个值是两两相等的
GUILayout.TextField(string.Format("mainCam.pixelWidth = {0}\n" +
"mainCam.pixelHeight = {1}\n" +
"Screen.width = {2}\n" +
"Screen.height = {3}\n", mainCam.pixelWidth, mainCam.pixelHeight, Screen.width, Screen.height));
}
}

题外话:刚画出来的时候因为设置的球的半径很小,所以在 Scene 面板上转了好几圈都没找到,当时没想到把半径改大一点,结果还是把 p 打印出来,再放一个 Cube 到那个位置,F 定位 Cube 才找到小球球,瞬间感觉自己是瞎子。

实战小技巧

因为物体只能在 World Space 中移动,所以很多时候我们都需要将坐标转化能 World Space 中的坐标再进行运算,而且还要提防物体不能超出我们的屏幕范围,可以通过下面代码获取屏幕坐标的边界 ( Z 轴的值有待商榷 )

1
Vector3 fullScreen = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width,Screen.height,10));

然后用下面语句来判断场景中的物体是否出了相机范围:

1
if (aRect.transform.position.y>fullScreen.y) {}

参考

UnityEngine.Camera
三种坐标图解
知乎四种坐标及其转换API
UnityAnswer