您的位置:首页 > 其它

3D和2D游戏开发技术里的坐标系总结

2017-08-12 22:21 260 查看
总结下在unity3d开发中,以及在其他游戏开发中的一些坐标系的概念。也在此分享下,一起进步。


第一部分:2D游戏开发。

先从简单2D世界说起吧。2D开发里的坐标系比较简单,2D坐标常见的有两种:
一种是以flash为代表的,以屏幕左上角为(0,0)点,右下角为(Screen.width, Screen.height)点。
一种是以cocos2d系列为代表的,由于使用的是OpenGL系提供的API,故以屏幕左下角为(0,0)点,右上角(Screen.width, Screen.height)点。

2D的坐标系就是如此简单,下面重点介绍3d的坐标系。


第二部分:3D游戏开发。

在3D开发中,用的是笛卡尔三维坐标系,包含两种3D的坐标系:左手坐标系和右手坐标系。

很多新人开始学3D开发时都会和我一样有很多疑惑,例如Unity3D和OpenGL, DirectX有啥区别?他们的坐标都是如何的?如何转换到屏幕上的坐标?这些问题的确挺头疼的。下面就逐个讲讲。

1.Unity3D和OpenGL, DirectX有啥不同?

其实他们拿来对比,是不太妥当的,因为他们不是在同一层级上的,不构成并列选择关系。Unity3D调用的是DirectX 和 OpenGL提供的 API。也可以这么理解,Unity3D封装了OpenGL和DirectX。

简单的说,就是:你的程序–>Unity3D–>DirectX/OpenGL–>图形设备(显卡)。

2.左手坐标系和右手坐标系如何理解

按照教科书的概念,左手坐标系和右手坐标系应该如图1:



(图一:两种坐标系)

那如何理解“左手”和“右手”的概念呢?我们做个实验(为此,笔者把自己的手指都秀出来了~)

伸出左手(或右手),中指为x轴,拇指为y轴,食指为z轴,就组成了左手(或右手)坐标系



(图二:左手坐标系实例)



(图三:左手坐标系实例)

3.那u3d用到的坐标系是啥坐标系呢?

答:在Unity3D里都是左手坐标系

u3d中世界坐标:左手坐标系。

屏幕坐标系:左手坐标系。什么!屏幕的坐标系是三维的?没错。那该如何理解?这样理解吧,屏幕从左下角开始为(0,0,0)点,往竖方向向上y递增,往右x递增,往屏幕里面z递增.

GUI坐标:2d的,左上角为(0,0)。和flash里的原点一样都是从左上开始。

简单来说,就是,在u3d构建中,3d的坐标都是左手坐标系。

4.那右手坐标系出现在什么地方?

1.OpenGL的世界坐标系是右手坐标系(蛋疼啊,和U3D习惯不一样)。

2.建模软件3dsMax:右手坐标系,而且z轴向上。在输出fbx保持up为Y轴的前提下,若不对3dsmax中模型的pivot进行旋转,则导入到Unity3D中后,模型会基于x有-90°的旋转。这种选择可以使得物体在直观印象不变的情况下,直接把左手坐标系转成右手坐标系。如图四,模型是以右手坐标系构建的。



(图四:3ds max中的建模坐标系)

但导入到unity后,绕x轴转了-90度,如图五,变成了图六所示。



(图五)



(图六)

5.为啥会左手坐标系和右手坐标系两种东西

额,这是当初标准没统一的问题。蛋疼的历史。

==============================================================

Unity3D中有哪些坐标系?

  坐标系这个概念最早是由法国数学家笛卡尔提出的,并由此创造了用代数方法来研究几何图形的数学分支——解析几何。解析几何的基本思想是将几何图形抽象成点的运动轨迹,从而点可以作为组成图形的基本元素,而描述一个点的位置首先需要建立合适的坐标系。所以,首先我们来了解下Unity3D中都有哪些坐标系吧!Unity3D中的坐标系目前可以分为以下四类:世界坐标、屏幕坐标、视图坐标和GUI坐标。下面我们来对这4类坐标进行详细说明:

世界坐标

世界坐标按照笛卡尔坐标系定义出来的绝对坐标系,下面的各种坐标系都建立在世界坐标的基础上。我们知道二维平面内任意一个点可以用二维坐标(x,y)来表示,如果将这个概念延伸到三维空间内,那么三维空间内任意一个点都可以用三维坐标(x,y,z)来表示。这就是世界坐标的概念啦,坐标系通常可以分为左手坐标系和右手坐标系,而Unity3D采用的是左手坐标系。在Unity3D中我们可以使用transform.position来获取场景中一个物体的世界坐标,通常情况下编辑器中的Inspector窗口是以世界坐标来描述一个3D物体的位置的,除非当一个3D物体存在父物体的时候,它会以相对坐标来描述其位置。

屏幕坐标

屏幕坐标是以像素来定义的,它的范围是以左下角为(0,0),右上角为(Screen.width,Screen.height)定义的这样一个矩形。屏幕坐标是一个3D坐标,Z轴是以相机的世界单位来衡量的。屏幕坐标和相机之间满足:Screen.width=Camera.pixelWidth和Screen.height=Camera.pixelHeight这两个条件。例如我们将相机正对着场景中的原点(0,0,0),相机的Z轴分量为-10,按照屏幕坐标的定义,假设屏幕为800X640的大小,则此时原点转化为屏幕坐标后应该是(400,320,10)。在Unity3D中我们可以使用WorldToScreenPoint来将一个世界坐标转换为屏幕坐标。

视口坐标

视口坐标是标准化后的屏幕坐标。标准化的概念我们可以引申到向量的标准化中,比如一个向量(x,y)将过标准化后可以得到一个单位向量(x’,y’)。类似地,视口坐标是以0到1间的数字来表示的,它的范围是以左下角为(0,0),右上角为(1,1)定义的这样一个矩形。视口坐标是一个3D坐标,Z轴是以相机的世界单位来衡量的。通过对比可以发现视口坐标和屏幕坐标特别的相似,所以这里大家可以对比着来学习。同样以屏幕坐标中的例子来将这里的转换,例如我们将相机正对着场景中的原点(0,0,0),相机的Z轴分量为-10,按照屏幕坐标的定义,假设屏幕为800X640的大小,则此时原点转化为屏幕坐标后应该是(0.5,0.5,10)。在Unity3D中我们可以使用WorldToViewportPoint来将一个世界坐标转换为视口坐标。

GUI坐标

GUI坐标是指通过OnGUI方法绘制UI时使用的坐标。这个坐标系和屏幕坐标类似,它同样是以像素来定义的,它的范围是以左上角为(0,0),右下角为(Screen.width,Screen.height)定义的这样一个矩形,GUI坐标是一个2D坐标(绝对坐标)。因为GUI在早期的文章中曾有所设计,属于Unity3D中较为基础的内容,此外我们知道使用绝对坐标来进行布局的话是没有办法做自适应的,所以这部分内容我们就不展开讲了,UI自适应的一个主要观点就是不要使用绝对坐标!不要使用绝对坐标!不要使用绝对坐标!重要的事情要说三遍的嘛!这里想特别说说Unity4.6以后推出的全新UI支持:uGUI。在uGUI的Screen
Space模式下,Unity3D的编辑器以屏幕坐标来显示UI元素的位置;在World Space模式下,Unity3D的编辑器以世界坐标来显示UI元素的位置。相信这一点大家在使用的时候都没有注意到,最近在看一个论坛帖子的时候发现对uGUI坐标转换的相关知识一片茫然,所以对这部分内容进行了深入的研究,最终得出的结论是:uGUI的坐标本质上是特殊的屏幕坐标,因为uGUI的Anchor决定了该坐标系的原点,pivot决定了元素本身坐标系的原点,正是这两个属性让uGUI的坐标看起来显得扑朔迷离。RectTransform组件继承自Transform,所以它们的position和localPosition是等价的,都是世界坐标;anchoredPosition是UI元素的屏幕坐标,在对UI元素进行操作的时候应该考虑使用这个坐标。

二、Unity3D各种坐标系间的转换

虽然Unity3D里提供了各种坐标系间的转换API,可是我们这是在写博客不是在说API不是?所以从实用性的角度出发,我们这里提供了一个Unity3D各种坐标系间转换的FAQ!

1、屏幕坐标如何转换为世界坐标?

在Unity3D中通过camera.ScreenToWorldPoint(Vector3 v)方法可以将一个屏幕坐标转化为世界坐标。其中,camera是游戏场景中的场景相机。通过Input.mousePosition或者 Input.touches[0].position可以获得鼠标或者单根手指的屏幕坐标。典型的应用场景是第一人称射击游戏中子弹的发射和RPG游戏中点击地面移动角色。

2、世界坐标如何转换为屏幕坐标?

在Unity3D中通过camera.WorldToScreenPoint(Vector3 v)方法可以将一个世界坐标转化为屏幕坐标。其中,camera是游戏场景中的UI相机。比如我们需要将一个世界坐标转换到NGUI坐标的时候,可以使用场景相机将世界坐标转为屏幕坐标,然后再利用UI相机将屏幕坐标转换为世界坐标,最后再赋值给NGUI控件。典型的应用场景是NPC头顶的文字显示和怪物头顶的血条显示。

3、屏幕坐标如何转换为视口坐标?

在Unity3D中可以通过camera.ScreenToViewportPoint()来将一个屏幕坐标转换为视口坐标,其中camera是游戏场景中的场景相机。当我们了解了视口坐标本质就是屏幕坐标的标准化坐标以后,这个真心没有什么可说的。

4、世界坐标如何转换为视口坐标?

在Unity3D中可以通过camera.WorldToViewportPoint()来将一个世界坐标转换为视口坐标,其中camera是游戏场景中的场景相机。当我们了解了视口坐标本质就是屏幕坐标的标准化坐标以后,这个真心没有什么可说的。

5、屏幕坐标如何转化为GUI坐标?

我们注意到屏幕坐标和GUI坐标的根本差异在于坐标系原点选定不同,即屏幕坐标的原点在左下角,而GUI坐标的原点在左下角。因此我们可以定义一个ScreenToGUIPoint方法:

[C#] 纯文本查看 复制代码

?
6、GUI坐标如何转换为屏幕坐标?

一个更为神奇的事情是你可以继续调用ScreenToGUIPoint方法。

7、屏幕坐标如何转换为uGUI坐标?

当我们接触到uGUI以后常常会遇到类似NGUI需要为uGUI控件坐标赋值的问题,此时我们我们有以下两种思路处理:

直接把屏幕坐标赋值给transform.position,这种方式简单粗暴在ScreenSpace-Overlay模式下没有问题,然而在ScreenSpace-Camera模式下会出现因为深度数值不正确而引发的问题,需要注意(并且需要确保被赋值的物体在Canvas的根节点下),前段时间有人在群里问我position和localPosition的区别,我就呵呵哒,现在人都是一般的浮躁啊,宁肯整天钻研某某插件的用法都不愿意钻研下API文档、计算机图形学、3D数学这些东西。

通过RectTransformUtility.ScreenPointToWorldPointInRectangle方法将屏幕坐标转化为世界坐标然后再赋值给transform.position。两种方法看起来大同小异,然而这种方法是更为保险的,因为它在这两种模式下都能稳定地运行。更重要的是它可以和uGUI的事件系统完美地融合。我猜测它是直接改变UI元素的世界坐标,因为它是赋值给transform.position。可是话说回来,以前给怪物头顶显示血条的时候都是直接把世界坐标转屏幕坐标再赋值给transform.position,这样看来当初的想法可能是错误的呀,如果要给UI控件屏幕坐标那不是应该给anchoredPosition赋值吗?好吧,这块儿暂时还没有搞明白,如果有朋友知道怎么回事,可以告诉我,谢谢!

好了,今天的内容就是这样啦,如果对文章中的内容有不明白或者质疑的地方,欢迎在博客里留言。目测今天写了篇虎头蛇尾的文章啊,然而最近一直没有比较好的状态怎么破,感觉快被自己打败了,哈哈!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐