UG4L12——坐标系统

本章代码关键字

1
2
3
4
5
6
gObject.LocalToGlobal()        //输出元件在FGUI坐标系下的物理屏幕坐标
gObject.GlobalToLocal() //获取物理屏幕坐标在UI元件上的局部坐标
GRoot.inst.GlobalToLocal() //将元件在物理屏幕上的实际坐标转换为FGUI计算使用的逻辑屏幕坐标
gObject.LocalToRoot() //将局部坐标转换为FGUI计算会使用的逻辑屏幕坐标(第二个参数必须传入GRoot.inst)
gObject.RootToLocal() //逻辑屏幕坐标转换为相对于父元件的局部坐标(第二个参数必须传入GRoot.inst)
gObject.TransformPoint() //计算相对于自己的某个位置相对于另一个元件的位置

FairyGUI的坐标原点

FairyGUI是以屏幕左上角为原点的,Unity的屏幕坐标是以左下角为原点

image

如果想转化屏幕坐标和UI坐标:

1
2
3
4
5
6
7
8
9
10
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 screenPos = Input.mousePosition;
print($"Unity的屏幕坐标:{screenPos}");
screenPos.y = Screen.height - screenPos.y;
print($"FairyGUI的屏幕坐标:{screenPos}");
}
}

输出:image

UI对象和屏幕坐标转换

在FGUI我们会与三种屏幕坐标打交道:

  • 相对于父元件的局部坐标
  • 相对于FGUI坐标系的实际在屏幕上显示的物理屏幕坐标(由于分辨率自适应的存在很可能与逻辑屏幕坐标不一致)
  • 相对于FGUI坐标系的FGUI元件真正参与计算的逻辑屏幕坐标(和FGUI编辑器设置的位置一致)
  1. GObject​里的x/y/position​值都是局部坐标,也就是相对于父元件的偏移

    image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void Start()
    {
    //显示面板组件的方法
    TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("teach");
    //图形相对于面板组件的局部坐标
    print(panel.m_graphTest.x);
    print(panel.m_graphTest.position);
    GObject obj = panel.m_svTest.GetChild("n1");
    //面板下的滚动视图中的第二个格子相对于滚动视图组件的局部坐标
    print(obj.x);
    print(obj.y);
    print(obj.position);
    }

    输出:image

    对比其在FairyGUI编辑器显示的坐标:

    image​​image

  2. 获取任何一个元件在屏幕上的坐标(FairyGUI坐标系下,左上角原点,不常用)

    该方法的参数和返回值可以是Vector2​也可以是Rect​,
    传入的Vector2​参数代表的是与元件锚点的偏移量,如果只是获取位置只需要传入Vector2.zero​即可

    值得一提的是,该方法得到的是该元件在物理屏幕上的坐标,由于屏幕的分辨率和FGUI处理的设计分辨率不一致,
    因此这里得到的坐标很可能与在FairyGUI编辑器处设置的元件坐标不一致,该坐标不适用于直接与FGUI相关元件交互

    image

    1
    2
    3
    4
    5
    6
    7
    8
    //显示面板组件的方法
    TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("teach");
    //面板下的滚动视图中的第二个格子相对于滚动视图组件的局部坐标
    GObject obj = panel.m_svTest.GetChild("n1");
    print("局部坐标" + obj.position);
    //转换为FGUI坐标系下的坐标
    Vector2 screenPos = obj.LocalToGlobal(Vector2.zero);
    print("相对于FGUI屏幕坐标系的坐标" + screenPos);

    输出:image

  3. 获取屏幕坐标在UI元件上的局部坐标(不常用)

    注意,这里的屏幕坐标指的是在物理屏幕上的坐标,而非FGUI计算所采用的逻辑屏幕(设计分辨率)的坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    GObject obj = panel.m_svTest.GetChild("n1");
    print(obj.x);
    print(obj.y);
    print("局部坐标" + obj.position);
    //把局部坐标转为实际屏幕坐标
    Vector2 screenPos = obj.LocalToGlobal(Vector2.zero);
    print("相对于FGUI屏幕坐标系的坐标" + screenPos);
    //把实际屏幕坐标转为局部坐标
    Vector2 localPos = obj.GlobalToLocal(screenPos);
    print("通过FGUI坐标系下的屏幕坐标 转为 相对于某个对象内部的局部坐标" + localPos);

    输出:image

  4. 物理屏幕坐标转换为逻辑屏幕坐标

    如果UI适配导致全局缩放,那么逻辑屏幕大小和物理屏幕大小不一致,逻辑屏幕坐标就是GRoot里的坐标
    编辑器里定义和代码中处理的 指的都是逻辑屏幕坐标(UI坐标)

    而gObject.LocalToGlobal()​输出的都是物理屏幕坐标,此方法获取的坐标不适合直接用于FGUI相关元件交互,
    例如在某个元件的位置上再添加一个元件,就不适用,需要将其转换为逻辑屏幕坐标

    image

    实际Unity编辑器下的物理屏幕分辨率为image
    而FairyGUI编辑器下的逻辑屏幕分辨率为image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //显示面板组件的方法
    TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("teach");
    //面板下的滚动视图中的第二个格子相对于滚动视图组件的局部坐标
    GObject obj = panel.m_svTest.GetChild("n1");
    print("局部坐标" + obj.position);
    //把局部坐标转为实际屏幕坐标
    Vector2 screenPos = obj.LocalToGlobal(Vector2.zero);
    print("相对于FGUI屏幕坐标系的物理屏幕坐标" + screenPos);
    //把物理屏幕坐标转换为逻辑屏幕坐标
    Vector2 logicalPos = GRoot.inst.GlobalToLocal(screenPos);
    print("相对于FGUI屏幕坐标系的逻辑屏幕坐标" + logicalPos);

    输出:image

    对比FairyGUI编辑器设置的位置:
    imageimage

  5. UI元件坐标与逻辑屏幕坐标之间的转换

    gObject.LocalToRoot()​可以直接将局部坐标转换为FGUI计算会使用的逻辑屏幕坐标(第二个参数必须传入GRoot.inst​)
    该方法得到的坐标可以直接参与FGUI元件的交互,因此它最常用

    如果要将逻辑屏幕坐标转换为相对于父元件的局部坐标,需要使用gObject.RootToLocal()​(第二个参数必须传入GRoot.inst​)

    image

    1
    2
    3
    4
    5
    6
    7
    TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("teach");
    //面板下的滚动视图中的第二个格子相对于滚动视图组件的局部坐标
    GObject obj = panel.m_svTest.GetChild("n1");
    print("局部坐标" + obj.position);
    Vector2 logicalScreenPos = obj.LocalToRoot(Vector2.zero, GRoot.inst);
    print("对象相对屏幕的逻辑坐标" + logicalScreenPos);
    print("逻辑坐标转换为局部坐标" + obj.RootToLocal(logicalScreenPos, GRoot.inst));

    输出:image

    对比FairyGUI编辑器设置的位置:
    imageimage

  6. A中相对坐标相对B的位置

    • 参数一:相对于自己的某个坐标
    • 参数二:要计算相对于哪个元件的位置

    image

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("teach");
    //图形相对于面板组件的局部坐标
    print(panel.m_graphTest.x);
    print(panel.m_graphTest.position);
    //面板下的滚动视图中的第二个格子相对于滚动视图组件的局部坐标
    GObject obj = panel.m_svTest.GetChild("n1");
    //计算相对于B的位置
    Vector2 relativeToBPos = obj.TransformPoint(Vector2.zero, panel.m_imgTest);
    print("A中相对位置(0,0)相对于B的位置" + relativeToBPos);
    relativeToBPos = obj.TransformPoint(new Vector2(10, 10), panel.m_imgTest);
    print("A中相对位置(10,10)相对于B的位置" + relativeToBPos);

    输出:image

    对比FairyGUI编辑器设置的位置:
    image​​imageimage

    计算A中相对位置(0, 0)​相对于B的位置:(7256,81525)=(719,444)(725-6, 81-525)=(719, -444)
    计算A中相对位置(10, 10)​相对于B的位置:(725+106,81+10525)=(729,434)(725+10-6, 81+10-525)=(729, -434)

世界坐标转换

  1. 世界坐标转UI坐标

    世界 ——> 屏幕 ——> UI

    假设要得到下面的Cube对象相对于FairyGUI坐标系的逻辑屏幕坐标(UI坐标)

    image

    1
    2
    3
    4
    5
    6
    7
    //得到相对于Unity坐标系的物理屏幕坐标
    Vector2 screenPos = Camera.main.WorldToScreenPoint(this.transform.position);
    //得到相对于FairyGUI坐标系的物理屏幕坐标
    screenPos.y = Screen.height - screenPos.y;
    //得到FairyGUI直接使用计算的逻辑屏幕坐标
    Vector2 logicalPos = GRoot.inst.GlobalToLocal(screenPos);
    print("Cube在相对于FairyGUI坐标系的逻辑屏幕坐标(UI坐标)位置" + logicalPos);

    输出:image

  2. UI坐标转世界坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    print("世界坐标" + this.transform.position);
    //得到相对于Unity坐标系的物理屏幕坐标
    Vector3 screenPosVector3 = Camera.main.WorldToScreenPoint(this.transform.position);
    float distance = screenPosVector3.z;
    //得到相对于FairyGUI坐标系的物理屏幕坐标
    screenPosVector3.y = Screen.height - screenPosVector3.y;
    //得到FairyGUI直接使用计算的逻辑屏幕坐标
    Vector2 logicalPos = GRoot.inst.GlobalToLocal(screenPosVector3);
    print("Cube在相对于FairyGUI坐标系的逻辑屏幕坐标(UI坐标)位置" + logicalPos);
    //将逻辑屏幕坐标转换为相对于FairyGUI坐标系的物理屏幕坐标
    screenPosVector3 = GRoot.inst.LocalToGlobal(logicalPos);
    //得到相对于Unity坐标系的屏幕坐标
    screenPosVector3.y = Screen.height - screenPosVector3.y;
    //一般情况下,还需要提供距离摄像机视野正前方distance长度的参数作为screenPos.z(如果需要,将screenPos改为Vector3类型)
    screenPosVector3.z = distance;
    Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPosVector3);
    print("从逻辑屏幕坐标转换为世界坐标" + worldPos);

    输出:image