Camera Zoom When Camera Touches Geometry

So here is my script
I have a box collider around my camera.
I have another script to control the zoom of the camera.
This script sets that zoom.
I have found out it does not detect collision.

using UnityEngine;
using System.Collections;

public class AntiClip : MonoBehaviour {

	public	bool zoomReady= true;
	public bool gotPreviousZoomState;
	public float previousZoomState;

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

	void OnTriggerEnter(Collider col)
	{
		print ("clip");
		if (gotPreviousZoomState == false)
		{
			previousZoomState = GetComponent <CameraControl>().distance;
			gotPreviousZoomState = true;
			zoomReady = true;
		}

		if (zoomReady == true)
		{
			StartCoroutine (zoom());
		}
	}

	void OnTriggerExit (Collider col)
	{
		GetComponent <CameraControl>().distance = previousZoomState;
		gotPreviousZoomState = false;
	}
	IEnumerator zoom()
	{
		zoomReady = false;
		GetComponent <CameraControl>().distance = previousZoomState = GetComponent <CameraControl>().distance- 0.5f;
		yield return new WaitForSeconds (0.01f);
		zoomReady = true;
	}
}

I would use Raycasts instead. They can shoot a physics check in a direction and return point information. Unfortunately they only detect in a straight line, but you could use a SphereCast for more complex physics checks. Here’s a part of my camera script (it’s a modified version of the MouseOrbit.js) Also sorry it’s in Javascript :stuck_out_tongue:

 		var newDistance = distance;
 		
 		var p : RaycastHit;
 		var l = target.position - transform.position;
 		
 		
 		if (Physics.Raycast(target.position, -l, p, distance, m))
 		{
 			var d = Vector3.Distance(target.position, p.point);
 			//Debug.Log("" + d);
 			newDistance = d;
 		}
 		
 		
 		Debug.DrawRay(target.position, -l, Color.green);
 		
        var rotation = Quaternion.Euler(y, x, 0);
        var position = rotation * Vector3(0.0, 0.0, -newDistance + 0.3) + target.position;
        
        transform.rotation = rotation;
        transform.position = position;

Where “m” is the layer mask and x and y are clamped versions of Mouse Input.

I will just dump my unoptimized, uncommented customized SmoothFollow script here for your to take some ideas from.

The basic ideas focuses on the camera’s frustrum box: You calculate four world points which sit at the camera’s field of view’s four corners. Then you calculate four more points by taking one frustrum point and going forward (relative to the camera) for a distance equal to the distance between camera and player (his head, preferably). Then I cast rays from each of the four points near the player’s position back twards the frustrum points. If one one the points hits an obstacle, I know that one of the camera’s viewport corners would clip through terrain, so I move the camera at a distance from the player that is equal to the lowest distance between any of the four rays and their hit.points (or the maximum camera distance, if none have hit anything).

It’s a little complicated to describe, but it’s much better than casting a signel ray because with one ray, you will have the clipping at corners isue where your camera sees through terrain that is very near. The downside is that thing objects like street lamps are not registered by the rays because the object lies between them, So you could theoretically enhance the method by casting more rays (one through the middle, for example).

/*
This camera smoothes out rotation around the y-axis and height.
Horizontal Distance to the target is always fixed.

There are many different ways to smooth the rotation but doing it this way gives you a lot of control over how the camera behaves.

For every of those smoothed values we calculate the wanted value and the current value.
Then we smooth it using the Lerp function.
Then we apply the smoothed values to the transform's position.
*/


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class SmoothFollow : MonoBehaviour {

	// The target we are following
	public Transform target;
    //the position the player looks at (like, on a wall some distance away, or on the ground next to him)
	public Vector3 lookAtTarget;
	public float defaultMinDistance = 0.5f;
	public float defaultMaxDistance = 5f;
	public float defaultMaxHeight = 3f;
	// The distance in the x-z plane to the target
	public float distance = 6.0f;
	public float minDistance = 1.0f;
	public float maxDistance = 6.0f;
	// the height we want the camera to be above the target
	public float maxHeight = 3.0f;
	public float height = 3.0f;

	public float dampingFactor = 3.0f;
	public bool collision = false;
	public LayerMask mask;
	private float maximumY;
	private ValueWrapper<float> camAnchorRotX = new ValueWrapper<float>(0f);
	public float horizontalOffsetDefault = 0.22f;
	public float horizontalOffset;
	public bool damping = false;


	void Start() {
		//CreateFrustrumBox();
		maximumY = Manager.player.GetComponent<PlayerControl>().maximumY;
	}
		
	void Update() {
		camAnchorRotX.Value = target.localEulerAngles.x;
		if(camAnchorRotX.Value > 180) {
			camAnchorRotX.Value -= 360;
		}
		CastFrustrumRays();
	}

	void FixedUpdate () {
		// Early out if we don't have a target
		if (target == null) {
			return;
		}

		/*
		// Calculate the current rotation angles
		float wantedRotationAngle = target.eulerAngles.y;
		float wantedHeight = target.position.y + maxHeight;
		
		float currentRotationAngle = transform.eulerAngles.y;
		float currentHeight = transform.position.y;
		
		// Damp the rotation around the y-axis
		currentRotationAngle = Mathf.LerpAngle (currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);
		
		// Damp the height
		currentHeight = Mathf.Lerp (currentHeight, wantedHeight, heightDamping * Time.deltaTime);
		
		// Convert the angle into a rotation
		Quaternion currentRotation = Quaternion.Euler (0, currentRotationAngle, 0);
		
		// Set the position of the camera on the x-z plane to:
		// distance meters behind the target
		transform.position = target.position;
		transform.position -= currentRotation * Vector3.forward * distance;
		
		// Set the height of the camera
		transform.position = new Vector3(transform.position.x, currentHeight, transform.position.z);
		
		// Always look at the target
		lookAtTarget = new Vector3(target.transform.position.x, target.transform.position.y + maxHeight, target.transform.position.z);
		transform.LookAt (lookAtTarget);
		*/


		//height = maxHeight * (1 - (Mathf.Abs(camAnchorRotX.Value) / maximumY));
		lookAtTarget = target.position + Vector3.up * height + target.right * horizontalOffset;
		Vector3 wantedPosition = lookAtTarget - target.forward * distance;
		if(damping == true) {
			transform.position = Vector3.Lerp(transform.position, wantedPosition, Time.deltaTime * dampingFactor);
		}
		else {
			transform.position = wantedPosition;
		}

		// Always look at the target
		transform.LookAt (lookAtTarget);

	}

	void CreateFrustrumBox() {

		float h = Mathf.Tan(GetComponent<Camera>().fov * Mathf.Deg2Rad * 0.5f) * GetComponent<Camera>().nearClipPlane * 2f;
		GetComponent<Collider>().transform.localScale = new Vector3(h * GetComponent<Camera>().aspect, h, 0.01f);
		GetComponent<BoxCollider>().center = new Vector3(0, 0, 100);
	}

	void CastFrustrumRays() {

		float distanceToCamera = Vector3.Distance(lookAtTarget, transform.position);

		Vector3 pos00_Cam = GetComponent<Camera>().ViewportToWorldPoint(new Vector3(0, 0, GetComponent<Camera>().nearClipPlane));
		Vector3 pos01_Cam = GetComponent<Camera>().ViewportToWorldPoint(new Vector3(0, 1, GetComponent<Camera>().nearClipPlane));
		Vector3 pos11_Cam = GetComponent<Camera>().ViewportToWorldPoint(new Vector3(1, 1, GetComponent<Camera>().nearClipPlane));
		Vector3 pos10_Cam = GetComponent<Camera>().ViewportToWorldPoint(new Vector3(1, 0, GetComponent<Camera>().nearClipPlane));

		//float forwardOffset = maxDistance - camera.nearClipPlane - (maxHeight - maxHeight * (1 - (Mathf.Abs(camAnchorRotX.Value) / maximumY)));
		float forwardOffset = Mathf.Clamp(distanceToCamera, minDistance, maxDistance);

		if(forwardOffset <= 0) {
			forwardOffset = 0.1f;
		}

		Vector3 pos00_Target = pos00_Cam + transform.forward * forwardOffset;
		Vector3 pos01_Target = pos01_Cam + transform.forward * forwardOffset;
		Vector3 pos11_Target = pos11_Cam + transform.forward * forwardOffset;
		Vector3 pos10_Target = pos10_Cam + transform.forward * forwardOffset;

		Ray ray00 = new Ray(pos00_Target, -transform.forward);
		Ray ray01 = new Ray(pos01_Target, -transform.forward);
		Ray ray11 = new Ray(pos11_Target, -transform.forward);
		Ray ray10 = new Ray(pos10_Target, -transform.forward);

		RaycastHit hit00 = new RaycastHit();
		RaycastHit hit01 = new RaycastHit();
		RaycastHit hit11 = new RaycastHit();
		RaycastHit hit10 = new RaycastHit();

		Debug.DrawLine(pos00_Target, pos01_Target);
		Debug.DrawLine(pos01_Target, pos11_Target);
		Debug.DrawLine(pos11_Target, pos10_Target);
		Debug.DrawLine(pos10_Target, pos00_Target);

		Ray[] rays = new Ray[] {ray00, ray01, ray11, ray10};
		RaycastHit[] hits = new RaycastHit[] {hit00, hit01, hit11, hit10};
		Vector3[] positions_Target = new Vector3[] {pos00_Target, pos01_Target, pos11_Target, pos10_Target};
		Vector3[] positions_Cam = new Vector3[] {pos00_Cam, pos01_Cam, pos11_Cam, pos10_Cam};

		int i = 0;

		if(Physics.Raycast(ray00, out hit00, maxDistance, mask)) {
			collision = true;
			Debug.DrawRay(pos00_Target, pos00_Cam - pos00_Target, Color.red);
		}
		else if(Physics.Raycast(ray01, out hit01, maxDistance, mask)) {
			collision = true;
			Debug.DrawRay(pos01_Target, pos01_Cam - pos01_Target, Color.red);
		}
		else if(Physics.Raycast(ray11, out hit11, maxDistance, mask)) {
			collision = true;
			Debug.DrawRay(pos11_Target, pos11_Cam - pos11_Target, Color.red);
		}
		else if(Physics.Raycast(ray10, out hit10, maxDistance, mask)) {
			collision = true;
			Debug.DrawRay(pos10_Target, pos10_Cam - pos10_Target, Color.red);
		}
		else {
			collision = false;
		}

		if(collision == false) {
			for(i = 0; i < 4; i++) {
				Debug.DrawRay(positions_Target<em>, positions_Cam <em>- positions_Target*, Color.green);*</em></em>

* }*
* }*

* float minCollisionDistance = maxDistance;*

* if(collision == true) {*
* float distance00 = Vector3.Distance(lookAtTarget, hit00.point);*
* float distance01 = Vector3.Distance(lookAtTarget, hit01.point);*
* float distance11 = Vector3.Distance(lookAtTarget, hit11.point);*
* float distance10 = Vector3.Distance(lookAtTarget, hit10.point);*
* minCollisionDistance = Mathf.Min(distance00, distance01, distance11, distance10);*
* minCollisionDistance = Mathf.Clamp(minCollisionDistance, minDistance, maxDistance);*
* }*
* else {*
* minCollisionDistance = maxDistance;*
* }*

_ distance = minCollisionDistance - Mathf.Abs(0 + horizontalOffset) * 2;
* }
}*_