该系列笔记基于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(, , ));
//将小盒子设定为读取对象的子对象, 保证跟随运动
cube.transform.SetParent(transform);
//去掉物理交互
cube.GetComponent<Rigidbody>().isKinematic = true;
}
//扔出小盒子
if (Input.GetKey(KeyCode.E))
{
if (cube.transform.parent == transform)
{
//使用掉物理交互
cube.GetComponent<Rigidbody>().isKinematic = false;
//解除所有子物件的绑定关系
transform.DetachChildren();
//获取方向
Vector3 cameraDirect = transform.TransformDirection(, , );
//添加缓冲的力
cube.GetComponent<Rigidbody>().AddForce(cameraDirect, ForceMode.Impulse);
}
}
}
}

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

位移

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

本地和世界坐标

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

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

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

Translate

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

 public function Translate(translation: Vector3, relativeTo: Space = Space.Self): void;
public function Translate(x: float, y: float, z: float, relativeTo: Space = Space.Self): void;

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

 public function Translate(translation: Vector3, relativeTo: Transform): void;
public function 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 + * Time.deltaTime,
myAnimationCurve.Evaluate(Time.time * 0.5f) * ,
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, , () => 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);
}
}
} /// <summary>
/// 移动到指定点.
/// </summary>
/// <param name="targetPosition">目标点.</param>
/// <param name="speed">移动速度, 米/秒.</param>
/// <param name="onArrived">到达后调用的方法.</param>
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 = ;
}
}

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

旋转之欧拉角

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

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

Rotate

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

 public function Rotate(eulerAngles: Vector3, relativeTo: Space = Space.Self): void;
public function Rotate(xAngle: float, yAngle: float, zAngle: float, relativeTo: Space = Space.Self): void;

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

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

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

RotateAround

我们先看看其参数:

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

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

LookAt

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

 public void LookAt(Transform target, Vector3 worldUp = Vector3.up);
public void LookAt(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, * 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

Unity3D之空间转换学习笔记(一):场景物体变换的更多相关文章

  1. Unity3D之空间转换学习笔记(二):基础数学

    这期笔记我们专注Unity提供的各种数学相关的类来学习. 时间Time API文档地址:http://docs.unity3d.com/ScriptReference/Time.html 时间加/减速 ...

  2. Unity3D之空间转换学习笔记(三):3D数学

    3D数学基础 向量 向量可以看做具有方向和大小的一条线段. 比如:我们如果用点A减去点B,则可以得到一个向量,该向量的方向为点B面向点A的方向,而大小为两点的距离.这个方法在游戏开发中经常用到,比如我 ...

  3. Unity3D 骨骼动画原理学习笔记

    最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...

  4. Unity3d之Hash&Slash学习笔记之(二)--角色基础类的构建

    Hash&Slash学习笔记之(二)--角色基础类的构建 BaseStat类的构建 基本成员变量: _baseValue //基础属性值 _buffValue //增加的buff值 _expT ...

  5. SharpGL学习笔记(七) OpenGL的变换总结

    笔者接触OpenGL最大的困难是: 经常调试一份代码时, 屏幕漆黑一片, 也不知道结果对不对,不知道如何是好! 这其实就是关于OpenGL"变换"的基础概念没有掌握好, 以至于对& ...

  6. [学习笔记]NTT——快速数论变换

    先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...

  7. Yii2学习笔记之场景

    场景 一个模型可能在多个场景中使用,在不同的场景中,模型可能使用不同的业务逻辑和规则.例如, User 模型可能在用户登录时使用,也可能在用户注册时使用,某些属性可能在用户注册时强制要求有,在用户登录 ...

  8. Cocos2dx 学习笔记整理----场景切换

    据说Cocos2dx场景切换的方法有32种:cocos2dx 常见的32种切换场景的动画 无需一一求证,只需要知道切换场景需要怎么做就行了. 作为导演CCDirector,切换场景的事情当然归它管了. ...

  9. Redis 学习笔记-应用场景

    Redis作缓存系统 Redis可以对每个键设置生存时间 可以限定数据占用的最大内存空间,在数据达到空间限制后可以按照一定规则自动淘汰不需要的键. 设置方法: 修改配置文件的maxmemory参数,限 ...

随机推荐

  1. 实现窗口逐渐增大(moveTo(),resizeTo(),resizeBy()方法)

    moveTo()方法格式:window.moveTo(x,y); 功能:将窗口移动到指定坐标(x,y)处; resizeTo()方法格式:window.resizeTo(x,y); 功能:将当前窗口改 ...

  2. Codeforces Round #272 (Div. 2)

    A. Dreamoon and Stairs 题意:给出n层楼梯,m,一次能够上1层或者2层楼梯,问在所有的上楼需要的步数中是否存在m的倍数 找出范围,即为最大步数为n(一次上一级),最小步数为n/2 ...

  3. 使用讯飞SDK,实现文字在线合成语音

    private SpeechSynthesizer mTts; private int isSpeaking = 0; mTts= SpeechSynthesizer.createSynthesize ...

  4. UVA 10917 Walk Through the Forest(dijkstra+DAG上的dp)

    用新模板阿姨了一天,换成原来的一遍就ac了= = 题意很重要..最关键的一句话是说:若走A->B这条边,必然是d[B]<d[A],d[]数组保存的是各点到终点的最短路. 所以先做dij,由 ...

  5. linux lnmp编译安装

    关闭SELINUX vi /etc/selinux/config #SELINUX=enforcing #注释掉 #SELINUXTYPE=targeted #注释掉 SELINUX=disabled ...

  6. java中时间格式yyyyMMddHHmmss的大小写问题

    字母     日期或时间元素 表示 示例 G Era 标志符 Text AD y 年 Year 1996 ; 96 M 年中的月份 Month July ; Jul ; 07 w 年中的周数 Numb ...

  7. 面试题 IQ

    现在有一大块金条,它可以分为七小块金条.是这样子的,工人为你工作7天,每天都将获得一小块金条,你要做的就是发工资,切割大块金条的次数最多两次,你有什么方法让工人每天都获得一小块金条呢?

  8. mac下SSH很快被断开

    解决方法: 1. 切换到root账号:sudo bash -c 'su - root' 2. 修改/etc/ssh_config文件 ServerAliveCountMax 5 ServerAlive ...

  9. 请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间

    链接:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之间的 ...

  10. OpenGL超级宝典第5版&&GLSL法线变换

    在GLSL中,有一些情况需要把局部坐标系下的向量或点转换到视点坐标系下,如光照计算时,需要把法向转化到视点坐标系.如果是模型上一点p 转化到视点坐标系下,直接(model-view matrix )* ...