Unity3d里面Transfrom关于旋转的变量是rotation,rotation是一个四元数,四元数就应该有四个值。

  但是在编辑器里面,rotation里面只有三个值,这是为啥呢?

  在Unity里面,一个正面朝上的Plane,他的EularAngles是new vector3(0,0,0)

  但是当我们把他的EularAngles改成new vector3(-180,-180 ,-180 )的时候,发现他仍旧是正面朝上

  那我们不用new vector3(-180,-180 ,-180 ),我们用new vector3(-1800,-1800 ,-1800)可不可以,表达正面朝上的角度?也行!

  所以用Vector3的欧拉角来表示一个角度,可以有千千万万的表示方法!那么我们在计算角度的时候偶尔会把这些重复的角度也计算进去,造成各种各样意向不到的bug和计算错误。这种错误怎么避免呢?聪明的人类引入了一个来自四维空间的数,这个数叫做四元数Quaternion,四元数的最本质是为了解决欧拉角的万向锁和无限自增的问题!

  下面用一个摄像机跟随来表现一下,使用四元数计算角度在游戏开发过程中相对于欧拉角的优势在哪。

  如图项目,我们想做一个类似GTA5摄像机跟随的效果,

  那么第一步,肯定是要记录摄像机的相对位置

  我们通过摄像机的世界坐标-玩家的世界坐标来获得相对位置。

  那么这样,在每一帧让摄像机的位置,等于主角加方向,在直线上就可以实现摄像机跟随了。

  但是如果玩家转身了呢?这个相对位置怎么算?

  由上图我们可以看到转身的过程模拟,其中红色的代表主角,黑色的代表摄像机,虚线表示之前的位置,绿线代表之前算的摄像机相对主角的方向。

  那么我们可以很清楚的看出来,通常主角模型没有弄错的话,转身基本上都是转Y轴,主角顺时针转了45度,摄像机相对主角的方向向量相对于自身转了

  45度。所以看似很复杂的问题,其实一点都不负责,我们只要让之前算出来的方向*主角旋转值的四元数就行了。

  PS:四元数*向量等于旋转之后的向量,不理解原理的话就死记,在3D数学里面是很重要的公式!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using  UnityEngine;
using  System.Collections;
public  class  CameraFollow : MonoBehaviour {
public  GameObject mPlayer;  //获得主角
private  Vector3 m_Dir;  //获得摄像机相对于主角的方向
// Use this for initialization
void  Start () {
m_Dir =  this .transform.position - mPlayer.transform.position;
}
// Update is called once per frame
void  Update () {
this .transform.position = mPlayer.transform.rotation * m_Dir + mPlayer.transform.position; //摄像机的新位置 = 主角位置 + 主角的旋转值 * 摄像机相对于主角的方向
}
}

  但是我们发现,主角旋转之后,摄像机跟着转了,但是摄像机角度没有更正过来,于是我们接着修改摄像机角度,和上面的一样,主角的旋转值=摄像机的旋转值。于是我们可以直接用欧拉角来赋值,但是一开始摄像机是朝下看的,所以它的旋转值的欧拉角的x轴不为0,但是人物旋转值得x是等于0的,所以我要在最后修改摄像机的旋转值的欧拉角的x轴。

  (摄像机角度对了,但是方向没转过来)

  修改后代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using  UnityEngine;
using  System.Collections;
public  class  CameraFollow : MonoBehaviour {
public  GameObject mPlayer;  //获得主角
private  Vector3 m_Dir;  //获得摄像机相对于主角的方向
private  float  m_Angle;
// Use this for initialization
void  Start () {
m_Dir =  this .transform.position - mPlayer.transform.position;
m_Angle =  this .transform.eulerAngles.x;
}
// Update is called once per frame
void  Update () {
this .transform.position = mPlayer.transform.rotation * m_Dir + mPlayer.transform.position; //摄像机的新位置 = 主角位置 + 主角的旋转值 * 摄像机相对于主角的方向
this .transform.eulerAngles = mPlayer.transform.eulerAngles;  //摄像机旋转值=主角旋转值
this .transform.eulerAngles =  new  Vector3 (m_Angle,  this .transform.eulerAngles.y,  this .transform.eulerAngles.z);  //让摄像机旋转值的x轴强制位
}
}

  这样我们就弄出来了最简单的摄像机跟随,但是我们又发现,这种实现效果太简单,和把摄像机拖到主角身上成为子物体没啥两样,于是我们对逻辑进行了优化,让他有个渐变过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using  UnityEngine;
using  System.Collections;
public  class  CameraFollow : MonoBehaviour {
public  GameObject mPlayer;  //获得主角
private  Vector3 m_Dir;  //获得摄像机相对于主角的方向
private  float  m_Angle;
// Use this for initialization
void  Start () {
m_Dir =  this .transform.position - mPlayer.transform.position;
m_Angle =  this .transform.eulerAngles.x;
int  a = 6;
int  i = 7;
Debug.Log (a + i++ + i);
}
// Update is called once per frame
void  Update () {
Vector3 pos = mPlayer.transform.rotation * m_Dir + mPlayer.transform.position; //摄像机的新位置 = 主角位置 + 主角的旋转值 * 摄像机相对于主角的方向
this .transform.position = Vector3.Lerp( this .transform.position,pos,Time.deltaTime * 2);  //对位置线性插值
Vector3 angle = mPlayer.transform.eulerAngles;  //摄像机旋转值=主角旋转值
angle.x = m_Angle;
this .transform.eulerAngles = Vector3.Lerp ( this .transform.eulerAngles, angle, Time.deltaTime * 2);  //对旋转值线性插值
}
} 

  我们对计算结果进行线性插值,会发现效果好很多了

  但是这时候又出来个问题!

  当旋转值接近360°的时候,他会转1圈回来,造成整个摄像机不受控制。

  这是为啥呢?

  原来上面的欧拉角在359 + x 的时候,如果超过360,他会默认变回从0,开始,那么我们的摄像机就会做一个359 -》0的插值,会转倒着一圈回来

  这就是欧拉角在处理旋转的时候,最容易碰到的头疼的问题,如果我们用四元数,就能完美避开这种情况,因为四元数的世界里面,每个值是匹配一个角度的。

  不像欧拉角,一个旋转值有无穷种表示方式。

  于是我们把上面的代码改成四元数计算旋转值得,就能弄出来摄像机跟随的最终版本!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using  UnityEngine;
using  System.Collections;
public  class  CameraFollow : MonoBehaviour {
public  GameObject mPlayer;  //获得主角
private  Vector3 m_Dir;  //获得摄像机相对于主角的方向
private  float  m_Angle;
// Use this for initialization
void  Start () {
m_Dir =  this .transform.position - mPlayer.transform.position;
m_Angle =  this .transform.eulerAngles.x;
int  a = 6;
int  i = 7;
Debug.Log (a + i++ + i);
}
// Update is called once per frame
void  Update () {
Vector3 pos = mPlayer.transform.rotation * m_Dir + mPlayer.transform.position; //摄像机的新位置 = 主角位置 + 主角的旋转值 * 摄像机相对于主角的方向
this .transform.position = Vector3.Lerp( this .transform.position,pos,Time.deltaTime * 2);  //对位置线性插值
Quaternion qua = mPlayer.transform.rotation;  //摄像机旋转值=主角旋转值
this .transform.rotation = Quaternion.Lerp ( this .transform.rotation, qua , Time.deltaTime * 2); //对旋转值线性插值
this .transform.eulerAngles =  new  Vector3 (m_Angle,  this .transform.eulerAngles.y,  this .transform.eulerAngles.z);  //强制固定x轴;
}
}

Unity3d培训中Rotation和EularAngles的正确使用方法相关推荐

  1. TensorFlow中EMA的概念和正确使用方法

    目录 EMA介绍 概念 弥补不足:初始数据积累不足的情况 深度学习训练中的作用 实现 典型步骤 一个EMA影子变量的例子 进一步接近真实情景,让w1变动 例2:global_step的trainabl ...

  2. Openwrt中动态IPV6 防火墙的正确设置方法

    环境:光猫桥接+公网IPV6 问题:动态IPV6地址不知道怎么设置防火墙 解决办法:模糊匹配前缀,特定后缀 背景:将家中光猫桥接后,获得了公网的IPV6地址,可以从外部用IPV6访问家中的设备,但IP ...

  3. 在python中print 应用_Python print正确使用方法浅析

    Python编程语言是一款比较新颖的编程语言,相对于其他语言来说,有很多不同的特点引起了大多数开发人员的兴趣.在这里我们可以先从Python print的相关应用方法来分析,初步了解这一语言的应用方式 ...

  4. mysql 中default什么意思_数据库中default的用法正确使用方法

    数据库中default的用法的正确使用方法你清楚吗,今天学习啦小编就跟大家详细介绍下数据库中default的用法,希望能帮到大家. 数据库中default的用法的用法 SQL Server数据库def ...

  5. UI设计培训中的扁平化理念

    本文是为正在学习UI设计的同学们整理的一份资料,主要讲的是UI设计培训中的扁平化理念,扁平化的设计是抛弃一切装饰的设计,扁平化设计使得用户操作起来更加简洁.高效和舒适.简洁大方的交互界面设计自然能够引 ...

  6. unity android服务器端,【深圳Unity3D培训】 Android客户端与PC服务器实现Socket通信

    [深圳Unity3D培训] Android客户端与PC服务器实现Socket通信 Android终端连续扫描AP信息并发送给服务器端的完成.起首基于TCP协定在Android终端和PC两头之间构成收集 ...

  7. 【蓝鸥给您支招】北京Unity3D培训学费贵不贵

     北京Unity3D培训学费贵不贵 北京Unity3D培训学费贵不贵?面对游戏.VR.AR行业等行业展现的巨大商机,而通过Unity3D开发引擎的工具的学习,结合其他语言基础做出来的效果,也就是说,学 ...

  8. Swift中编写单例的正确方式

    本文由CocoaChina译者leon(社区ID)翻译自krakendev 原文:THE RIGHT WAY TO WRITE A SINGLETON 转载请保持所有内容和链接的完整性. 在之前的帖子 ...

  9. web前端培训:CSS中单行文本溢出显示省略号的方法

    CSS中单行文本溢出显示省略号的方法你知道吗?在web前端技术学习中,这个问题其实是属于老生常谈了,因为css单行文本的应用是非常频繁的,比如网站最基本的文章列表,标题会很长,而显示列表的区域宽度却没 ...

最新文章

  1. super(Student,self).__init__()初始化的是什么东西?
  2. 使用python处理子域名爆破工具subdomainsbrute结果txt
  3. linux:安装mysql
  4. linux tomcat 突然验证码出不来
  5. C语言SHELL排序算法
  6. helm命令的基本使用
  7. Java基础提升篇:理解String 及 String.intern() 在实际中的应用
  8. unity2019,打包APK时的gradle错误问题
  9. python比较两个字符串相似度_详解Python 字符串相似性的几种度量方法
  10. Failed to access IIS metabase
  11. 高并发大流量专题---1、高并发大流量解决方案总结
  12. 孪生网络 应用_数字孪生在航空发动机制造工艺中的应用探索
  13. Adobe Acrobat如何快速将PDF文档的书签修改为“承前缩放”——PDF增效插件AutoBookmark
  14. 笔试c语言编程题技巧,c语言笔试题答题技巧
  15. 智能陈桥输入法软件测试,智能陈桥五笔输入法
  16. 一个0中间一斜杠_斜杠青年喜欢的“网红台灯”:会唱歌,能无线充电,抖音获赞16W+...
  17. 【小工具】极客时间GitChat专栏下载脚本
  18. Android Studio中的mavenCentral、jcenter、google仓库
  19. 如何解决 【eclipse】中注释时乱码的问题
  20. R语言使用BOOT重抽样获取cox回归方程C-index(C指数)可信区间(2)

热门文章

  1. char 与 unsign char的转换
  2. java堆空间(内存)
  3. 施密特正交化(Schmidt)
  4. Ubuntu-Unattended upgrade in progress during shutdown
  5. ctP2ISP:使用卷积和数据增强的转换器预测蛋白质-蛋白质相互作用位点
  6. 基于FPGA的密码锁开发——(3)密码设置模块驱动
  7. Windows使用命令关闭一键关闭所有程序
  8. Python实现简单分类器
  9. Ubuntu更新-换源问题
  10. iPhone彻底删除的照片能恢复吗,2个找回永久删除照片的方法