当前位置:   article > 正文

Unity 一种更为简洁明了的环绕相机方案_unity orbitcamera

unity orbitcamera

配置方法

创建一个空对象挂载相机控制脚本(在代码中让该对象与角色的位置重合),相机作为其子物体

将相机相对于父物体的距离设定为理想的 相机-人物 距离

指定环绕中心和相机

原理

想要实现环绕,首先就需要获取环绕点和相机的Transform属性。

想要表示相机相对于环绕点的方位,你可能会觉得使用相对位置描述更为方便,其实不然,实际上使用两个欧拉角来描述比使用位置描述更为方便,因为我们后面需要对相机的旋转过程插值,使用位置来描述显然是不便于插值的。

这样,我们就有了相机相对于环绕点的角度,加上距离就可以精确描述相机的位置了。

线性插值

如果要对一个变量进行插值,首先要明确插值的实现。什么是线性插值?

给定两个数,a、b,对a、b进行一次线性插值,再给一个参数t,代表插值的位置。

比如对0、1插值,参数为0.1,那么进行一次插值得到的结果就是0.1。

线性插值的公式是 a + (b-a) * t 。

那么插值有哪些实际应用呢?

- 平滑运动中的相机与角色的距离

- 平滑旋转中的相机与角色的角度,并实现一定时长的惯性旋转效果。

为什么插值会产生延时呢?

因为插值有两个数,一个是当前值,一个是目标值,另外还有一个参数是百分比。

当目标值不再变化之后,当前值逼近目标值需要一定的时间。

两个数之间的差距随着时间的变化曲线,类似于一个方向向下的抛物线。

周围障碍检测

对角色前后左右四个方向进行射线检测,获取返回的距离,取得一个最小值。

如果想要更精确的结果,可以将正交的方向向量分别两两相加,得到四个斜的方向向量,这样就可以做八个方向的距离检测。

相机被遮挡处理

如果相机被遮挡,就将相机距离调整到小于 距离角色水平方向最近的障碍距离 。

在手动调整距离时,记录偏好距离。

如果不再被遮挡,就恢复偏好距离。

代码

  1. using UnityEngine;
  2. using System.Collections;
  3. using UnityEngine.Animations;
  4. public class OrbitCamera : MonoBehaviour
  5. {
  6. public Transform pivot;
  7. public Transform camera;
  8. private float distance;
  9. public bool distanceAdjustable = true;
  10. public bool rotationAdjustable = true;
  11. void Start()
  12. {
  13. targetSideRotation = transform.eulerAngles.y;
  14. currentSideRotation = transform.eulerAngles.y;
  15. targetUpRotation = transform.eulerAngles.x;
  16. currentUpRotation = transform.eulerAngles.x;
  17. distance = -camera.localPosition.z; //相机局部坐标z值为-1.8,那么相机与距离人物为1.8
  18. }
  19. void LateUpdate()
  20. {
  21. if (!pivot) return;
  22. Follow();
  23. DragRotate();
  24. ScrollScale();
  25. OcclusionJudge();
  26. }
  27. void Follow()
  28. {
  29. if (!pivot.gameObject.GetComponentInParent<ParentConstraint>())
  30. {
  31. transform.position = Vector3.Lerp(transform.position, pivot.position, Time.deltaTime * 5); //相机跟随角色的插值,相机当前帧的实际位置为它们中间10%的位置
  32. camera.localPosition = -Vector3.forward * distance; //仅z有值,并且方向为负
  33. }
  34. else //高速运动下不再插值
  35. {
  36. transform.position = pivot.position;
  37. camera.localPosition = -Vector3.forward * distance;
  38. }
  39. }
  40. public float MinimumDegree = 0;
  41. public float MaximumDegree = 60;
  42. private float targetSideRotation;
  43. private float targetUpRotation;
  44. private float currentSideRotation;
  45. private float currentUpRotation;
  46. void DragRotate()
  47. {
  48. if (!rotationAdjustable) return;
  49. if (Input.GetMouseButton(0))
  50. {
  51. targetSideRotation += Input.GetAxis("Mouse X") * 5;
  52. targetUpRotation -= Input.GetAxis("Mouse Y") * 5;
  53. }
  54. targetUpRotation = Mathf.Clamp(targetUpRotation, MinimumDegree, MaximumDegree);
  55. currentSideRotation = Mathf.LerpAngle(currentSideRotation, targetSideRotation, Time.deltaTime * 5);
  56. currentUpRotation = Mathf.Lerp(currentUpRotation, targetUpRotation, Time.deltaTime * 5);
  57. transform.rotation = Quaternion.Euler(currentUpRotation, currentSideRotation, 0);
  58. }
  59. float MinimumDistance = 1;
  60. float MaximumDistance = 4;
  61. void ScrollScale()
  62. {
  63. if (!distanceAdjustable) return;
  64. distance *= (1 - Input.GetAxis("Mouse ScrollWheel") * 0.2f); //在原值的基础上调整为原值的百分比
  65. distance = Mathf.Clamp(distance, MinimumDistance, MaximumDistance);
  66. if(Input.GetAxis("Mouse ScrollWheel")!=0)
  67. preferdDistance = distance;
  68. }
  69. float preferdDistance = 1;
  70. bool resumable = false;
  71. void OcclusionJudge()
  72. {
  73. if (Physics.Raycast(pivot.position, -camera.forward, distance))
  74. {
  75. resumable = true;
  76. distance = NearestObstacleDistance(pivot);
  77. while (Physics.Raycast(pivot.position, -camera.forward, distance) && distance > MinimumDistance)
  78. {
  79. distance *= 0.99f;
  80. distance = Mathf.Clamp(distance, MinimumDistance, MaximumDistance);
  81. float dist = Mathf.Lerp(-camera.transform.localPosition.z, distance, 1f);
  82. camera.localPosition = -Vector3.forward * dist;
  83. }
  84. }
  85. if (!resumable) return;
  86. if (resumable && !Physics.Raycast(pivot.position, -camera.forward, preferdDistance))
  87. {
  88. distance = preferdDistance;
  89. float dist = Mathf.Lerp(-camera.transform.localPosition.z, distance, 1f);
  90. camera.localPosition = -Vector3.forward * distance;
  91. resumable = false;
  92. }
  93. }
  94. float NearestObstacleDistance(Transform start)
  95. {
  96. float dis = float.MaxValue;
  97. RaycastHit hit;
  98. Physics.Raycast(start.position, start.forward, out hit);
  99. if(hit.distance!=0) dis = Mathf.Min(dis, hit.distance);
  100. Physics.Raycast(start.position, -start.forward, out hit);
  101. if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
  102. Physics.Raycast(start.position, start.right, out hit);
  103. if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
  104. Physics.Raycast(start.position, -start.right, out hit);
  105. if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
  106. return dis;
  107. }
  108. }

效果就不演示了,比之前写的那个要好不少

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/127968
推荐阅读
相关标签
  

闽ICP备14008679号