- Home /
How can i prevent from moving the camera view under the terrain ?
I'm using this mouseorbit script attached to a camera. The problem is when i move the camera with the mouse and rotating it so the camera is under the terrain. I want that when it get to the terrain height then stop don't move down i mean don't get to this view under the character maximum to be in the terrain height..
To stop on terrain height i mean something like that when it's getting to this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MouseOrbit : MonoBehaviour
{
/* These variables are what tell the camera how its going to function by
* setting the viewing target, collision layers, and other properties
* such as distance and viewing angles */
public Transform viewTarget;
public LayerMask collisionLayers;
public float distance = 6.0f;
public float distanceSpeed = 150.0f;
public float collisionOffset = 0.3f;
public float minDistance = 4.0f;
public float maxDistance = 12.0f;
public float height = 1.5f;
public float horizontalRotationSpeed = 250.0f;
public float verticalRotationSpeed = 150.0f;
public float rotationDampening = 0.75f;
public float minVerticalAngle = -60.0f;
public float maxVerticalAngle = 60.0f;
public bool useRMBToAim = false;
/* These variables are meant to store values given by the script and
* not the user */
private float h, v, smoothDistance;
private Vector3 newPosition;
private Quaternion newRotation, smoothRotation;
private Transform cameraTransform;
/* This is where we initialize our script */
void Start()
{
Initialize();
}
/* This is where we set our private variables, check for null errors,
* and anything else that needs to be called once during startup */
void Initialize()
{
h = this.transform.eulerAngles.x;
v = this.transform.eulerAngles.y;
cameraTransform = this.transform;
smoothDistance = distance;
NullErrorCheck();
}
/* We check for null errors or warnings and notify the user to fix them */
void NullErrorCheck()
{
if (!viewTarget)
{
Debug.LogError("Please make sure to assign a view target!");
Debug.Break();
}
if (collisionLayers == 0)
{
Debug.LogWarning("Make sure to set the collision layers to the layers the camera should collide with!");
}
}
/* This is where we do all our camera updates. This is where the camera
* gets all of its functionality. From setting the position and rotation,
* to adjusting the camera to avoid geometry clipping */
void LateUpdate()
{
if (!viewTarget)
return;
/* We check for right mouse button functionality, set the rotation
* angles, and lock the mouse cursor */
if (!useRMBToAim)
{
/* Check to make sure the game isn't paused and lock the mouse cursor*/
if (Time.timeScale > 0.0f)
Cursor.lockState = CursorLockMode.Locked;
h += Input.GetAxis("Mouse X") * horizontalRotationSpeed * Time.deltaTime;
v -= Input.GetAxis("Mouse Y") * verticalRotationSpeed * Time.deltaTime;
h = ClampAngle(h, -360.0f, 360.0f);
v = ClampAngle(v, minVerticalAngle, maxVerticalAngle);
newRotation = Quaternion.Euler(v, h, 0.0f);
}
else
{
if (Input.GetMouseButton(1))
{
/* Check to make sure the game isn't paused and lock the mouse cursor */
if (Time.timeScale > 0.0f)
Cursor.lockState = CursorLockMode.Locked;
h += Input.GetAxis("Mouse X") * horizontalRotationSpeed * Time.deltaTime;
v -= Input.GetAxis("Mouse Y") * verticalRotationSpeed * Time.deltaTime;
h = ClampAngle(h, -360.0f, 360.0f);
v = ClampAngle(v, minVerticalAngle, maxVerticalAngle);
newRotation = Quaternion.Euler(v, h, 0.0f);
}
else
{
Cursor.lockState = CursorLockMode.Confined;
}
}
/* We set the distance by moving the mouse wheel and use a custom
* growth function as the time value for linear interpolation */
distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel") * 10, minDistance, maxDistance);
smoothDistance = Mathf.Lerp(smoothDistance, distance, TimeSignature(distanceSpeed));
/*We give the rotation some smoothing for a nicer effect */
smoothRotation = Quaternion.Slerp(smoothRotation, newRotation, TimeSignature((1 / rotationDampening) * 100.0f));
newPosition = viewTarget.position;
newPosition += smoothRotation * new Vector3(0.0f, height, -smoothDistance);
/* Calls the function to adjust the camera position to avoid clipping */
CheckSphere();
smoothRotation.eulerAngles = new Vector3(smoothRotation.eulerAngles.x, smoothRotation.eulerAngles.y, 0.0f);
cameraTransform.position = newPosition;
cameraTransform.rotation = smoothRotation;
}
/* This is where the camera checks for a collsion hit within a specified radius,
* and then moves the camera above the location it hit with an offset value */
void CheckSphere()
{
/* Add height to our spherecast origin */
Vector3 tmpVect = viewTarget.position;
tmpVect.y += height;
RaycastHit hit;
/* Get the direction from the camera position to the origin */
Vector3 dir = (newPosition - tmpVect).normalized;
/* Check a radius for collision hits and then set the new position for
* the camera */
if (Physics.SphereCast(tmpVect, 0.3f, dir, out hit, distance, collisionLayers))
{
newPosition = hit.point + (hit.normal * collisionOffset);
}
}
/* Keeps the angles values within their specificed minimum and maximum
* inputs while at the same time putting the values back to 0 if they
* go outside of the 360 degree range */
private float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
/* This is our custom logistic growth time signature with speed as input */
private float TimeSignature(float speed)
{
return 1.0f / (1.0f + 80.0f * Mathf.Exp(-speed * 0.02f));
}
}
It looks like the script i grabbed already handles terrain collision... i just need to make sure to set the collision layers on it to include the terrain. But not sure how to do it.
What i tried:
I added now a new layer in the inspector called it Terrain. Then in the hierarchy on the Terrain i change it's layer to terrain. Also in the script i selected Terrain. But it's still not working.
Answer by Getsumi3 · Aug 31, 2017 at 05:17 PM
Hello @haimmoshe ! I have a solution that I made a time ago with help of Unity community Just attach this script to your camera and set which layers 'should be protected from camera collision'
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera-Control/Mouse Orbit with zoom")]
public class MouseOrbitImproved : MonoBehaviour
{
public Transform target;
public float targetHeight = 1.7f;
public float distance = 5.0f;
public float offsetFromWall = 0.1f;
public float xSpeed = 120.0f;
public float ySpeed = 120.0f;
public float yMinLimit = -20f;
public float yMaxLimit = 80f;
public int zoomRate = 40;
public float zoomDampening = 5.0f;
public float distanceMin = .5f;
public float distanceMax = 15f;
private Rigidbody rigidbody;
public LayerMask collisionLayers = -1;
private float currentDistance;
private float desiredDistance;
private float correctedDistance;
float x = 0.0f;
float y = 0.0f;
// Use this for initialization
void Start()
{
Vector3 angles = transform.eulerAngles;
x = angles.y;
y = angles.x;
rigidbody = GetComponent<Rigidbody>();
// Make the rigid body not change rotation
if (rigidbody != null)
{
rigidbody.freezeRotation = true;
}
if (target == null)
FindPlayer();
}
void FindPlayer()
{
CharacterController controller = FindObjectOfType<CharacterController>();
if (controller)
{
target = controller.gameObject.transform;
}
}
void LateUpdate()
{
Vector3 vTargetOffset;
if (target)
{
x += Input.GetAxis("Mouse X") * xSpeed * distance * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
y = ClampAngle(y, yMinLimit, yMaxLimit);
Quaternion rotation = Quaternion.Euler(y, x, 0);
// calculate distance
desiredDistance -= Input.GetAxis ("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs (desiredDistance);
desiredDistance = Mathf.Clamp (desiredDistance, distanceMin, distanceMax);
correctedDistance = desiredDistance;
// calculate camera position
vTargetOffset = new Vector3 (0, -targetHeight, 0);
Vector3 position = target.position - (rotation * Vector3.forward * desiredDistance + vTargetOffset);
RaycastHit collisionHit;
Vector3 trueTargetPosition = new Vector3 (target.position.x, target.position.y + targetHeight, target.position.z);
bool isCorrected = false;
if (Physics.Linecast (trueTargetPosition, position, out collisionHit, collisionLayers.value))
{
// calculate and remove clipping
correctedDistance = Vector3.Distance (trueTargetPosition, collisionHit.point) - offsetFromWall;
isCorrected = true;
}
currentDistance = !isCorrected || correctedDistance > currentDistance ? Mathf.Lerp (currentDistance, correctedDistance, Time.deltaTime * zoomDampening) : correctedDistance;
currentDistance = Mathf.Clamp (currentDistance, distanceMin, distanceMax);
position = target.position - (rotation * Vector3.forward * currentDistance + vTargetOffset);
transform.rotation = rotation;
transform.position = position;
}
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp(angle, min, max);
}
}
Happy developing ^^
This script saved me! Searched many days to solve this camera under terrain issue and helped me improve my camera movement. Added some lines to access rotation only if right click is clicked. Thank you ^_^
Answer by FlipItPizZa · Aug 31, 2017 at 04:54 PM
public Transform Player;
public Transform Camera;
public float adjustBy;
void Update(){
Vector3 Adjustment = new Vector3(0 , adjustBy , 0);
if(Camera.position.y < Player.position.y){
Camera.position = Camera.Position + Adjustment;
}
}
Here the idea is to make it so that Whenever Camera Tries to go below feet level of player , the position of camera should get adjusted by some value
This should work unless you have situations where camera need to go below feet of player.