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;
	}

}