该系列笔记基于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. UVa 10341 (二分求根) Solve It

    很水的一道题,因为你发现这个函数是单调递减的,所以二分法求出函数的根即可. #include <cstdio> #include <cmath> //using namespa ...

  2. ADO和DAO的区别

    ADO(ACTIVEX DATA OBJECTS)应用层的数据访问接口ODBC 数据库驱动接口OLE DB 系统级数据访问接口DAO (DATA ACCESS OBJECTS) 对象的数据访问接口AD ...

  3. Jquery 模板插件 jquery.tmpl.js 的使用方法(1):基本语法,绑定,each循环,ajax获取json数据

    jquery.tmpl.js 是一个模板js  ,主要有2个方法 (1):$.template()方法,将一段script或者是Html编译为模板,例如 $.template('myTemplate' ...

  4. ES PES TS

    1.流媒体系统结构 ES:elemental stream 基本数据流: PES:packet elemental stream分组的基本数据流: 然后把PES打包成PS ,TS流,PS:progra ...

  5. 【转】linux下a.out >outfile 2>&1重定向问题

    原文网址:http://blog.chinaunix.net/uid-25909722-id-2912890.html 转自:http://blog.chinaunix.net/space.php?u ...

  6. Oracle 课程五之优化器和执行计划

    课程目标 完成本课程的学习后,您应该能够: •优化器的作用 •优化器的类型 •优化器的优化步骤 •扫描的基本类型 •表连接的执行计划 •其他运算方式的执行计划 •如何看执行计划顺序 •如何获取执行计划 ...

  7. windows主线程等待子线程退出卡死问题

    在windows下调用_beginthread创建子线程并获得子线程id(函数返回值),如果子线程很快退出,在主线程中调用WaitForSingleObject等待该线程id退出,会导致主线程卡死.需 ...

  8. HDU5731 Solid Dominoes Tilings 状压dp+状压容斥

    题意:给定n,m的矩阵,就是求稳定的骨牌完美覆盖,也就是相邻的两行或者两列都至少有一个骨牌 分析:第一步: 如果是单单求骨牌完美覆盖,请先去学基础的插头dp(其实也是基础的状压dp)骨牌覆盖 hiho ...

  9. 【LeetCode 213】House Robber II

    This is an extension of House Robber. After robbing those houses on that street, the thief has found ...

  10. 《Python 学习手册4th》 第十三章 while和for循环

    ''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容 (每天看42页内容,可以保证月底看完此书) “重点 ...