Unity3D之空间转换学习笔记(一):场景物体变换

该系列笔记基于Unity3D 5.x的版本学习,部分API使用和4.x不一致。

目前在Unity3D中,除了新的UGUI部分控件外,所有的物体(GameObject)都必带有Transform组件,而Transform组件主要是控制物体在3D空间中的位置、旋转以及缩放。

学习和掌握物体的变换是Unity3D开发者必备的基础知识。

基础变换

最基础的变换就是通过脚本直接对物体的位置旋转缩放等进行变换。

匀速移动

我们下面实现一个匀速移动物体的效果,我们在场景中添加一个Cube物体,把下面的脚本绑定到摄像机上并把Cube拖拽赋予transfrom属性。

using UnityEngine;

using System.Collections;

public class Demo01Script : MonoBehaviour

{

    public Transform myTransform;

    void Start()

    {

    }

    void Update()

    {

        myTransform.position = new Vector3(myTransform.position.x, myTransform.position.y + 1.0f * Time.deltaTime, myTransform.position.z);

    }

}


运行游戏,我们会发现Cube会匀速上升。我们回到编辑场景,对Cube进行任意的旋转后运行游戏该Cube仍然是向上上升,这是因为位置和旋转是相互独立的,我们直接操作位置的话程序是不会理会旋转属性的,更换为localPosition效果也是一致的。

根据物体方向匀速移动

我们发现如果使用上面的方法来按照物体面向的方向移动物体是不容易的,我们需要根据物体的朝向计算出x、y、z这3个分量的数值再应用回物体中才行,这需要扎实的3维运算功底,不过好在Unity已经给我们提供了大量的属性及方法,方便我们直接调用来达到我们需要的效果。

本地坐标系变量

transform.right:物体本地坐标的x轴正方向朝向,1米的单位。

transform.up:物体本地坐标的y轴正方向朝向,1米的单位。

transform.forward:物体本地坐标的z轴正方向朝向,1米的单位。

由于我们知道了物体本地坐标的信息,所以可以方便的通过这个来按照物体的朝向移动物体了,比如,下面的代码会朝着物体的y轴正方向每秒1米的速度匀速移动:

using UnityEngine;

using System.Collections;

public class Demo01Script : MonoBehaviour

{

    public Transform myTransform;

    void Start()

    {

    }

    void Update()

    {

        Vector3 pos = myTransform.position;

        pos.x += myTransform.up.x * 1.0f * Time.deltaTime;

        pos.y += myTransform.up.y * 1.0f * Time.deltaTime;

        pos.z += myTransform.up.z * 1.0f * Time.deltaTime;

        myTransform.position = pos;

    }

}

坐标系转换

由于坐标系存在本地坐标系和世界坐标系两种,那么就需要有方法可以对这两种坐标系进行转换。

transform.localToWorldMatrix:本地坐标转世界坐标的矩阵信息。

transform.worldToLocalMatrix:世界坐标转本地坐标的矩阵信息。

transform.TransformDirection:将方向从本地坐标转换为世界坐标,不受缩放影响。

transform.InverseTransformDirection:将方向从世界坐标转换为本地坐标,不受缩放影响。

transform.TransformPoint:将位置从本地坐标转换为世界坐标,受缩放影响。

transform.InverseTransformPoint:将位置从世界坐标转换为本地坐标,受缩放影响。

transform.TransformVector:将坐标点从本地坐标转换为世界坐标,不受位置影响但受缩放影响。

transform.InverseTransformVector:将坐标点从世界坐标转换为本地坐标,不受位置影响但受缩放影响。

TransformPoint和TransformVector的区别

下面我们看看这两个方法的区别,首先,我们添加一个空物体到舞台并设置该物体的坐标为(1,1,1),然后把Cube对象拖入该空物体成为其子项设定其坐标为(2,2,2),修改脚本如下:

using UnityEngine;

using System.Collections;

public class Demo01Script : MonoBehaviour

{

public Transform myTransform;

void Start()

{

    Vector3 pos = myTransform.TransformPoint(new Vector3(1, 1, 1));

    Debug.Log(pos);

    //(4.0, 4.0, 4.0)

    Vector3 vec = myTransform.TransformVector(new Vector3(1, 1, 1));

    Debug.Log(vec);

    //(1.0, 1.0, 1.0)

}

void Update()

{

}

}


接下来我们把空物体的尺寸缩小一半看看结果会如何:



结论

TransformPoint转变会受物体的位置和缩放影响转换,而TransformVector仅受物体的缩放影响转换。

Demo01

这里做了一个示例,具体的功能是按下指定的键抓取到场景中的小盒子,使其始终位于屏幕前方,按下另一个键将这个小盒子抛出。

下面我们看核心的实现。

using UnityEngine;

using System.Collections;

public class Demo01Script : MonoBehaviour{ 

 public Transform cube; 

 void Start() { } 

 void Update() 

 {

 //抓取小盒子 if (Input.GetKey(KeyCode.Q))

 { 

 //设定小盒子的位置到屏幕前方 

 cube.transform.position = transform.TransformPoint(new Vector3(0, 0, 2)); 

 //将小盒子设定为读取对象的子对象, 保证跟随运动

 cube.transform.SetParent(transform); 

 //去掉物理交互 

 cube.GetComponent().isKinematic = true; 

 } 

 //扔出小盒子

 if (Input.GetKey(KeyCode.E)) 

 {

 if (cube.transform.parent == transform) 

 {

 //使用掉物理交互 

 cube.GetComponent().isKinematic = false;

 //解除所有子物件的绑定关系

 transform.DetachChildren(); 

 //获取方向 

 Vector3 cameraDirect = transform.TransformDirection(0, 0, 5); 

 //添加缓冲的力 

 cube.GetComponent().AddForce(cameraDirect, ForceMode.Impulse);

}

}

}

}

我们先将摄像机前的一个点转换为世界坐标赋予给小盒子的世界坐标使其位于摄像机之前,抛出时把摄像机向前方向的一个向量转换为世界方向赋予小盒子抛出。

位移

Unity3D里提供了方便控制物体位移的属性及方法。

本地和世界坐标

transform.position:设置和获取物件的世界坐标。

transform.localPosition:设置和获取物件的本地坐标,相对于父级的坐标。

注意,在Inspector面板中的Transform里显示的是本地坐标。

Translate

Transform的Translate方法可以更加方便的对物体的位移进行操作,该方法有四个重载:

1publicfunction Translate(translation: Vector3, relativeTo: Space = Space.Self):void;2publicfunction Translate(x:float, y:float, z:float, relativeTo: Space = Space.Self):void;

相对于本地坐标系或世界坐标系对物体进行位移操作。

1publicfunction Translate(translation: Vector3, relativeTo: Transform):void;2publicfunction Translate(x:float, y:float, z:float, relativeTo: Transform):void;

相对于指定物体的坐标进行位移操作。

注意:如果是相对于本地坐标系,则如果向上移动就是朝向物体本身的上方移动,如果是相对于世界坐标系则是向世界的上方向移动,如果是相对于其他物体则是向这指定的物体的上方向移动。

AnimationCurve

AnimationCurve可以用来定义自定义的动画轨迹,我们通过在脚本中声明一个该类型的对象,就可以在编辑器窗口对其进行编辑,然后使我们的物体按照编辑的轨迹进行移动等操作。

比如我们想要得到一个物体在X轴匀速移动,Y轴进行上下循环移动的时候,可以使用下面的脚本:

using UnityEngine;

using System.Collections;

public class Demo02Script : MonoBehaviour

{

public AnimationCurve myAnimationCurve;

public Transform myTransform;

void Start()

{

}

void Update()

{

myTransform.position = new Vector3(

myTransform.position.x + 1 * Time.deltaTime,

myAnimationCurve.Evaluate(Time.time * 0.5f) * 2,

myTransform.position.z);

}

}

编辑器编辑的曲线如下:



Demo02

在游戏中都会有一个最基本的需求,就是移动到指定的点,下面我们来实现一下这个基本的功能,脚本如下:

using System;

using UnityEngine;

using System.Collections;

public class Demo02Script : MonoBehaviour

{

 public Transform myTransform;

 public Transform myTarget; 

 private bool _isArrived = true; 

 private Vector3 _origin; 

 private Vector3 _target;

 private float _speed; 

 private Action _onArrived; 

 private float _allTime;

 private float _time;

 void Start()

 { 

 MoveTo(myTarget.position, 1, () => Debug.Log("I am arrived!"));

 } void Update() { if (!_isArrived) { _time += Time.deltaTime; //判断是否抵达终点 if (_time >= _allTime) { //校正位置 myTransform.position = _target; //标记到达和调用回调 _isArrived = true; if (_onArrived != null) { _onArrived(); } } else { //这里使用Lerp方法进行差值运算也可以得到相同的效果, 但是我们作为学习还是自己实现 //myTransform.position = Vector3.Lerp(_origin, _target, _time / _allTime); //获取方向的单位向量 Vector3 dirction = _target - _origin; dirction.Normalize(); //朝方向运动 myTransform.Translate(dirction * Time.deltaTime); } } } ////// 移动到指定点.

//////目标点.    ///移动速度, 米/秒.    ///到达后调用的方法.    private void MoveTo(Vector3 targetPosition, float speed, Action onArrived)    {        _isArrived = false;        _origin = myTransform.position;        _target = targetPosition;        _speed = speed;        _onArrived = onArrived;        //计算总共需要花费的时间        _allTime = Vector3.Distance(myTransform.position, _target) / _speed;        //重置使用的时间        _time = 0;    }}


运行后小盒子会想指定的物体进行匀速移动,到达后会输出“I am arrived!”的字符串。

旋转之欧拉角

欧拉角是由3个轴的旋转角度组成的旋转数据,比如我们在Inspector界面的Transform中看到的就是物体本地坐标系的欧拉角:



欧拉角每个轴数字都在0-360之间,表示其旋转的角度。

Rotate

官方提供的旋转方法,其一共有三个重载方法:

1publicfunction Rotate(eulerAngles: Vector3, relativeTo: Space = Space.Self):void;2publicfunction Rotate(xAngle:float, yAngle:float, zAngle:float, relativeTo: Space = Space.Self):void;

指定在本地坐标系或世界坐标系下旋转到指定的角度。

publicfunction Rotate(axis: Vector3, angle:float, relativeTo: Space = Space.Self):void;

指定在本地坐标系或世界坐标系下基于轴axis进行旋转,旋转到angle角度。

RotateAround

我们先看看其参数:

publicfunction RotateAround(point: Vector3, axis: Vector3, angle:float):void;

表示我们的物体围绕指定的点point在轴axis下旋转angle的角度。

LookAt

可以使物体面向指定的点,我们看看其参数:

1publicvoidLookAt(Transform target, Vector3 worldUp =Vector3.up);2publicvoidLookAt(Vector3 worldPosition, Vector3 worldUp = Vector3.up);

即使我们的物体面向指定的物体或点。

旋转之四元数

欧拉角理解和使用都相当的方便,但是在实际进行旋转时存在万向锁的问题,所以引入了比较抽象的四元数的概念,当然我们在Unity中只要直接使用即可,是非常方便的。

这里提供一个视频,可以让大家直观的了解什么是万向锁:http://v.youku.com/v_show/id_XNzkyOTIyMTI=.html

Quaternion

在Transform中,eulerAngles属性是使用欧拉角来表示旋转,而rotation属性则是使用四元数来表示旋转。

四元数提供了许多的静态方法来使我们完成特定需求的效果,点击这里可查看帮助。

Demo03

如果我们想要实现一个效果,物体匀速旋转到指定角度时,使用欧拉角对每个轴进行变换是相当复杂的,同时如果两个轴重合了就会出现万向锁的问题,无法解决,而使用四元数则可以避免这些问题,下面是实现的脚本:


using UnityEngine;

using System.Collections;

public class Demo03Script : MonoBehaviour

{

public Transform myTransform;

public Transform myTarget;

void Start()

{

}

void Update()

{

RotateToTarget();

}

private void RotateToTarget()

{

//获取目标方向的单位向量

Vector3 dicetion = (myTarget.position - myTransform.position).normalized;

//获取目标方向的四元数对象

Quaternion targetDicetion = Quaternion.LookRotation(dicetion);

//按照每秒 45 度的速度旋转面向目标对象

myTransform.rotation = Quaternion.RotateTowards(myTransform.rotation, targetDicetion, 45 * Time.deltaTime);

}

}


这样我们就可以使我们的物体匀速的转向指定的目标对象了。

缩放与位置关系

缩放

缩放比较简单,没有提供更多的方法。

Transform.lossyScale:只读,获取本物体相对于世界坐标的缩放大小。

Transform.localScale:设置或获取本物体相对于父级IDE缩放大小。

位置关系

在Unity3D中,所有3D对象是按照树形结构进行组合的,而操作物体之间的位置关系的所有API都存放在Transform对象中,下面我们看看常用的属性及方法。

属性

Transform.parent:设置和获取父级对象。

Transform.root:获取层次最高的对象。

Transform.childCount:获取子级对象的数量。

方法

Transform.Find:根据名字寻找子项。

Transform.IsChildOf:判断是否为指定Transform对象的子项。

Transform.DetachChildren:解除所有子项。

Transform.GetChild:根据索引获取子项。

Transform.GetSiblingIndex:获取同一级别的物体的索引。

Transform.SetAsFirstSibling:设置为同一级别的物体为第一个索引。

Transform.SetAsLastSibling:设置为同一级别的物体为最后一个索引。

Transform.SetSiblingIndex:设置同一级别的物体的索引。

工程文件下载

http://pan.baidu.com/s/1sjQJ5j3

天道酬勤,功不唐捐!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容