Unity中的坐标系与常用变换
一、三种常用坐标系
Unity中两个常用坐标系分别是是:世界坐标系(World Coordinate System)和局部坐标系(Local Coordinate System)。
- 世界坐标系
地球上某一点为参考,通常以地球表面某一点为原点。世界坐标系通常用于描述车辆在地图中的位置和路线规划。
在unity中,原点通常位于场景的中心,其轴定义通常是左手手坐标系(X轴指向右侧,Z轴指向前方,Y轴指向场景的上方)。
- 局部坐标系
以车辆当前位置为参考,通常以车辆当前位置为原点,车辆当前前进方向为Y轴正方向,车辆右侧为Z轴正方向,垂直于车辆底部向上为Y轴正方向。
局部坐标系通常用于描述车辆周围的环境,比如障碍物的位置和形状(雷达数据)。
- 自然坐标系(Frenet坐标系)
其实,在自动驾驶领域还有一个坐标系比较重要,叫做自然坐标系。
自然坐标系是以规划的轨迹作为参考建立的坐标系。
在自然坐标系中,有两个重要的单位矢量:
(1)切向单位矢量:沿着质点所在点的轨道切线方向。
(2)法向单位矢量:垂直于切向单位矢量并指向曲线的凹侧。
使用Frenet坐标系的原因主要有两点:
(1)可以将车辆的纵向控制与横向控制解耦,这意味着可以减少计算量。通过在世界坐标系和Frenet坐标系之间相互转换,可以更方便地进行车辆控制。
(2)可以将不规则的轨迹转换为直线轨迹,这样可以更容易地建立数学模型和方程,从而更方便地进行路径规划和车辆控制。
二、Unity中Gameobject
与transform
在Unity中,GameObject
和Transform
是两个核心的概念,它们在游戏对象和它们的变换中扮演着重要的角色。下面我会详细介绍这两个概念:
1、GameObject
GameObject
是Unity中所有实体的基类,它代表了游戏场景中的一个实体。
这些实体可以是玩家、NPC、物体、灯光、摄像机等等。
GameObject
本身不包含太多功能,但它可以作为一个容器,允许你添加不同类型的组件(变换、渲染器、碰撞器、脚本等)来赋予其特定的功能。
在C#脚本中,你可以通过以下方式来获取和操作GameObject
:
// 获取当前对象的GameObject
GameObject myGameObject = this.gameObject;
// 通过名称查找GameObject
GameObject otherGameObject = GameObject.Find("ObjectName");
// 创建新的GameObject
GameObject newGameObject = new GameObject("NewObject");
2、Transform
Transform
组件定义了GameObject
的位置、旋转和缩放信息。
每个GameObject
都有一个关联的Transform
组件,这意味着可以通过访问GameObject
的transform
属性来获取或修改其变换属性。
Transform
提供了一系列的属性和方法,如position
、rotation
和localScale
,用于控制和操作GameObject
的变换属性。
在C#脚本中,你可以通过以下方式来获取和操作Transform
:
// 获取当前对象的Transform
Transform myTransform = this.transform;
// 获取当前对象的位置
Vector3 position = myTransform.position;
// 设置当前对象的位置
myTransform.position = new Vector3(0, 0, 0);
// 获取当前对象的旋转
Quaternion rotation = myTransform.rotation;
// 设置当前对象的旋转
myTransform.rotation = Quaternion.Euler(0, 0, 0);
// 获取当前对象的缩放
Vector3 scale = myTransform.localScale;
// 设置当前对象的缩放
myTransform.localScale = new Vector3(1, 1, 1);
3、二者对比
GameObject
(实体对象)是Unity场景中的实体对象,可以添加各种组件来赋予其功能。Transform
(社会关系)是GameObject
的一个组件,用于控制GameObject
的位置、旋转和缩放属性。- 一般来说,我们认为Unity中场景树是以
Transform
为树节点,GameObject
为叶子节点的树结构。这也比较容易理解,毕竟Transform
用于控制GameObject
的社会关系
。 - 既可以通过
GameObject
来获取Transform
,也可以通过Transform
来获取GameObject
,二者是可以互相索引的。
// 根据GameObject获取当前对象的Transform
Transform myTransform = myGameObject.transform;
// 根据Transform获取当前对象的GameObject
GameObject myGameObject = myTransform.gameObject;
// 当然,也可以通过GameObject的transform组件来获取Transform
Transform myTransform = myGameObject.GetComponent<Transform>();
// 多数情况下,在类中,我们可以直接使用this来获取当前对象的GameObject和Transform
GameObject myGameObject = this.gameObject;
Transform myTransform = this.transform;
// 甚至this也可以省略
GameObject myGameObject = gameObject;
Transform myTransform = transform;
三、如何查找对象
在Unity中,你可以使用多种方法来索引和查找对象。以下是一些常用的方法:
1、通过名称查找
GameObject obj = GameObject.Find("ObjectName");
2、通过标签查找
GameObject[] objs = GameObject.FindWithTag("TagName");
// 或者
GameObject obj = GameObject.FindGameObjectWithTag("TagName");
3、通过组件查找
ComponentType component = gameObject.GetComponent<ComponentType>();
4、通过层级查找**:
GameObject childTransform = this.transform.Find("ChildName").gameObject;
5、通过层级路径查找**:
使用Transform.Find
方法通过完整的层级路径查找对象。
GameObject childTransform = this.transform.Find("ParentName/ChildName").gameObject;
6、通过标签和名称查找
使用FindGameObjectWithTag
或FindWithTag
与Find
结合使用来查找特定标签下的特定名称的对象。
GameObject obj = GameObject.FindGameObjectWithTag("TagName").transform.Find("ChildName").gameObject;
请注意,使用Find
方法和FindWithTag
方法是比较耗性能的操作,因此应尽量避免在每一帧中多次调用这些方法。
如果可能的话,最好是在Start
方法中进行一次查找,并将结果存储在一个变量中以供后续使用。
多事情况下,建议使用层级查找对象,这样可以避免不必要的性能开销。
四、transform的常用操作
1、transform的属性
Transform
组件提供了许多属性,用于获取或设置游戏对象的变换信息。以下是Transform
类中常用的一些属性:
(1)位置、旋转和缩放
position
获取或设置对象在世界空间中的位置。Vector3 worldPosition = transform.position;
localPosition
获取或设置对象在其父对象局部空间中的位置。Vector3 localPosition = transform.localPosition;
rotation
获取或设置对象在世界空间中的旋转。Quaternion worldRotation = transform.rotation;
localRotation
获取或设置对象在其父对象局部空间中的旋转。Quaternion localRotation = transform.localRotation;
localScale
获取或设置对象在其父对象局部空间中的缩放。Vector3 localScale = transform.localScale;
(2)层级和父对象
parent
获取或设置对象的父对象。Transform parentTransform = transform.parent;
childCount
获取对象的子对象数量。int childCount = transform.childCount;
(3)查找和访问子对象
Find(string name)
在当前对象的子对象中查找具有指定名称的对象。Transform childTransform = transform.Find("ChildObjectName");
(4)其他属性
forward
获取对象前方的向量。Vector3 forwardDirection = transform.forward;
right
获取对象右侧的向量。Vector3 rightDirection = transform.right;
up
获取对象上方的向量。Vector3 upDirection = transform.up;
当然,这里是Transform
常用的方法总结:
2、transform的方法
(1)位置操作
Translate(Vector3 translation)
沿着当前对象的局部坐标系移动对象。transform.Translate(Vector3.forward * Time.deltaTime);
SetPositionAndRotation(Vector3 position, Quaternion rotation)
设置对象的位置和旋转。transform.SetPositionAndRotation(new Vector3(1, 2, 3), Quaternion.Euler(0, 90, 0));
(2)旋转操作
Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self)
按照给定的欧拉角旋转对象。transform.Rotate(new Vector3(0, 90, 0), Space.World);
RotateAround(Vector3 point, Vector3 axis, float angle)
绕指定点和轴旋转对象。transform.RotateAround(Vector3.zero, Vector3.up, 30 * Time.deltaTime);
(3)缩放操作
Scale(Vector3 scale)
按照给定的缩放因子缩放对象。transform.localScale += new Vector3(0.1f, 0.1f, 0.1f);
(4)查找操作
Find(string name)
在当前对象的子对象中查找具有指定名称的对象。Transform childTransform = transform.Find("ChildObjectName");
(5)层级操作
SetParent(Transform parent, bool worldPositionStays = true)
将对象设置为新的父对象,并选择是否保持其在世界空间中的位置。otherTransform.SetParent(transform);
在使用这些方法时,需要注意对象的变换是基于局部空间还是世界空间进行的,并根据需要选择合适的空间类型(Space.Self
或 Space.World
)。
五、坐标变换
1、位置坐标
确实,Unity中的位置通常使用Vector3
来表示。
Vector3
是一个三维向量,用于表示三维空间中的点或方向。
(1)获取和设置位置
你可以通过Transform
组件的position
属性来获取或设置游戏对象的位置。
// 获取游戏对象的当前位置
Vector3 currentPosition = transform.position;
// 设置游戏对象的新位置
transform.position = new Vector3(1.0f, 2.0f, 3.0f);
(2)向量运算
Vector3
支持各种基本的向量运算,如加法、减法和标量乘法。
Vector3 vector1 = new Vector3(1.0f, 2.0f, 3.0f);
Vector3 vector2 = new Vector3(4.0f, 5.0f, 6.0f);
// 向量加法
Vector3 sum = vector1 + vector2;
// 向量减法
Vector3 difference = vector1 - vector2;
// 向量标量乘法
Vector3 scaledVector = vector1 * 2.0f;
(3)常用属性
除了position
属性,Vector3
还有其他一些常用属性和方法,如magnitude
(向量的长度)和normalized
(单位化向量)。
Vector3 vector = new Vector3(1.0f, 2.0f, 3.0f);
// 获取向量的长度
float length = vector.magnitude;
// 获取向量的单位向量
Vector3 normalizedVector = vector.normalized;
2、角度坐标
在Unity中,角度通常使用Quaternion
或Vector3
来表示。具体来说:
(1) Quaternion
Quaternion
是一个四元数,用于表示旋转。
Quaternion.Euler
允许你通过欧拉角(Euler angles)来创建一个Quaternion
。
Quaternion
可以避免万向节锁定(Gimbal Lock)等问题。
// 创建一个绕X轴旋转90度的Quaternion
Quaternion rotation = Quaternion.Euler(90, 0, 0);
// 或者
Quaternion rotDown = Quaternion.AngleAxis(90.0f, transform.right);
(2) Vector3
Vector3
也可以用于表示角度,尤其是当你想要直接操作和检查欧拉角时。
然而,需要注意的是,Vector3
中的欧拉角单位是度(degrees),而Quaternion
中的欧拉角单位是弧度(radians)。
// 创建一个表示绕X轴旋转90度的Vector3
Vector3 eulerAngle = new Vector3(90, 0, 0);
(3) Quaternion与Vector3的互换
// Vector3 转 Quaternion
eulerAngle = new Vector3(90, 0, 0);
Quaternion rotationFromVector = Quaternion.Euler(eulerAngle);
// Quaternion 转 Vector3
rotation = Quaternion.Euler(90, 0, 0);
Vector3 eulerFromRotation = rotation.eulerAngles;
3、在全局坐标系与局部坐标系中进行变换
在Unity中,Transform
组件提供了多种方法来在全局坐标系和局部坐标系中进行位置和角度的变换。以下是如何在这两种坐标系中进行变换的方法:
(1) 全局坐标系(World Space)
在全局坐标系中,位置和角度是相对于世界原点的。使用Translate
和Rotate
方法时,默认情况下,它们都是在全局坐标系中进行的。
位置变换
// 沿着全局Y轴移动对象
transform.Translate(Vector3.up * Time.deltaTime, Space.World);
2. 角度变换
// 绕全局Y轴旋转对象
transform.Rotate(Vector3.up * Time.deltaTime, Space.World);
(2) 局部坐标系(Local Space)
在局部坐标系中,位置和角度是相对于父对象的。当你在子对象上应用变换时,这些变换是在子对象的局部坐标系中进行的。
位置变换
// 沿着局部Y轴移动对象
transform.Translate(Vector3.up * Time.deltaTime, Space.Self);
角度变换
// 绕局部Y轴旋转对象
transform.Rotate(Vector3.up * Time.deltaTime, Space.Self);
(3) 强制坐标转换
从局部坐标系转换到全局坐标系
Vector3 localPoint = new Vector3(1, 2, 3);
Vector3 globalPoint = transform.TransformPoint(localPoint);
从全局坐标系转换到局部坐标系
Vector3 globalPoint = new Vector3(4, 5, 6);
Vector3 localPoint = transform.InverseTransformPoint(globalPoint);