在 Unity 中为物体旋转提供了各种 API ,例如 Rotate、RotateAround、LookAt 等方法。但为了避免万向节死锁的问题,一般使用四元数来表示物体的旋转。

而接下来的旋转方法我将不再区分Vector和Transform实现的区别,重点来看方法本身。

目录

一、基础旋转

①Rotate()

②RotateAround()

③LookAt()

二、四元数旋转

①Euler()

②Lerp()

③LookRotation()

三、应用案例

①第一人称相机旋转

②第三人称相机环绕

③第三人称平滑旋转缩放与跟随


一、基础旋转

图0-1 物体设置

①Rotate()

public void Rotate (Vector3 eulers, Space relativeTo= Space.Self);

应用一个围绕 Z 轴旋转 eulerAngles.z 度、围绕 X 轴旋转 eulerAngles.x 度、围绕 Y 轴旋转 eulerAngles.y 度(按此顺序)的旋转。

旋转采用欧拉角形式的 Vector3 参数。第二个参数是旋转轴,可以将其设置为本地轴 (Space.Self) 或全局轴 (Space.World)。旋转以欧拉角度数表示。

参数从左到右分别是,Vector类型的旋转角度和物体轴类设置。

知晓参数和使用方法后,我们将场景中的两个物体提前设置好,为了看出轴不同的设置带来的影响,把两个物体旋转一定的角度如图0-1。具体实现效果如图1-1~2,使用如下代码:

    [Header("旋转参数")]public float rotationSpeed;public GameObject aroundPoint; //围绕中心点[Header("旋转对象")]public GameObject rotateObj1;public GameObject rotateObj2;void Update(){//SelfRotate1();SelfRotate2();}void SelfRotate1(){//rotateObj1.transform.Rotate(xAngle, yAngle, zAngle, Space.World); //直接赋值Transform变换//rotateObj2.transform.Rotate(xAngle, yAngle, zAngle, Space.Self);rotateObj1.transform.Rotate(Vector3.forward * rotationSpeed * Time.deltaTime, Space.World); //通过V3类特定某个轴rotateObj2.transform.Rotate(Vector3.forward * rotationSpeed * Time.deltaTime, Space.Self);}

图1-1 Space.World

Rotate方法中的Space.World坐标轴设置,世界坐标的设置让物体是围绕着世界坐标轴的Z轴旋转。

图1-2 Space.Self

Rotate方法中的Space.Self坐标轴设置,与前一种不同,可以看出来很明显的围绕自己的Z坐标轴旋转。


②RotateAround()

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

将变换围绕穿过世界坐标中的 point 的 axis 旋转 angle 度。

这会修改变换的位置和旋转。

参数从左到右分别为应围绕的点,围绕点的哪个轴,旋转多少度。

使用设置好的两个物体和一个围绕中心点看一下效果,见图2-1,及以下代码段:

public GameObject aroundPoint; //围绕中心点void SelfRotate2(){rotateObj1.transform.RotateAround(aroundPoint.transform.position, Vector3.up, rotationSpeed * Time.deltaTime);rotateObj2.transform.RotateAround(aroundPoint.transform.position, Vector3.up, rotationSpeed * Time.deltaTime);}

图2-1 RotateAround()

可以看到,两个物体都围绕着设置好的中心点旋转类似星球环绕的效果。


③LookAt()

public void LookAt (Transform target);

public void LookAt (Transform target, Vector3 worldUp= Vector3.up);

旋转变换,使向前矢量指向 target 的当前位置。

然后,它旋转变换以将其向上方向矢量指向 worldUp 矢量暗示的方向。 如果省略 worldUp 参数,该函数将使用世界空间的 Y 轴。 worldUp 只是一个提示矢量。如果向前方向垂直于 worldUp,则旋转的向上矢量将仅匹配 worldUp 矢量。

参数从左到右分别为看向目标,看向轴向。

我们让物体1以Z轴始终看向物体2,物体2保持环绕旋转,效果见图3-1,及以下代码段。

 void SelfRotate3(){rotateObj1.transform.LookAt(rotateObj2.transform);//rotateObj1.transform.LookAt(rotateObj2.transform, rotateObj2.transform.up);rotateObj2.transform.RotateAround(aroundPoint.transform.position, Vector3.up, rotationSpeed * Time.deltaTime);}

图3-1 LookAt()

其中注意,无论如何都是物体的Z轴始终指向物体,而具体指向后物体想以怎么样的状态保持指向还需要看第二个参数,可以看到上图3-1中看向后是默认X轴向上的,但如果这里的worldUp设置为rotateObj2.transform.up,则意味着以物体Y轴作为世界Y轴朝向,其他轴向类似见下图3-2。

图3-2 worldUp设置

二、四元数旋转

①Euler()

public static Quaternion Euler (float x, float y, float z);

返回一个围绕 Z 轴旋转 z 度、围绕 X 轴旋转 x 度、围绕 Y 轴旋转 y 度的旋转。

我们将前次课的行为树用上,定义一个物体来回运动。效果见图1-1,及以下代码段。

    /// <summary>/// Euler()/// </summary>void QuaternionRotate1() {eulerRotate.y += rotationSpeed * Time.deltaTime;rotateObj1.transform.rotation = Quaternion.Euler(eulerRotate);rotateObj2.transform.rotation = Quaternion.Euler(eulerRotate);}

图1-1 Euler()


②Lerp()

public static Quaternion Lerp (Quaternion a, Quaternion b, float t); 

在 a 和 b 之间插入 t,然后对结果进行标准化处理。参数 t 被限制在 [0, 1] 范围内。

该方法比 Slerp 快,但如果旋转相距很远,其视觉效果也更糟糕。

参数从左到右分别为指定Z轴要对齐的物体,世界upwards方向的V3值。

效果见图2-1,及以下代码段。

    /// <summary>/// Lerp()/// </summary>void QuaternionRotate2(){rotateObj1.transform.rotation = Quaternion.Lerp(from.rotation, to.rotation, rotationSpeed * Time.time);rotateObj2.transform.rotation = Quaternion.Lerp(from.rotation, to.rotation, rotationSpeed * Time.time);}

图2-1 Lerp()


③LookRotation()

public static Quaternion LookRotation (Vector3 forward, Vector3 upwards= Vector3.up);

使用指定的 forward 和 upwards 方向创建旋转。

Z 轴将与forward对齐,X 轴与 forward 和 upwards 之间的差积对齐,Y 轴与 Z 和 X 之间的差积对齐。如果 forward 或 upwards 量值为零,则返回恒等。 如果 forward 和 upwards 共线,则返回恒等。

参数从左到右分别为指定Z轴要对齐的物体,世界upwards方向的V3值。

定义两个物体来回运动,将应对齐的Z轴改为两物体间的位置Z值,upwards就设置默认的Y轴up。效果见图3-1,及以下代码段。

    /// <summary>/// LookRotation()/// </summary>void QuaternionRotate3(){Vector3 relativePos = from.position - to.position; //相对位置rotateObj1.transform.rotation = Quaternion.LookRotation(relativePos, Vector3.up);rotateObj2.transform.rotation = Quaternion.LookRotation(relativePos, Vector3.up);}

图3-1  LookRotation()

三、应用案例

①第一人称相机旋转

   [Header("第一人称")]public Camera firstCamera;public float mouseSensitivityX = 5.0f;public float mouseSensitivityY = 5.0f;public float minCameraY = -30.0f;public float maxCameraY = 30.0f;private float currentRotationY;private float currentRotationX;/// <summary>/// 第一人称角色视角/// </summary>private void FirstPlayerCameraRotate() {currentRotationX += Input.GetAxis("Mouse X") * mouseSensitivityX;currentRotationY += Input.GetAxis("Mouse Y") * mouseSensitivityY;currentRotationY = AngleLimit(currentRotationY, minCameraY, maxCameraY);firstCamera.transform.eulerAngles = new Vector3(-currentRotationY, currentRotationX, 0);}/// <summary>/// 视角角度限制/// </summary>/// <param name="rotationLimited">应限制的旋转轴</param>/// <param name="min">最小值</param>/// <param name="max">最大值</param>/// <returns>可到达的角度范围</returns>private float AngleLimit(float rotationLimited, float min, float max) {if (currentRotationY < min) {return min;}if (currentRotationY > max) {return max;}return rotationLimited;}

代码段中可以看到分成了两个函数,第一人称视角主要控制函数FirstPlayerCameraRotate()及AngleLimit()视角角度限制函数。

FirstPlayerCameraRotate()控制函数就比较简单,通过对"Mouse X"和"Mouse Y"轴向的获取,乘以提前设置好的鼠标灵敏度赋值给最新旋转XY。如果好奇这里为什么是对-currentRotationY当作V3的x轴值,在Unity中去试一下Transfom Rotation的值看下效果就明白了。

AngleLimit()视角角度限制函数则是想实现对视角上下有限制,将当前最新旋转值currentRotationY与最大值和最小值比较,若大于最大值则不能继续旋转,小于最小值同样。具体实现效果如下图1

图1 第一人称视角

②第三人称相机环绕

    [Header("第三人称")]public Camera thirdCamera;public GameObject followObj;public float rotateSpeedX = 10.0f;public float rotateSpeedY = 10.0f;private Quaternion curRotation;private float currentMouseX = 0.0f;private float currentMouseY = 0.0f;void Start(){currentRotationX = thirdCamera.transform.eulerAngles.x;currentRotationY = thirdCamera.transform.eulerAngles.y;}    /// <summary>/// 第三人称角色视角/// </summary>private void ThirdPlayerCameraRotate(){try {if (followObj != null){currentMouseX += Input.GetAxis("Mouse X") * rotateSpeedX;currentMouseY += Input.GetAxis("Mouse Y") * rotateSpeedY;curRotation = Quaternion.Euler(currentMouseY, currentMouseX, 0); //将V3转化为QuaternionthirdCamera.transform.RotateAround(followObj.transform.position, Vector3.up, currentMouseX);}}catch {Debug.Log("出了点问题,可能是追随目标没设置");}}

代码段中可以看到只有一个函数ThirdPlayerCameraRotate()第三人称控制函数。

首先函数需要对应的跟随物体followObj,其次同样是对"Mouse X"和"Mouse Y"轴向的获取,利用上述Euler函数将V3数值转化为四元素Quaternion数值,后就可以使用RotateAround这一类Transform四元素挂钩的函数了。具体效果见下图2

图2 第三人称环绕旋转

③第三人称平滑旋转缩放与跟随

    [Header("相机")]public Camera thirdCamera;public GameObject target;[Header("相机缩放")]public float zoomSpeed = 2.0f;public float maxDistance = 15.0f;public float minDistance = 2.0f;private float cameraDistance;private Vector3 cameraPosition;[Header("相机旋转")]public float rotateSpeedX;public float rotateSpeedY;public float maxRotationY = 60.0f;public float minRotationY = -10.0f;private float rotateY;private float rotateX;private Quaternion cameraRotation;[Header("平滑设置")]public bool isDamping = false;public float damping = 5.0f;void Update(){ThirdCameraControll();}/// <summary>/// 第三相机控制/// </summary>void ThirdCameraControll() {//旋转rotateX += Input.GetAxis("Mouse X") * rotateSpeedX * Time.deltaTime;rotateY += Input.GetAxis("Mouse Y") * rotateSpeedY * Time.deltaTime;rotateY = AngleLimit(rotateY, minRotationY, maxRotationY);cameraRotation = Quaternion.Euler(rotateY, rotateX, 0);//缩放cameraDistance -= Input.GetAxis("Mouse ScrollWheel") * zoomSpeed;cameraDistance = Mathf.Clamp(cameraDistance, minDistance, maxDistance);//位置控制cameraPosition = cameraRotation * new Vector3(0.0f, 0.0f, -cameraDistance) + target.transform.position;//平滑处理if (isDamping){thirdCamera.transform.position = Vector3.Lerp(thirdCamera.transform.position ,cameraPosition, damping * Time.deltaTime);thirdCamera.transform.rotation = Quaternion.Lerp(thirdCamera.transform.rotation, cameraRotation, damping * Time.deltaTime);}else {thirdCamera.transform.position = cameraPosition;thirdCamera.transform.rotation = cameraRotation;}}/// <summary>/// 角度限制/// </summary>/// <param name="LimitedValue"></param>/// <param name="min"></param>/// <param name="max"></param>/// <returns></returns>private float AngleLimit(float LimitedValue, float min, float max) {if (LimitedValue < -360) return LimitedValue += 360;if (LimitedValue > 360) return LimitedValue -= 360;return Mathf.Clamp(LimitedValue, min, max);}

最终效果入下图3

图3 线性插值和欧拉的处理

至于万向节死锁问题,我这不去讲述了感兴趣的小伙伴们可以自行学习。

喜欢的话还请关注B站账号:波仔羔。好了如果觉得对你有帮助的话不如给个点赞加关注,如果有问题也欢迎评论或私聊我解决哦。

【Unity步步升】各类旋转逻辑的区别,如欧拉旋转、插值旋转、矢量朝向等...及游戏视角案例相关推荐

  1. Blender图解教程:使用曲线编辑器(Graph Editor)的“不连续项(欧拉)过滤器”解决欧拉模式下旋转异常问题

    情景再现 做升龙拳的时候在第25帧和第30帧之间遇到的这个问题(请注意角色脚部) 第25帧,正常的腾空勾拳pose 第30帧 也是一个正常的下落Pose 两个关键帧之间由Blender自动生成补间,然 ...

  2. 【Unity编程】Unity中的欧拉旋转

    版权声明:本文为博主原创文章,欢迎转载.请保留博主链接:http://blog.csdn.net/andrewfan 欧拉角的定义 在写这篇博客之前,我搜索了网上很多关于欧拉角的定义,发现大部分引用自 ...

  3. oracle逻辑备份和物理备份,oracle数据库物理备份和逻辑备份区别

    oracle数据库物理备份和逻辑备份区别 ORACLE中数据备份分为物理备份和逻辑备份两种.物理备份就是转储ORACLE物理文件(如数据文件.控制文件.归档日志文件等),一旦数据库发生故障,可以利用这 ...

  4. 物理服务器备份系统,物理备份和逻辑备份区别

    物理备份和逻辑备份区别 内容精选 换一换 可能这份面试题还不足以包含所有Java问题,但有了它,我相信你一定不会"败"的很惨,有了它,足以应对目前市面上绝大部分的Java面试了,因 ...

  5. Unity骰子插值旋转的投掷功能,获得正面点数(可按钮控制上下左右插值翻转,无万向锁问题)

    标题Unity骰子插值旋转的滚动投掷功能,和点数的获得 功能效果展示 可按钮控制上下左右插值翻转,无万向锁问题. 有需要demo的联系我,QQ:763992638

  6. Unity Window触摸屏电脑和移动端Input触控,控制相机旋转缩放

    Unity Window触摸屏电脑和移动端Input触控,控制相机旋转缩放 原理就是获取手指滑动的偏移量,来计算 代码帖出来 using UnityEngine; using System; usin ...

  7. Unity 镜头拉近拉远 和旋转视角

    自己使用的是 Unity2018和VS2019版. 向主相机添加FollowPlayer类. 下面呈现代码 这里有小Bug, 拉近拉远和旋转视角无法同时使用,后续会进行更改完善 public clas ...

  8. qt 模拟鼠标滑轮_【游戏流体力学基础及Unity代码(四)】用欧拉方程模拟无粘性染料之公式推导...

    先放一张动态图吊一下胃口~下面就是最终的效果 不可压缩的欧拉方程只比NS方程少一个粘性项.所以下面的内容是完全适合NS方程的.各位请准备好! 散度定理 模拟流体的时候会遇到许多数学公式.为了深刻理解这 ...

  9. 从数学角度理解欧拉旋转中的万向节死锁

    欧拉旋转 姿态角pitch/roll/yaw ​ 姿态角是飞行器的机体坐标系与地面坐标系的夹角,也叫欧拉角.其中, pitch是俯仰角,yaw是偏航角,roll是滚转角. ​ 欧拉角最直观.最容易理解 ...

最新文章

  1. php 变成 25,2020-09-25 PHP变量介绍
  2. vivado----fpga硬件调试 (一)----mark_debug
  3. observable_在Spring MVC流中使用rx-java Observable
  4. C语言中“数组名”和“数组名”
  5. 【答案放在最后,看题看不到答案】2017年下半年软件设计师 上午选择题
  6. 2021牛客暑期多校训练营2 L-WeChat Walk(分块)
  7. 微软著名程序员、歌手、NBA球队老板保罗·艾伦逝世,盖茨、库克等大佬发文悼念...
  8. Ubuntu ibus 输入法之Skype不能输入中文
  9. matlab查找指定文件夹下文件(附汉字和标点符号读取方法)
  10. PHP运算符 - 对象的方法或者属性, =数组的元素值
  11. 运维之Linux秋招重点(根据面经和常见笔试题总结,持续更新)
  12. 笔记之STM32F072CBT6芯片的串口高级功能之反相配置问题(HAL库)
  13. 百度网盘破解版Pandownload开发者被抓
  14. 与速度对偶的角速度系公式
  15. 《演讲的力量》TED主席和首席教练教你演讲的基本技巧
  16. html 嵌入页面,html5页面嵌入
  17. PDF文件怎么旋转页面
  18. 为何日本手机走不出国门?
  19. 场景化、细分化来袭:从2022深圳时尚家居设计周看家居行业的重塑之路
  20. varchar2和varchar的区别

热门文章

  1. 钉钉小程序复选框 全选反选 表单获取内容有误处理
  2. sql字段转换字符串——CONVERT (VARCHAR(50),字段)
  3. 原码、反码、补码和真值
  4. PMEdit一个富文本框可以编辑文本、图片并可以显示GIF动画
  5. canvas实现点线动画效果
  6. 无法启动此程序,因为计算机中丢失pthreadVC2.dll
  7. kettle 9.x 版本连接资源库,资源库灰色
  8. 淘宝线上线下“出淘”欲打造零售业航母
  9. python京东抢购软件_福利来了,python 京东抢购茅台脚本(亲测可用)
  10. 软件测试自动化分类,自动化测试的主要分类