Unity 中的坐标系

前言

在制作魔剑镇魂曲的 TileMap 时用到了各种坐标系之间的转换, 当时被整得云里雾里的, 现在来回顾一下. 游戏中我只用到了笛卡尔坐标系, 没有用到极坐标系, 后面就直接简称为坐标系了.

坐标系与坐标

  • 首先需要强调一下坐标系和坐标完全不是一回事, 文章中有很多地方涉及到坐标系和坐标, 为了避免混淆, 一定要在脑海中清晰地区分两者. 坐标系是一个系统, 而坐标只是一个表示位置的数字.

  • 坐标是不可能独立存在的, 它必须依存于某个坐标系, 因此很多时候我们就直接简称为某某坐标. 比如在屏幕坐标系中一个点的坐标简称为 "屏幕坐标", 在世界坐标系中一个点的坐标简称为 "世界坐标".

分类

Unity 中的坐标大致分为 4 种:

坐标 关键字
世界坐标 World Point
本地坐标 Local Point
屏幕坐标 Screen Point
视图坐标 View Point

世界坐标 (World Point)

  • Unity 中有一个覆盖全部游戏物体的坐标系, 所有的游戏物体都靠这个坐标系来确定自身在游戏世界中的准确位置, 这个坐标系就是世界坐标系. 有时为了明显和 "局部坐标系" 相对应也被称为 "全局坐标系". 在世界坐标系中得出的点的坐标称为世界坐标 (World Point).

  • 但是遗憾的是在游戏物体 Inspector 面板中, Transform 处显示的 Position 是在父物体坐标系中的坐标, 而不是在世界坐标系中的坐标. 只有当物体没有父物体的时候, Transform 处显示的 Position 才是在世界坐标系中的坐标.

  • 但是在脚本中, 使用 transform.position 获取的却是游戏物体的世界坐标.

本地坐标 (Local Point)

  • Unity 中每个游戏物体都有其自身坐标系, 其子物体便是使用这个坐标系来确定与父物体的相对位置关系. 在父物体坐标系下得出的点的坐标称为本地坐标 (Local Point).

  • 由于本地坐标系和物体绑定, 因此当游戏物体进行旋转, 移动的时候, 坐标系也会进行相应的旋转和移动. 但是本地坐标却有一个特点: 不变性. 当游戏物体进行旋转, 移动的时候, 其子物体的本地坐标是不变的, 即子物体相对于自身的相对位置不会改变, 因此子物体会跟随其父物体旋转, 移动.

  • Unity 中模型 Mesh 保存的顶点坐标均为本地坐标, 因为本地坐标的不变性, 即使物体进行旋转或者移动, 也不会影响顶点的坐标.

  • 在脚本中使用 transform.position 获取的是游戏物体的世界坐标, 使用 transform.localPosition 获取的便是游戏物体的本地坐标. 但是当物体没有父物体的时候, transform.localPosition 获取的也是世界坐标.

屏幕坐标 (Screen Point)

  • 屏幕坐标系是基于游戏窗口建立的坐标系.

  • 屏幕坐标系以像素为单位, 窗口的左下角坐标为 (0, 0), 右上角坐标为窗口大小. 脚本中可以通过 (Screen.width, Screen.height) 来获取窗口大小.

  • 脚本中使用 Input.mousePosition 可以获得鼠标坐标, 这个坐标便是基于屏幕坐标系计算得来的. 另外使用 Input.GetTouch(0).position 可以获得单个手指触摸屏幕时手指的坐标, 这个坐标也是基于屏幕坐标系计算得来的.

  • 需要注意的是屏幕坐标不是根据玩家显示器的大小建立的坐标系, 而是以游戏窗口建立的坐标系, 比如玩家屏幕为: 1920 X 1080, 但是游戏窗口是: 800 X 600, 那么右上角的坐标是 (800, 600).

视图坐标 (View Point)

  • 摄像机预览窗口显示的画面, 也是基于游戏窗口建立的坐标系.

  • 视图坐标系的大小为单位 1, 窗口的左下角坐标为 (0, 0), 右上角坐标为 (1, 1).

  • 屏幕坐标单位化后得到的就是视图坐标.

参考文章

  • Unity中屏幕坐标、视口坐标和世界坐标、局部坐标与其之间的相互转换

  • 理解Unity3D中的四种坐标体系