- Home /
Question by
PlaguedShadow · Apr 05, 2014 at 04:57 PM ·
cameraclippingclipping plane
3rd Person Camera clips through walls
Right now what I have sort of works. It does stop the camera from clipping most of the time. However if you rotate the camera too quickly it will clip. There is something that is causing the issue, but I just can't figure out what it is.
This is my camera code:
using UnityEngine;
using System.Collections;
public class CameraControl2 : MonoBehaviour
{
// Inspector serialized
[SerializeField]
private float _distanceBehind; // Distance behind the player where camera will be.
[SerializeField]
private float _distanceUp;
[SerializeField]
public static float _lookAtSmoothing;
[SerializeField]
private Transform _targetXForm;
[SerializeField]
private CharacterControllerLogic2 targetScript;
// Smoothing and damping
private Vector3 velocityCamSmooth = Vector3.zero;
[SerializeField]
private float camSmoothDampTime = 0.1f;
// Private global
private Vector3 _lookDir;
private Vector3 _targetPos;
private float rightX = 0;
private float rightY = 0;
private Transform transparentObj;
private AutoTransparent transparentObjScript;
private CamStates camState = CamStates.Behind;
private Animator _playerAnimator;
public LayerMask ignoreCamCollisionLayer;
public struct clipPlanePoints
{
public Vector3 upperLeft;
public Vector3 upperRight;
public Vector3 lowerLeft;
public Vector3 lowerRight;
}
public float occlusionDistanceStep = 0.5f;
public int maxOcclusionChecks = 10;
private float tempDesiredDistance = 0f;
private float desiredDistance = 0f;
public float distanceSmooth = 0.5f;
private float velDistance = 0f;
private Vector3 desiredPosition;
public enum CamStates
{
Behind,
Target,
Free
}
public CamStates CamState
{
get { return this.camState; }
}
void OnEnable()
{
Messenger.AddListener("EnterTargeting", EnterTargeting);
Messenger.AddListener("ExitTargeting", ExitTargeting);
}
void OnDisable()
{
Messenger.RemoveListener("EnterTargeting", EnterTargeting);
Messenger.RemoveListener("ExitTargeting", ExitTargeting);
}
void Awake()
{
targetScript = GameObject.FindGameObjectWithTag("Player").GetComponent<CharacterControllerLogic2>();
}
// Use this for initialization
void Start ()
{
targetScript = GameObject.FindGameObjectWithTag("Player").GetComponent<CharacterControllerLogic2>();
_targetXForm = GameObject.FindGameObjectWithTag("Player").transform;
_playerAnimator = GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>();
_lookDir = _targetXForm.forward;
if (targetScript == null)
{
Debug.LogError("Could not find \"CharacterControllerLogic2\"!");
}
ignoreCamCollisionLayer = ~ignoreCamCollisionLayer;
}
// Update is called once per frame
void Update ()
{
}
void FixedUpdate()
{
rightX = 0;
rightY = 0;
if (GameSettings.KEYBOARD)
{
rightX = Input.GetAxis("KBCameraH");
rightY = Input.GetAxis("KBCameraV");
}
else if (GameSettings.XBOX)
{
rightX = Input.GetAxis("XBOXCameraH");
rightY = Input.GetAxis("XBOXCameraV");
}
else if (GameSettings.PS3)
{
rightX = Input.GetAxis("PS3CameraH");
if (GameSettings.PS3_OSX)
{
rightY = Input.GetAxis("PS3CameraVOSX");
}
else
{
rightY = Input.GetAxis("PS3CameraV");
}
}
Vector3 characterOffset = Vector3.zero;
characterOffset = _targetXForm.position + new Vector3(0, _distanceUp, 0);
if (!_playerAnimator.GetBool("Targeting"))
{
camState = CamStates.Behind;
}
switch(camState)
{
case CamStates.Behind:
// Calculate direction from camera to player, kill y, and normalize to give a valid direction with unit magnitude.
_lookDir = characterOffset - this.transform.position;
_lookDir.y = 0;
_lookDir.Normalize();
Debug.DrawRay(this.transform.position, _lookDir, Color.black);
//Debug.DrawRay(_targetXForm.position, Vector3.up * _distanceUp, Color.red);
//Debug.DrawRay(_targetXForm.position, -Vector3.forward * _distanceBehind, Color.blue);
//Debug.DrawLine(_targetXForm.position, _targetPos, Color.magenta);
break;
case CamStates.Target:
rightX = 0;
rightY = 0;
_lookDir = _targetXForm.forward;
break;
}
if (PlayerCharacter.currentPlayerState == PlayerCharacter.playersState.PLAY)
{
// allow free rotation with right joystick
transform.RotateAround(_targetXForm.position, Vector3.up, rightX * 200 * Time.deltaTime);
}
else if (PlayerCharacter.currentPlayerState == PlayerCharacter.playersState.DEAD)
{
// allow free rotation with right joystick
transform.RotateAround(_targetXForm.position, Vector3.up, rightX * 50 * Time.deltaTime);
}
Vector3 relativePos = _targetXForm.position - transform.position;
Vector3 relativePosRight = Vector3.Cross(relativePos, Vector3.up);
if (PlayerCharacter.currentPlayerState == PlayerCharacter.playersState.PLAY)
transform.RotateAround(_targetXForm.position, relativePosRight, rightY * 200 * Time.deltaTime);
// Setting target position to be correct offset.
_targetPos = characterOffset + (_targetXForm.up * _distanceUp) - (_lookDir * _distanceBehind);
CompensateForWalls(characterOffset, ref _targetPos);
// Move camera to new position smoothly.
SmoothPosition(this.transform.position, _targetPos);
//Vector3 lookAtTarget = characterOffset;
//if (targetScript.PushingObject)
//lookAtTarget = _targetXForm.forward;
// Look at player.
transform.LookAt(characterOffset);
//Debug.Log (rightX + " " + rightY);
}
Vector3 CalculateDesiredPosition(Vector3 characterOffset)
{
tempDesiredDistance = Mathf.SmoothDamp(tempDesiredDistance, _distanceBehind, ref velDistance, distanceSmooth);
desiredPosition = CalculatePosition(tempDesiredDistance, characterOffset);
return desiredPosition;
}
Vector3 CalculatePosition(float distance, Vector3 characterOffset)
{
return characterOffset + (_targetXForm.up * _distanceUp) - (_lookDir * distance);
}
public static clipPlanePoints ClipPlaneAtNear(Vector3 pos)
{
var clipPlanePoints = new clipPlanePoints();
if(Camera.main == null)
{return clipPlanePoints;}
var transform = Camera.main.transform;
var halfFOV = (Camera.main.fieldOfView/2)*Mathf.Deg2Rad;
var aspect = Camera.main.aspect;
var distance = Camera.main.nearClipPlane;
var height = distance *Mathf.Tan(halfFOV);
var width = height * aspect;
//'draw' the points
clipPlanePoints.lowerRight = pos + transform.right *width;
clipPlanePoints.lowerRight -= transform.up*height;
clipPlanePoints.lowerRight += transform.forward * distance;
clipPlanePoints.lowerLeft = pos - transform.right *width;
clipPlanePoints.lowerLeft -= transform.up*height;
clipPlanePoints.lowerLeft += transform.forward * distance;
clipPlanePoints.upperRight = pos + transform.right *width;
clipPlanePoints.upperRight += transform.up*height;
clipPlanePoints.upperRight += transform.forward * distance;
clipPlanePoints.upperLeft = pos - transform.right *width;
clipPlanePoints.upperLeft += transform.up*height;
clipPlanePoints.upperLeft += transform.forward * distance;
return clipPlanePoints;
}
bool CheckIfOccluded(int count, Vector3 characterOffset)
{
var isOccluded = false;
var nearestDistance = checkCameraPoints(characterOffset, _targetPos);
if(nearestDistance != -1)
{
if(count < maxOcclusionChecks)
{
isOccluded = true;
tempDesiredDistance -= occlusionDistanceStep;
if (tempDesiredDistance < 0.25f)
{
tempDesiredDistance = 0.25f;
}
}
else
{
tempDesiredDistance = nearestDistance - Camera.main.nearClipPlane;
}
desiredDistance = tempDesiredDistance;
}
return isOccluded;
}
float checkCameraPoints(Vector3 from, Vector3 to)
{
float nearDistance = -1.0f;
RaycastHit hitInfo;
clipPlanePoints clipPlanePoints;
clipPlanePoints = ClipPlaneAtNear(to);
//draw lines
Debug.DrawLine(from, to + transform.forward * -camera.nearClipPlane, Color.red);
Debug.DrawLine(from, clipPlanePoints.upperLeft);
Debug.DrawLine(from, clipPlanePoints.upperRight);
Debug.DrawLine(from, clipPlanePoints.lowerLeft);
Debug.DrawLine(from, clipPlanePoints.lowerRight);
Debug.DrawLine(clipPlanePoints.upperLeft, clipPlanePoints.upperRight);
Debug.DrawLine(clipPlanePoints.upperRight, clipPlanePoints.lowerRight);
Debug.DrawLine(clipPlanePoints.lowerRight, clipPlanePoints.lowerLeft);
Debug.DrawLine(clipPlanePoints.lowerLeft, clipPlanePoints.upperLeft);
if(Physics.Linecast(from, clipPlanePoints.upperLeft, out hitInfo, ignoreCamCollisionLayer))
{
nearDistance = hitInfo.distance;
}
if(Physics.Linecast(from, clipPlanePoints.lowerLeft, out hitInfo, ignoreCamCollisionLayer))
{
if(hitInfo.distance < nearDistance || nearDistance == -1)
{
nearDistance = hitInfo.distance;
}
}
if(Physics.Linecast(from, clipPlanePoints.upperRight, out hitInfo, ignoreCamCollisionLayer))
{
if(hitInfo.distance < nearDistance || nearDistance == -1)
{
nearDistance = hitInfo.distance;
}
}
if(Physics.Linecast(from, clipPlanePoints.lowerRight, out hitInfo, ignoreCamCollisionLayer))
{
if(hitInfo.distance < nearDistance || nearDistance == -1)
{
nearDistance = hitInfo.distance;
}
//Debug.LogWarning(hitInfo.normal);
}
// if(Physics.Linecast(from, to + transform.forward * -camera.nearClipPlane, out hitInfo) && hitInfo.collider.tag != "Player")
// {
// if(hitInfo.distance < nearDistance || nearDistance == -1)
// {
// nearDistance = hitInfo.distance;
// }
// }
//Debug.Log("NearDistance: " + nearDistance);
return nearDistance;
}
private void SmoothPosition(Vector3 fromPos, Vector3 toPos)
{
this.transform.position = Vector3.SmoothDamp(fromPos, toPos, ref velocityCamSmooth, camSmoothDampTime);
}
private void SmoothLookAt(Vector3 fromPos, Vector3 targetPos)
{
Quaternion rotation = Quaternion.LookRotation(targetPos - fromPos);
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, rotation, Time.deltaTime * _lookAtSmoothing);
}
private void CompensateForWalls(Vector3 fromObject, ref Vector3 toTarget)
{
RaycastHit wallHit;
// if (Physics.Linecast(fromObject, toTarget, out wallHit, ignoreCamCollisionLayer))
// {
// if (wallHit.transform.tag.Equals("Transparent"))
// {
// transparentObj = wallHit.transform;
//
// transparentObjScript = transparentObj.GetComponent<AutoTransparent>();
// if (transparentObjScript == null) // if no script is attached, attach one
// {
// transparentObjScript = transparentObj.gameObject.AddComponent<AutoTransparent>();
// }
// transparentObjScript.BeTransparent = true;
// }
// else
// {
// if (transparentObjScript != null)
// {
// transparentObjScript.BeTransparent = false;
// transparentObjScript = null;
// }
// }
// }
// else
// {
// if (transparentObjScript != null)
// {
// transparentObjScript.BeTransparent = false;
// transparentObjScript = null;
// }
var count = 0;
do
{
count++;
}while (CheckIfOccluded(count, fromObject));
toTarget = CalculateDesiredPosition(fromObject);
//}
}
private void EnterTargeting()
{
if (PlayerCharacter.currentPlayerState == PlayerCharacter.playersState.PLAY)
{
_playerAnimator.SetBool("Targeting", true);
camState = CamStates.Target;
}
}
private void ExitTargeting()
{
if (PlayerCharacter.currentPlayerState == PlayerCharacter.playersState.PLAY)
{
_playerAnimator.SetBool("Targeting", false);
camState = CamStates.Behind;
}
}
}
Comment