赞
踩
配置方法
创建一个空对象挂载相机控制脚本(在代码中让该对象与角色的位置重合),相机作为其子物体
将相机相对于父物体的距离设定为理想的 相机-人物 距离
指定环绕中心和相机
原理
想要实现环绕,首先就需要获取环绕点和相机的Transform属性。
想要表示相机相对于环绕点的方位,你可能会觉得使用相对位置描述更为方便,其实不然,实际上使用两个欧拉角来描述比使用位置描述更为方便,因为我们后面需要对相机的旋转过程插值,使用位置来描述显然是不便于插值的。
这样,我们就有了相机相对于环绕点的角度,加上距离就可以精确描述相机的位置了。
如果要对一个变量进行插值,首先要明确插值的实现。什么是线性插值?
给定两个数,a、b,对a、b进行一次线性插值,再给一个参数t,代表插值的位置。
比如对0、1插值,参数为0.1,那么进行一次插值得到的结果就是0.1。
线性插值的公式是 a + (b-a) * t 。
那么插值有哪些实际应用呢?
- 平滑运动中的相机与角色的距离
- 平滑旋转中的相机与角色的角度,并实现一定时长的惯性旋转效果。
为什么插值会产生延时呢?
因为插值有两个数,一个是当前值,一个是目标值,另外还有一个参数是百分比。
当目标值不再变化之后,当前值逼近目标值需要一定的时间。
两个数之间的差距随着时间的变化曲线,类似于一个方向向下的抛物线。
周围障碍检测
对角色前后左右四个方向进行射线检测,获取返回的距离,取得一个最小值。
如果想要更精确的结果,可以将正交的方向向量分别两两相加,得到四个斜的方向向量,这样就可以做八个方向的距离检测。
相机被遮挡处理
如果相机被遮挡,就将相机距离调整到小于 距离角色水平方向最近的障碍距离 。
在手动调整距离时,记录偏好距离。
如果不再被遮挡,就恢复偏好距离。
代码
- using UnityEngine;
- using System.Collections;
- using UnityEngine.Animations;
-
- public class OrbitCamera : MonoBehaviour
- {
- public Transform pivot;
- public Transform camera;
- private float distance;
-
- public bool distanceAdjustable = true;
- public bool rotationAdjustable = true;
-
- void Start()
- {
- targetSideRotation = transform.eulerAngles.y;
- currentSideRotation = transform.eulerAngles.y;
- targetUpRotation = transform.eulerAngles.x;
- currentUpRotation = transform.eulerAngles.x;
- distance = -camera.localPosition.z; //相机局部坐标z值为-1.8,那么相机与距离人物为1.8
- }
-
- void LateUpdate()
- {
- if (!pivot) return;
-
- Follow();
- DragRotate();
- ScrollScale();
- OcclusionJudge();
- }
-
- void Follow()
- {
- if (!pivot.gameObject.GetComponentInParent<ParentConstraint>())
- {
- transform.position = Vector3.Lerp(transform.position, pivot.position, Time.deltaTime * 5); //相机跟随角色的插值,相机当前帧的实际位置为它们中间10%的位置
- camera.localPosition = -Vector3.forward * distance; //仅z有值,并且方向为负
- }
- else //高速运动下不再插值
- {
- transform.position = pivot.position;
- camera.localPosition = -Vector3.forward * distance;
- }
- }
-
- public float MinimumDegree = 0;
- public float MaximumDegree = 60;
- private float targetSideRotation;
- private float targetUpRotation;
- private float currentSideRotation;
- private float currentUpRotation;
- void DragRotate()
- {
- if (!rotationAdjustable) return;
-
- if (Input.GetMouseButton(0))
- {
- targetSideRotation += Input.GetAxis("Mouse X") * 5;
- targetUpRotation -= Input.GetAxis("Mouse Y") * 5;
- }
-
- targetUpRotation = Mathf.Clamp(targetUpRotation, MinimumDegree, MaximumDegree);
-
- currentSideRotation = Mathf.LerpAngle(currentSideRotation, targetSideRotation, Time.deltaTime * 5);
- currentUpRotation = Mathf.Lerp(currentUpRotation, targetUpRotation, Time.deltaTime * 5);
- transform.rotation = Quaternion.Euler(currentUpRotation, currentSideRotation, 0);
- }
-
- float MinimumDistance = 1;
- float MaximumDistance = 4;
- void ScrollScale()
- {
- if (!distanceAdjustable) return;
-
- distance *= (1 - Input.GetAxis("Mouse ScrollWheel") * 0.2f); //在原值的基础上调整为原值的百分比
- distance = Mathf.Clamp(distance, MinimumDistance, MaximumDistance);
- if(Input.GetAxis("Mouse ScrollWheel")!=0)
- preferdDistance = distance;
- }
-
- float preferdDistance = 1;
- bool resumable = false;
- void OcclusionJudge()
- {
- if (Physics.Raycast(pivot.position, -camera.forward, distance))
- {
- resumable = true;
-
- distance = NearestObstacleDistance(pivot);
-
- while (Physics.Raycast(pivot.position, -camera.forward, distance) && distance > MinimumDistance)
- {
- distance *= 0.99f;
- distance = Mathf.Clamp(distance, MinimumDistance, MaximumDistance);
- float dist = Mathf.Lerp(-camera.transform.localPosition.z, distance, 1f);
- camera.localPosition = -Vector3.forward * dist;
- }
- }
-
- if (!resumable) return;
-
- if (resumable && !Physics.Raycast(pivot.position, -camera.forward, preferdDistance))
- {
- distance = preferdDistance;
- float dist = Mathf.Lerp(-camera.transform.localPosition.z, distance, 1f);
- camera.localPosition = -Vector3.forward * distance;
- resumable = false;
- }
- }
-
- float NearestObstacleDistance(Transform start)
- {
- float dis = float.MaxValue;
- RaycastHit hit;
- Physics.Raycast(start.position, start.forward, out hit);
- if(hit.distance!=0) dis = Mathf.Min(dis, hit.distance);
- Physics.Raycast(start.position, -start.forward, out hit);
- if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
- Physics.Raycast(start.position, start.right, out hit);
- if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
- Physics.Raycast(start.position, -start.right, out hit);
- if (hit.distance != 0) dis = Mathf.Min(dis, hit.distance);
- return dis;
- }
- }
效果就不演示了,比之前写的那个要好不少
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。