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