Transform
GameObject和Transform
Transform是每个GameObject都具有的属性,Transform是GameObject的一个特殊的Component。尝试删除Transform时会发现,Unity并没有提供可以删除Transform组件的选项,也就是说GameObject必须具有Transform组件。同样的,也没有任何方法添加Transform组件,也就是说GameObject只能具有一个Transform组件。
一个GameObject只对应一个Transform,一个Transform也只对应一个GameObject。在脚本中,我们可以直接通过gameObject获得对应Transform组件,也可以通过transform获得对应的GameObject,同时,用GetComponent方法也可以获得Transform组件:
public GameObject MonoBehaviour.gameObject{get;}
public Transform MonoBehaviour.transform{get;}
public Tranform GameObject.tranform{get;}
public GameObject Tranform.gameObject{get;}
gameObjcet.tranform == gameObject.GetComponent<Transform>();
tranform == gameObject.tranform.gameObject.GetComponent<Tranform>();
在Inspector中,每个GameObject的第一个组件都是Transform。Transform暴露出来的属性包括Position、Rotation和Scale,顾名思义,它们就是物体的位置、旋转、缩放信息。在Scene中移动、旋转、缩放物体,可以观察到Inspector中的Transform改变,而在Inspector中改变这三个属性,也可以在Scene和Game面板中看到修改的效果。
默认情况下,position、rotation均为零向量,scale为(1,1,1)。在Inspector面板中对Transform使用Reset即可使三个属性重置为默认值。注意,使本地座标归零的结果是使该物体与父物体座标系重合。
复习本地座标系与世界座标系:
本地座标系指物体的父节点的模型座标空间,如果物体没有父节点,则本地座标系等于世界座标空间。
本地座标是在本地座标系中的座标,而世界空间座标是在世界座标系中的座标。
Transform和座标信息
transform.position = new Vector3(1,2,3);
transform.localPosition = new Vector3(1,2,3);
transform.position += new Vector3(1,0,0);
transform.localPosition += new Vector3(-1,0,0);
通过position可以访问物体的世界座标,通过localPosition可以访问物体的本地座标。
由于Vector3重载了加减运算,故可以使用“+=”来修改position。
除了直接对position修改外,还可以调用Transform的函数:
public void Transform.Translate(float x, float y, float z, Space relativeTo = Space.Self);
public void Transform.Translate(Vector3 translation, Space relativeTo = Space.Self);
Space是一个枚举类型,具有两个枚举值:Space.Self和Space.World。前者使移动作用于本地座标,后者使移动作用于世界座标。
由于在Vector3类中,x、y、z属性是只读的,有时会造成麻烦,我们可以自己扩展方法,如下:
public static class Extensions
{
public static void SetPositionX(this Transform t, float newX)
{
t.position = new Vector3(newX, t.position.y, t.position.z);
}
}
扩展已有类时,我们需要一个静态类中的静态方法,这个方法的第一个参数增加this关键字,第一个参数的类型就是我们想要扩展的类型,这样我们就可以通过Transform对象直接调用这个方法了:
transform.SetPositionX(1);
public Quaternion Transform.rotation;
public Quaternion Transform.localRotation;
通过Transform的属性获取轴:
public vector3 Transform.up{get; private set;}
public vector3 Transform.forward{get; private set;}
public vector3 Transform.right{get; private set;}
通过Transform的方法旋转物体:
public void Transform.Rotate(Vector3 eulers, Space relativeTo = Space.Self);
public void Transform.Rotate(float x, float y, float z, Space relativeTo = Space.Self);
public void Transform.Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
public void Transform.RotateAround(Vector3 point, Vector3 axis, float angle);
public void Transform.LookAt(Vector3 worldPosition, Vector3 worldUp = Vector3.up);
public void Transform.LookAt(Transform target, Vector3 worldUp = Vector3.up);
Rotate函数具有两种完全不一致的效果,很容易使人产生困惑。在前两种重载中,物品以当前方向为基准重新进行欧拉变换,即沿z轴->y轴->x轴的顺序依次旋转指定角度,得到新的方向。而后一种重载中,物品绕自身的axis方向的轴,遵循左手螺旋定则的旋转angle角度。
RotateAround函数中,物体绕目标座标在axis方向的轴旋转angle度,旋转过程中座标和自身方向都会改变。
LookAt函数可以使物体的正面朝向某个点或Transform,所谓正面即z轴的正方向。在不使用第二个参数的情况下,物体只绕y轴旋转,而在使用第二个参数的情况下,物体的y轴正方向会指向第二个参数的方向。
Unity中没有使物体朝向某个方向的函数,但这个方法在移动时又十分常用,我们可以扩展一个LookTo方法来快捷的使用这个功能:
public static class Extensions
{
public static void LookTo(this Transform t, Vector3 direction, Vector3 worldUp = Vector3.up)
{
t.LookAt(t.position + direction,worldUp);
}
}
public vector3 Transform.localScale;
public vector3 Transform.lossyScale{get; private set;}
注意,世界座标的大小是只读的。
推荐尽量少使用改变缩放的功能:一些缩放可以改用动画系统实现,还有更多情况下,模型或图片的大小应该由美术确定好后直接导入,GameObject的Scale应该尽量都保持(1,1,1)。
Transform和树型结构
让相关的GameObject尽量按照树型结构组织,可以有效的提高搜索物体的效率,也减少不必要的内存损耗。
public Transform Transform.parent{get; private set;}
public Transform Transform.root{get; private set;}
public bool Transform.IsChildOf(Transform parent);
public void SetParent(Transform parent, bool worldPositionStays = true);
注:当没有父节点时parent返回null,在SetParent中输入参数null也可以让物体不再有父节点,在IsChildOf中输入参数null可以判断物体是否没有父节点。
root代表最终根节点,可以返回自己。
SetParent函数的第二个参数为false时,物体会改变在世界座标系中的位置,在新的座标系中保持和在原本座标系中一致的本地座标,即和新父物体的相对位置等于和旧父物体的相对位置。
public Transform Transform.Find(string n);
public Transform Transform.GetChild(int index);
public void Transform.DetachChildren();
使用Find可以根据物体名称搜索此节点派生出的所有后继节点,是GameObject.Find的范围缩小版本。
GetChild是速度最快的查找子物体的方法,所以让Prefabs或者GameObject以一个固定的顺序排列是一个很好的习惯。
DetachChildren可以一次性断开对所有子物体的连接,并使子物体移动到同一层级,即如果本物体有父物体,断开连接后子物体会成为原本父物体的子物体,成为本物体的兄弟节点。