Unity中的坐标系详解

前言

网上关于Unity坐标系讲解的不计其数,但真正讲的透彻明白的寥寥无几。作为新手,被Unity这几个坐标系搞的晕头转向,痛定思痛,翻阅多篇博客和帖子,汇总如下,希望给后来者提供些帮助,同时也勉励自己在IT行业走的更高更远。
如有不足之处,还望海涵,不吝批评指教,评论留言,共同进步。

Unity中经常用到的坐标系有4个,分别为世界坐标系,屏幕坐标系,视口坐标系和GUI坐标系

坐标系综述

1、世界坐标系

表示实际场景中的模型或UI元素物理位置,模型的话,就是其固有组件Transform中显示的position【父物体坐标不归零的除外】;UI元素则很少使用世界坐标去表示,主要用RectTransform表示,涉及锚点、轴心点,自行百度。

2、屏幕坐标系

所有UI或模型渲染出现在屏幕里才有意义,屏幕中显现的位置信息构成屏幕坐标系。屏幕坐标系为三维坐标系,但显示屏是二维平面的,(x,y)表示位置信息,屏幕左下角为(0,0)点【原点】,右上角为(Screen.宽度,Screen高度),以1920*1080的屏幕分辨率为例,右上角为(1920,1080),超出视野之外依然有数值,只是不显示在Game窗口中。z值表示深度,坐标系转换时细讲。
模型的屏幕坐标:(x,y)用分辨率宽高表示,z=模型pos.z-(摄像机pos.z);
UI元素的屏幕坐标:同上。
输入的鼠标位置和触摸位置,其屏幕坐标为横纵由分辨率表示,z值默认为0.

3、视口坐标系

做分屏界面时用到(赛车游戏后视镜看到的画面渲染在一个独立的窗口,这里称之为视口) 。与屏幕坐标系雷同,但略有区别。最小值(0,0)点为摄像机视野左下角,最大值(1,1)在摄像机视野右上角。z值后面坐标系转换时细讲。

4、GUI坐标系

Untiy老版UI界面,纯二维坐标系。数值类型与屏幕坐标系一样,用分辨率宽高表示。不同的是(0,0)点为左上角,(Screen.宽度,Screen高度)为右下角。【真的不常用,打脸了】

常见函数 【zInfo:深度信息】
//1.屏幕转世界坐标
Vector3 Camera.main.ScreenToWorldPoint(new Vector3(screenPos.x , screenPos.y , zInfo));
//2.世界转屏幕坐标
Vector3 Camera.main.WorldToScreenPoint(new Vector3(worldPos.x , worldPos.y , worldPos.z));
//3.世界转视口坐标
Vector3 Camera.main.WorldToViewportPoint();
//4.视口转世界坐标
Vector3 Camera.main.ViewportToWorldPoint(new Vector3(viewPortPos.x , viewPortPos.y , zInfo));
//5.视口转屏幕坐标
Vector3 Camera.main.ViewportToScreenPoint();
//6.屏幕转视口坐标
Vector3 Camera.main.ScreenToViewportPoint();

详解

以下讲解以发布平台为PC端为例,屏幕分辨率为1920-1080,Canvas Mode为Screen Space-Camera
Canvas配置

屏幕坐标<—>世界坐标

WorldToScreenPoint函数
接收一个世界空间下的位置信息,然后返回其所在的屏幕空间信息,x,y表示位置信息,z表示其相对于摄像机的深度信息。该深度信息由世界空间下摄像机和输入位置的z值来决定。
例子:摄像机的位置为(0.0, 0.0 , -10.0),输入的位置为(0.0,0.0,1.0)。则返回的结果为(960.0,540.0,11)【(screen.width/2 , screen.height/2 , 1-(-10))】【摄像机指向z方向,默认坐标为(0.0, 0.0 , -10.0)】

ScreenToWorldPoint函数
与WorldToScreenPoint相反,输入屏幕坐标及深度信息,返回其世界坐标位置。
需要注意的是,通常我们使用的Input.mousePosition是没有深度信息的,默认其z值为0,同理还有触摸touch形式的。类比理解:投影仪和幕布。幕布相当于屏幕,是二维平面,只有x,y位置信息;投影仪相当于摄像机,z值就如同投影仪到幕布的距离,只有知道z值才能将幕布上的点转换到三维世界坐标系中。

例子1:

      public Vector3 myscreenToworld(Vector3 mousepos,Transform targetTransform) 
      {
        //先计算相机到目标的向量
        Vector3 dir = targetTransform.position - Camera.main.transform.position;
        //计算投影
        Vector3 normardir = Vector3.Project(dir, Camera.main.transform.forward);
        //计算是节点,需要知道处置屏幕的投影距离
        Vector3 worldpos = Camera.main.ScreenToWorldPoint(
        new Vector3(mousepos.x,
                   mousepos.y,
                   normardir.magnitude)
                                                         );
        return worldpos;
       }

例子2

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//点击鼠标左键拖拽物体
public class DrageObj : MonoBehaviour
{
    private Vector3 TargetScreenSpace;// 目标物体的屏幕空间坐标
    private Vector3 TargetWorldSpace;// 目标物体的世界空间坐标

    private Vector3 MouseScreenSpace;// 鼠标的屏幕空间坐标
    private Vector3 OffsetDis;// 世界空间距离偏移量

    void Awake()
    { 



    }

    IEnumerator OnMouseDown()

    {

        // 把目标物体的世界空间坐标转换到它自身的屏幕空间坐标 
        TargetScreenSpace = Camera.main.WorldToScreenPoint(transform.position);

        // 存储鼠标的屏幕空间坐标(Z值使用目标物体的屏幕空间坐标) 
        MouseScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, TargetScreenSpace.z);

        // 计算目标物体与鼠标物体在世界空间中的偏移量 
        OffsetDis = transform.position - Camera.main.ScreenToWorldPoint(MouseScreenSpace);

        // 鼠标左键按下 
        while (Input.GetMouseButton(0))
        {

            // 存储鼠标的屏幕空间坐标(Z值使用目标物体的屏幕空间坐标)
            MouseScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, TargetScreenSpace.z);

            // 把鼠标的屏幕空间坐标转换到世界空间坐标(Z值使用目标物体的屏幕空间坐标),加上偏移量,以此作为目标物体的世界空间坐标
            TargetWorldSpace = Camera.main.ScreenToWorldPoint(MouseScreenSpace) + OffsetDis;

            // 更新目标物体的世界空间坐标 
            transform.position = TargetWorldSpace;

            // 等待固定更新 
            yield return new WaitForFixedUpdate();
        }
    }

}

视口坐标<—>世界坐标

WorldToViewportPoint:
输入世界坐标,返回的是对应的点所在的视口位置信息,当然以及其相对于摄像机的深度信息(距离)深度信息与同一摄像机下的屏幕坐标系的深度信息相同。
ViewportToWorldPoint:
输入视口坐标(记得对应的深度信息),返回点所在的世界坐标

例子:2D游戏 角色始终在视口范围内,不能越过四周边界

public float leftBorder;
public float rightBorder;
public float topBorder;
public float bottomBorder;

....

Vector3 leftBtm_cornerPos = Camera.main.ViewportToWorldPoint(new Vector3(0f, 0f,
            Mathf.Abs(-Camera.main.transform.position.z))); //这里的z轴在正交视图下意义不大
Vector3 rightTop_cornerPos = Camera.main.ViewportToWorldPoint(new Vector3(1f, 1f,
            Mathf.Abs(-Camera.main.transform.position.z)));
....

leftBorder = leftBtm_cornerPos.x;
rightBorder = rightTop_cornerPos.x;
topBorder = rightTop_cornerPos.y;
bottomBorder = leftBtm_cornerPos.y;

....

if (pos.x <= leftBorder)
{
    pos.x = leftBorder;
}
else if (pos.x >= rightBorde
{
    pos.x = rightBorder;
}

if (pos.y <= bottomBorder)
{
    pos.y = bottomBorder;
}
else if (pos.y >= topBorder)
{
    pos.y = topBorder;
}

视口坐标<—>屏幕坐标

前提:知道屏幕分辨率
ViewportToScreenPoint函数
乘法:视口位置坐标 * 宽高分辨率===>屏幕坐标位置信息
视口坐标深度信息=屏幕坐标深度信息(视口pos.z=屏幕pos.z)

ScreenToViewportPoint函数
反向运算
除法:屏幕位置坐标/宽高分辨率===>视口坐标位置信息
视口坐标深度信息=屏幕坐标深度信息(视口pos.z=屏幕pos.z)

版权声明:本文为qq_42187286原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42187286/article/details/102821701

智能推荐

matlab中的三维坐标变换,从直角坐标系转换到球坐标系

使用Matlab画图时,有时三维的球坐标图更直观,需要把直角坐标转换球坐标。 代码如下: 看起来很简单的,下面写个小程序调用一下。 结果如下:...

最简单的理解android中的坐标系

一、由于自定义控件中涉及到非常多的坐标的计算,特别是ViewGroup的自定义,所以很有必要对坐标这个东西进行多些的了解,如果有了解过Path、Canvas这些东西的使用的话,肯定会知道不管你画一条线、还是一个圆或者是一个正方形,没有坐标是完成不了的,当然那些坐标是基于canvas的坐标系的,那下面我们先看一张图,看看android的坐标系跟我们日常生活中所认识得坐标系有什么不一样的: ...

PCL点云库中的坐标系(CoordinateSystem)

讲一下我所理解的PCL点云库中的坐标系统。 引言 正文 代码 参考 引言 世上本没有坐标系,用的人多了,便定义了坐标系统用来定位。地理坐标系统用于定位地球上的位置,PCL点云库可视化窗口中的坐标系统用于定位其三维世界中的位置。 本人刚开始接触学习PCL点云库,计算机图形学基础为零,以下内容基于自己的理解,如有错误,欢迎指出。 正文 首先介绍一下PCL点云库visualization模块中的PCLV...

17.ROS中的坐标系管理系统

17.ROS中的坐标系管理系统 参考书籍“”“机器人学导论” 1、机器人中的坐标变换 1.1.安装tf功能包 1.2启动roslaunch的一个脚本文件来启动很多节点 1.3运行海龟的键盘控制节点 通过键盘移动发现,后面的海龟会跟随前面的海龟运动。 2、查看系统中TF坐标系之间的关系 生成了一个pdf文件,在主目录下 有三个坐标系,海龟1,2,世界坐...

Canvas 绘制坐标系中的点以及折线

需求 上一篇章介绍了如何使用Canvas绘制坐标系,那么本篇章来看看怎么简单绘制坐标系中的点。 示例图如下: 可以看到这里绘画的坐标点比较大,为了更好看一些。其实不管大小,基本的绘制步骤如下: 设置坐标点的中心圆点位置(x0,y0) 设置坐标点的大小 dotSize 计算坐标点的上下左右四角的点坐标 条件1和2可以直接通过设置获取,而坐标点上下左右四角坐标看看下面的计算示意图。 计算坐标点的上下左...

猜你喜欢

Java运行原理

1.Java运行原理       我们可通过文本编辑板生成Java源代码(.java)经过dos窗口由Java编译器(javac.exe)生成字节码文件(.class),字节码可由Java虚拟机转化为机器码供计算机读取处理。由于Java可以生成字节码可供虚拟机转译所以可跨平台运行。运行过程如下: 所以相对于C语言还需要转化为exe文件才能运行的权限,Java具有跨平台...

Python由放弃到入门,基础篇七(类)下

类的实例化 有感于现在python教程多如牛毛,且大多高不可攀,多次拜读而不得其门道,遂由入门到放弃。偶有机缘,得一不错教程,得以入门,现博客分享,想要获取完整教程,ff17328081445。 通过对比可以看到,实例化后再使用的格式,①是空着的,意思是这里不再需要@classmethod的声明,并且在第②处,把cls替换成了self。同时,实例化后再使用的格式,需要先赋值然后再调用(第③处): ...

pytorch CNN手写字体识别

数据整体训练一次,对于accuracy都是0的问题,由于刚开始学,有些代码的细节我也没看懂,不过整体结果是对的,可能是由于pytorch版本的更新,导致accuracy的计算方式有所改变 内容转载自:https://www.bilibili.com/video/av15997678/?p=19...

linux后台运行命令总结

linux后台运行命令总结 问题: 我们有时候需要登录远程服务器跑运行时间非常长的脚本,这个时候你要让脚本后台运行,不然占着终端窗口看着不舒服。但万一网络不好,(比如我这儿的破校园网,高峰时几秒钟断一次),终端突然和服务器之间的连接断了,那脚本就会自动停了(因为运行test.sh进程的父进程就是当前的shell终端进程,关闭当前shell终端时,父进程退出,会发送hangup信号给所有子进程,子进...

类对象模型和this指针

关于类/对象大小的计算 类只是一种类型定义,它本身是没有大小可言的。 我们这里指的类的大小,其实指的是类的对象所占的大小。因此,如果用sizeof运算符对一个类型名操作,得到的是具有该类型实体的大小。 首先,类大小的计算遵循结构体的对齐原则 类的大小与普通数据成员有关,与成员函数和静态成员无关。即普通成员函数,静态成员函数,静态数据成员,静态常量数据成员均对类的大小无影响 虚函数对类的大小有影响,...