Unity3d培训中Rotation和EularAngles的正确使用方法
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的正确使用方法相关推荐
- TensorFlow中EMA的概念和正确使用方法
目录 EMA介绍 概念 弥补不足:初始数据积累不足的情况 深度学习训练中的作用 实现 典型步骤 一个EMA影子变量的例子 进一步接近真实情景,让w1变动 例2:global_step的trainabl ...
- Openwrt中动态IPV6 防火墙的正确设置方法
环境:光猫桥接+公网IPV6 问题:动态IPV6地址不知道怎么设置防火墙 解决办法:模糊匹配前缀,特定后缀 背景:将家中光猫桥接后,获得了公网的IPV6地址,可以从外部用IPV6访问家中的设备,但IP ...
- 在python中print 应用_Python print正确使用方法浅析
Python编程语言是一款比较新颖的编程语言,相对于其他语言来说,有很多不同的特点引起了大多数开发人员的兴趣.在这里我们可以先从Python print的相关应用方法来分析,初步了解这一语言的应用方式 ...
- mysql 中default什么意思_数据库中default的用法正确使用方法
数据库中default的用法的正确使用方法你清楚吗,今天学习啦小编就跟大家详细介绍下数据库中default的用法,希望能帮到大家. 数据库中default的用法的用法 SQL Server数据库def ...
- UI设计培训中的扁平化理念
本文是为正在学习UI设计的同学们整理的一份资料,主要讲的是UI设计培训中的扁平化理念,扁平化的设计是抛弃一切装饰的设计,扁平化设计使得用户操作起来更加简洁.高效和舒适.简洁大方的交互界面设计自然能够引 ...
- unity android服务器端,【深圳Unity3D培训】 Android客户端与PC服务器实现Socket通信
[深圳Unity3D培训] Android客户端与PC服务器实现Socket通信 Android终端连续扫描AP信息并发送给服务器端的完成.起首基于TCP协定在Android终端和PC两头之间构成收集 ...
- 【蓝鸥给您支招】北京Unity3D培训学费贵不贵
北京Unity3D培训学费贵不贵 北京Unity3D培训学费贵不贵?面对游戏.VR.AR行业等行业展现的巨大商机,而通过Unity3D开发引擎的工具的学习,结合其他语言基础做出来的效果,也就是说,学 ...
- Swift中编写单例的正确方式
本文由CocoaChina译者leon(社区ID)翻译自krakendev 原文:THE RIGHT WAY TO WRITE A SINGLETON 转载请保持所有内容和链接的完整性. 在之前的帖子 ...
- web前端培训:CSS中单行文本溢出显示省略号的方法
CSS中单行文本溢出显示省略号的方法你知道吗?在web前端技术学习中,这个问题其实是属于老生常谈了,因为css单行文本的应用是非常频繁的,比如网站最基本的文章列表,标题会很长,而显示列表的区域宽度却没 ...
最新文章
- super(Student,self).__init__()初始化的是什么东西?
- 使用python处理子域名爆破工具subdomainsbrute结果txt
- linux:安装mysql
- linux tomcat 突然验证码出不来
- C语言SHELL排序算法
- helm命令的基本使用
- Java基础提升篇:理解String 及 String.intern() 在实际中的应用
- unity2019,打包APK时的gradle错误问题
- python比较两个字符串相似度_详解Python 字符串相似性的几种度量方法
- Failed to access IIS metabase
- 高并发大流量专题---1、高并发大流量解决方案总结
- 孪生网络 应用_数字孪生在航空发动机制造工艺中的应用探索
- Adobe Acrobat如何快速将PDF文档的书签修改为“承前缩放”——PDF增效插件AutoBookmark
- 笔试c语言编程题技巧,c语言笔试题答题技巧
- 智能陈桥输入法软件测试,智能陈桥五笔输入法
- 一个0中间一斜杠_斜杠青年喜欢的“网红台灯”:会唱歌,能无线充电,抖音获赞16W+...
- 【小工具】极客时间GitChat专栏下载脚本
- Android Studio中的mavenCentral、jcenter、google仓库
- 如何解决 【eclipse】中注释时乱码的问题
- R语言使用BOOT重抽样获取cox回归方程C-index(C指数)可信区间(2)