Unity 相机控制代码 一例
自制镜头控制脚本一例
#define MY_CAMERA_DEBUG using UnityEngine; using System.Collections; public class MyCamera : MonoBehaviour { public Transform targetLookAt; public bool pause = false; public bool fixedModel = false; public bool fixedForTargetLookAt = true; public bool fixedForTargetRotationModel = true; public bool fixedPositionModel = false; public Vector3 fixedPosition = Vector3.zero; public float fixedDistance = 10f; public float fixedRotationY = 60f; public float fixedRotationX = 0f; public float distance = 5f; public float distanceMax = 12.0f; public float distanceMin = 3.0f; public float distanceSmooth = 0.05f; public float distanceResetSmooth = 0f; public float X_MouseSensitivity = 1.5f; public float Y_MouseSensitivity = 1.5f; public float mouseWhellSensitivity = 5f; public float X_Smooth = 0.05f; public float Y_Smooth = 0.1f; public float Y_MinLimit = -40f; public float Y_MaxLimit = 80f; public float occlusionDistanceStep = 0.5f; public int maxOcclusionChecks = 3; public float minOcclusiondistance = 0.5f; public bool ignorePause = false; public float smoothMaxSpeed = 133f; protected float closeUpTime = 0f; public float mouseX = 0f; public float mouseY = 0f; private float valX = 0f; private float valY = 0f; private float valZ = 0f; private float velDistance = 0f; private float startDistance = 0f; private float desiredDistance = 0f; private Vector3 position = Vector3.zero; private Vector3 desiredPosition = Vector3.zero; private float _distanceSmooth = 0f; private float preOccludeddistance = 0f; private float fixedDesiredDistance; private float yVelocity = 0f; protected float lastTime; protected bool useCloseUp =false; protected bool fixedModelBak = false; protected bool fixedForTargetLookAtBak = false; protected bool fixedPositionModelBak = false; protected bool isCameraAxisController = false; protected Vector3 fixedPositionBak = Vector3.zero; protected Transform targetLookAtBak = null; private float giddinessTimeX; private float giddinessTimeY; private float giddinessXSpeed = 360f; private float giddinessYSpeed = 360f; public bool useBlur; public virtual void Awake () { position = transform.position; useBlur = GetComponent<MotionBlur>()!=null; } protected virtual void OnDestroy() { } public virtual void Start() { fixedDesiredDistance = fixedDistance; startDistance = distance; distance = Mathf.Clamp(startDistance,distanceMin,distanceMax); Reset(); } public virtual void CloseUp (MyEvent e = null) { closeUpTime = 3f; try{ closeUpTime = System.Convert.ToSingle(e.Value); }catch(System.Exception ex){ Debug.LogWarning("ERROR Format \n" + ex.StackTrace); } useCloseUp = true; fixedModelBak = fixedModel; fixedPositionModelBak = fixedPositionModel; fixedForTargetLookAtBak = fixedForTargetLookAt; targetLookAtBak = targetLookAt; if(e.Param!=null) { if(e.Param.ContainsKey("fixedPositionModel")) fixedPositionModel = System.Convert.ToBoolean( e.Param["fixedPositionModel"] ); if(e.Param.ContainsKey("fixedForTargetLookAt")) fixedForTargetLookAt = System.Convert.ToBoolean( e.Param["fixedForTargetLookAt"] ); if(e.Param.ContainsKey("targetLookAt")) targetLookAt = e.Param["targetLookAt"] as Transform; if(e.Param.ContainsKey("fixedPosition")) fixedPosition = (Vector3)e.Param["fixedPosition"]; if(e.Param.ContainsKey("giddinessX")) { giddinessTimeX = (float)e.Param["giddinessX"]; if(useBlur)GetComponent<MotionBlur>().enabled = true; } if(e.Param.ContainsKey("giddinessY")) { giddinessTimeY = (float)e.Param["giddinessY"]; if(useBlur)GetComponent<MotionBlur>().enabled = true; } } StartCoroutine(StartCloseUp()); } protected virtual IEnumerator StartCloseUp() { yield return new WaitForSeconds(closeUpTime); Reset(); } public virtual void LateUpdate() { if(targetLookAt == null ) return; // if(!fixedModel && !fixedForTargetLookAt && !fixedPositionModel && !fixedForTargetRotationModel) // return; if(animation!=null && animation.isPlaying) { // Quaternion.LookRotation // transform.LookAt(targetLookAt); return ; } if(fixedModel) { if(fixedPositionModel) transform.position = targetLookAt.transform.position + fixedPosition; else desiredDistance = fixedDistance; transform.LookAt(targetLookAt); }else{ if(ignorePause || !pause )HandlePlayerInput(); } if(fixedForTargetLookAt) { if(giddinessTimeX>0) { mouseX = (giddinessTimeX -= GetNextDetla()); mouseX = mouseX * X_MouseSensitivity * giddinessXSpeed; } if(giddinessTimeY>0) { mouseY = (giddinessTimeY -= GetNextDetla()); mouseY = mouseY * Y_MouseSensitivity * giddinessYSpeed; } if(useBlur && giddinessTimeX<=0 && giddinessTimeX<=0)GetComponent<MotionBlur>().enabled = false; int count = 0; do { CalculateDesiredPosition(); count++; }while (CheckIfOccluded(count)); UpdatePosition(); if(fixedForTargetRotationModel && !isCameraAxisController) TargetRoataion(); } } protected virtual void TargetRoataion() { Transform target; if(targetLookAt.parent!=null) target = targetLookAt.parent; else target = targetLookAt; float yAngle = 0f; Vector3 axis = Vector3.Cross(transform.forward, target.forward); Vector3 cameraXZVec = new Vector3(transform.forward.x, 0f,transform.forward.z); Vector3 targetXZVec = new Vector3(target.forward.x, 0f,target.forward.z); yAngle = Vector3.Angle( cameraXZVec,targetXZVec) * (axis.y > 0 ? 1.5f : -1.5f); // Debug.Log(" yAngle = "+yAngle); if( yAngle > 15f || yAngle < -15f) { mouseX = mouseX + (yAngle * Time.deltaTime); mouseX = Mathf.Repeat(mouseX,360f); } } protected virtual void HandlePlayerInput() { float deadZone = 0.01f; isCameraAxisController = Input.GetButton("A"); if (isCameraAxisController) { mouseX += Input.GetAxis("Mouse X") * X_MouseSensitivity; mouseY -= Input.GetAxis("Mouse Y") * Y_MouseSensitivity; } mouseY = ClampAngle(mouseY, Y_MinLimit, Y_MaxLimit); if (Input.GetAxis("Mouse ScrollWheel") < -deadZone || Input.GetAxis("Mouse ScrollWheel") > deadZone) { desiredDistance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel") * mouseWhellSensitivity, distanceMin, distanceMax); preOccludeddistance = desiredDistance; _distanceSmooth = distanceSmooth; } //#if MY_CAMERA_DEBUG if( Input.GetKey(KeyCode.C) ) { giddinessTimeX = 2f; GetComponent<MotionBlur>().enabled = true; } //#endif } public virtual void CalculateDesiredPosition() { ResetdesiredDistance(); if(fixedModel) { fixedDesiredDistance = Mathf.SmoothDamp(fixedDesiredDistance, desiredDistance, ref velDistance, _distanceSmooth,smoothMaxSpeed, GetNextDetla()); desiredPosition = CalculatePosition(fixedRotationY, fixedRotationX, fixedDesiredDistance); }else{ distance = Mathf.SmoothDamp(distance, desiredDistance, ref velDistance, _distanceSmooth,smoothMaxSpeed, GetNextDetla()); desiredPosition = CalculatePosition(mouseY, mouseX, distance); } } protected virtual Vector3 CalculatePosition(float rotationX, float rotationY ,float distance) { Vector3 direction = new Vector3(0, 0, -distance); Quaternion rotation = Quaternion.Euler(rotationX, rotationY, 0); return targetLookAt.position + rotation * direction; } protected virtual bool CheckIfOccluded(int count) { bool isOccluded = false; float nearestdistance = CheckCameraPoints(targetLookAt.position,desiredPosition); if( nearestdistance != -1) { if(count < maxOcclusionChecks) { isOccluded = true; distance -= occlusionDistanceStep; if(distance < minOcclusiondistance) distance = minOcclusiondistance; } else { distance = nearestdistance - Camera.mainCamera.nearClipPlane; } desiredDistance = distance; _distanceSmooth = distanceResetSmooth; } return isOccluded; } protected virtual float CheckCameraPoints(Vector3 from,Vector3 to) { float neardistance = -1f; RaycastHit hitInfo; MyCamera.ClipPlanePoints cpp = ClipPlaneAtNear(to); #if MY_CAMERA_DEBUG Debug.DrawLine(from,to + transform.forward * - camera.nearClipPlane,Color.red); Debug.DrawLine(from, cpp.UpperLeft); Debug.DrawLine(from, cpp.UpperRight); Debug.DrawLine(from, cpp.LowerLeft); Debug.DrawLine(from, cpp.LowerRight); Debug.DrawLine(cpp.UpperLeft,cpp.UpperRight); Debug.DrawLine(cpp.UpperLeft,cpp.LowerLeft); Debug.DrawLine(cpp.UpperRight,cpp.LowerRight); Debug.DrawLine(cpp.LowerLeft,cpp.LowerRight); #endif int mask = 1 << 12; //Terrain Layer if(Physics.Linecast(from, cpp.UpperLeft,out hitInfo,mask) ) neardistance = hitInfo.distance; if (Physics.Linecast(from, cpp.LowerRight,out hitInfo,mask) ) if(hitInfo.distance < neardistance || neardistance == -1) neardistance = hitInfo.distance; if (Physics.Linecast(from, cpp.LowerLeft,out hitInfo,mask) ) if(hitInfo.distance < neardistance || neardistance == -1) neardistance = hitInfo.distance; if (Physics.Linecast(from, cpp.UpperRight,out hitInfo,mask) ) if(hitInfo.distance < neardistance || neardistance == -1) neardistance = hitInfo.distance; if (Physics.Linecast(from, to + this.transform.forward * - camera.nearClipPlane,out hitInfo,mask) ) if(hitInfo.distance < neardistance || neardistance == -1) neardistance = hitInfo.distance; return neardistance; } protected virtual void ResetdesiredDistance() { if( desiredDistance < preOccludeddistance ) { Vector3 pos = CalculatePosition(mouseY,mouseX,preOccludeddistance); float nearestdistance = CheckCameraPoints(targetLookAt.position,pos); if(nearestdistance == -1 || nearestdistance > preOccludeddistance) { desiredDistance = preOccludeddistance; } } } protected virtual void UpdatePosition() { float posX = Mathf.SmoothDamp(position.x, desiredPosition.x, ref valX, X_Smooth,smoothMaxSpeed, GetNextDetla()); float posY = Mathf.SmoothDamp(position.y, desiredPosition.y, ref valY, Y_Smooth,smoothMaxSpeed, GetNextDetla()); float posZ = Mathf.SmoothDamp(position.z, desiredPosition.z, ref valZ, X_Smooth,smoothMaxSpeed, GetNextDetla()); if(float.IsNaN(posX)) posX = position.x; if(float.IsNaN(posY)) posY = position.y; if(float.IsNaN(posZ)) posZ = position.z; position = new Vector3(posX, posY , posZ); transform.position = position; // transform.position = Vector3.Lerp(transform.position, position, Time.deltaTime * 10f); transform.LookAt(targetLookAt); } public virtual void Reset() { mouseX = 0f; mouseY = 10f; distance = startDistance; desiredDistance = distance; preOccludeddistance = distance; if(useCloseUp) { fixedModel = fixedModelBak; fixedPositionModel = fixedPositionModelBak; fixedForTargetLookAt= fixedForTargetLookAtBak ; useCloseUp = false; } } public virtual Animation GetCameraAnimatnion() { if(animation == null) { this.gameObject.AddComponent<Animation>(); //this.animation.AddClip } return animation; } public static MyCamera UseExistingOrCreateNewMainCamera() { GameObject tempCamera = null; MyCamera myCamera; if(Camera.mainCamera != null) { tempCamera = Camera.mainCamera.gameObject; }else{ if(tempCamera == null) { tempCamera = new GameObject("Main Camera"); tempCamera.AddComponent<Camera>(); } tempCamera.tag = "MainCamera"; } if(tempCamera.GetComponent<MyCamera>()==null){ tempCamera.AddComponent<MyCamera>(); myCamera = tempCamera.GetComponent<MyCamera>(); // ttargetLookAt = GameObject.Find("targetLookAt") as GameObject; // if(ttargetLookAt == null) // { // ttargetLookAt = new GameObject("targetLookAt"); // ttargetLookAt.transform.position = Vector3.zero; // myCamera.targetLookAt = ttargetLookAt.transform; // }else{ // myCamera.targetLookAt = ttargetLookAt.transform; // } }else{ myCamera = tempCamera.GetComponent<MyCamera>() ; //myCamera.targetLookAt = ttargetLookAt.transform; } return myCamera; } protected virtual float GetNextDetla() { if(ignorePause) return Time.fixedDeltaTime ; else return Time.deltaTime; } public static ClipPlanePoints ClipPlaneAtNear(Vector3 pos) { ClipPlanePoints cpp = new ClipPlanePoints(); if(Camera.mainCamera == null) return cpp; Transform cameraTransform = Camera.mainCamera.transform; float halfFOV = (Camera.mainCamera.fieldOfView / 2) * Mathf.Deg2Rad; float aspect = Camera.mainCamera.aspect; float distance = Camera.mainCamera.nearClipPlane; float height = distance * Mathf.Tan(halfFOV); float width = height * aspect; cpp.LowerRight = pos + cameraTransform.right * width; cpp.LowerRight -= cameraTransform.up * height; cpp.LowerRight += cameraTransform.forward * distance; cpp.LowerLeft = pos - cameraTransform.right * width; cpp.LowerLeft -= cameraTransform.up * height; cpp.LowerLeft += cameraTransform.forward * distance; cpp.UpperRight = pos + cameraTransform.right * width; cpp.UpperRight += cameraTransform.up * height; cpp.UpperRight += cameraTransform.forward * distance; cpp.UpperLeft = pos - cameraTransform.right * width; cpp.UpperLeft += cameraTransform.up * height; cpp.UpperLeft += cameraTransform.forward * distance; return cpp; } public static float ClampAngle(float angle,float min,float max) { do { if (angle < -360) angle += 360; if (angle > 360) angle -= 360; } while (angle < -360 || angle > 360); return Mathf.Clamp(angle, min, max); } public struct ClipPlanePoints { public Vector3 UpperLeft; public Vector3 UpperRight; public Vector3 LowerLeft; public Vector3 LowerRight; } }