可旋转缩放,检测碰撞时位移以尽量保持对象不被遮挡,话不多说,直接上代码
using UnityEngine; using UnityStandardAssets.CrossPlatformInput; [AddComponentMenu("Zetan/Third Person Camera Follow")] public class CameraFollow : MonoBehaviour { public Transform target; public Vector3 headOffset = Vector3.zero; public bool smooth; public float smoothLevel = 0.5f; Vector3 direction; public float rotateSpeed = 10.0f; public float zoomSpeed = 5f; public float minDistance = 3.0f; public float maxDistance = 15.0f; public float maxLookUpAngle = 80.0f; public float maxLookDownAngle = 30.0f; [SerializeField] float OriginDistance; [SerializeField] float currentDistance; Vector3 visibleEuler; // Use this for initialization void Start() { ResetView(); } // Update is called once per frame void Update() { visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles); } private void LateUpdate() { float h = CrossPlatformInputManager.GetAxis("Mouse X"); float v = CrossPlatformInputManager.GetAxis("Mouse Y"); float zoom = CrossPlatformInputManager.GetAxis("Mouse ScrollWheel"); Translate(zoom); Rotate(h, v); } void Translate(float zoom) { OriginDistance -= zoom * zoomSpeed; OriginDistance = Mathf.Clamp(OriginDistance, minDistance, maxDistance); RaycastHit hit; if (Physics.Raycast(target.position + headOffset, transform.position - target.position - headOffset, out hit, OriginDistance, ~(1 << LayerMask.NameToLayer("Water") | 1 << LayerMask.NameToLayer("Player")), QueryTriggerInteraction.Ignore))//检测层依据需求更改 { currentDistance = hit.distance; } else { currentDistance = OriginDistance; } Vector3 disOffset = direction.normalized * currentDistance; if(smooth)transform.position = Vector3.Lerp(transform.position, target.position + headOffset + disOffset, smoothLevel);//平滑移动,但累计差会导致相机逐渐回到某个特定视角,且伴随有轻微抖动 else transform.position = target.position + headOffset + disOffset;//旋转时伴随轻微卡顿 transform.LookAt(target); } void Rotate(float h, float v) { float finallyRotateV = v * rotateSpeed; if (finallyRotateV + visibleEuler.x >= maxLookUpAngle) { finallyRotateV = visibleEuler.x + finallyRotateV - maxLookUpAngle; } else if (finallyRotateV + visibleEuler.x <= -maxLookDownAngle) { finallyRotateV = visibleEuler.x + finallyRotateV + maxLookDownAngle; } //左右旋转 transform.RotateAround(target.position + headOffset, transform.up, h * rotateSpeed); //上下旋转 transform.RotateAround(target.position + headOffset, transform.right, -finallyRotateV); transform.LookAt(target.position + headOffset); direction = (transform.position - target.position - headOffset).normalized; } public float ConvertAngle(float value) { float angle = value - 180; if (angle > 0) return angle - 180; return angle + 180; } public Vector3 ConvertEulerAngle(Vector3 euler) { return new Vector3(ConvertAngle(euler.x), ConvertAngle(euler.y), ConvertAngle(euler.z)); } public void ResetView() { if(minDistance > maxDistance) { maxDistance += minDistance; minDistance = maxDistance - minDistance; maxDistance -= minDistance; } direction = -target.forward; Vector3 disOffset = direction * (minDistance + maxDistance) / 2; transform.position = target.position + headOffset + disOffset; OriginDistance = disOffset.magnitude; } private void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawLine(target.position + headOffset, transform.position); Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(target.position + headOffset, minDistance); Gizmos.DrawLine(target.position + headOffset + target.forward * minDistance, target.position + headOffset + target.forward * maxDistance); Gizmos.DrawLine(target.position + headOffset - target.forward * minDistance, target.position + headOffset - target.forward * maxDistance); Gizmos.DrawLine(target.position + headOffset + target.up * minDistance, target.position + headOffset + target.up * maxDistance); Gizmos.DrawLine(target.position + headOffset - target.up * minDistance, target.position + headOffset - target.up * maxDistance); Gizmos.DrawLine(target.position + headOffset + target.right * minDistance, target.position + headOffset + target.right * maxDistance); Gizmos.DrawLine(target.position + headOffset - target.right * minDistance, target.position + headOffset - target.right * maxDistance); Gizmos.DrawWireSphere(target.position + headOffset, maxDistance); } }