Camera collision (mouse orbit) - Physics.Linecast not good enough.

Hello people! Me and some friends are in the process of making a 3rd person adventure game, and I’m having problems with the camera collision. As the title reads, in our opinion, using Physics.Linecast is not good enough since the RaycastHit.point results in having some minor cutting in the surroundings. I’ve searched the forums for hours, and most people suggest using Linecast to solve the camera collision. Do you have any other ideas how to solve this problem (I’d rather not use rigidbody/collider). Is there a way to fake a radius of the RaycastHit.point so to get a better result?

Here’s the script, I would be really grateful for your assistance.

var layermask : LayerMask;
var target: Transform;
var height: float = 0.4;
var distance: float = 3.0;
var hit : RaycastHit;
var yMin : float = -4.0;
var yMax : float = 25.0;

var x : float = 0.0;
var y : float = 0.0;

private var xSpeed = 180.0;
private var ySpeed = 80.0;

private var minZoom: float = 1.0;
private var maxZoom: float = 4.0;
private var zoom: float = 2.0;

var v_pressed;
var scroll;

function Start() {
	//The cameras starting position - behind the character.
	
	var wantedRotationAngle = target.eulerAngles.y;
	var currentRotationAngle = transform.eulerAngles.y;
	currentRotationAngle = wantedRotationAngle;
	
	x = currentRotationAngle;
	}
	
function RotateBehindPlayer() {
	//The camera rotates to the back of the character.
	
	var wantedRotationAngle = target.eulerAngles.y;
	var currentRotationAngle = transform.eulerAngles.y;
	currentRotationAngle = wantedRotationAngle;
	
	x = Mathf.Lerp(x, currentRotationAngle, Time.deltaTime * 7);
	
	if (x == currentRotationAngle || Input.GetAxis("Mouse X"))
	{
		v_pressed = false;
		}
	}
	
function ScrollWheel() {
	//Zoom (in/out).
	if(Input.GetAxis("Mouse ScrollWheel") < 0)
    {
    	if(zoom + 0.5 >= maxZoom)
    	{
    		zoom = maxZoom;
    		}	
    	else
    	{
    		zoom = zoom + 0.5;
    		}}
     
    if(Input.GetAxis("Mouse ScrollWheel") > 0)
    {
    	if(zoom - 0.5 < minZoom)
    	{
    		zoom = minZoom;
    		}
    	else
    	{
    		zoom = zoom - 0.5;
    		}}
	
    distance = Mathf.Lerp(distance, zoom, Time.deltaTime * 4);
}
	
function Update () 
{
	x += Input.GetAxis("Mouse X") * xSpeed * 0.02;			
	y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
	
	if (y > yMax)
	{
		y = yMax;
		} 
	else if (y < yMin) 
	{
		y = yMin;
		}
		
	if (x >= 360)
	{
		x = 0;
		}
	
	if (x <= -360)
	{
		x = 0;
		}
		
	var rotation = Quaternion.Euler(y, x, 0);
	var position = rotation * Vector3(0.0, height, -distance) + target.position;

	transform.rotation = rotation;
	transform.position = position;

	//When the key "V" is pressed, the function (RotateBehindPlayer) activates.
	if (Input.GetKeyDown(KeyCode.V))
	{
		v_pressed = true;
		}
			
	if (v_pressed == true)
	{
		RotateBehindPlayer();
		}
	
	//The cameras adjustment to collision.
//	if (Physics.Linecast(target.position, transform.position, hit, layermask))
//	{
//		var tempDistance = Vector3.Distance(target.position, hit.point);
//		position = rotation * Vector3(0.0, height, -tempDistance) + target.position;
//		transform.position = position;
//		}

    //When the scroll wheel is used, the function (ScrollWheel) activates.
    if (Input.GetAxis("Mouse ScrollWheel"))
    {
    	scroll = true;
    	}
	if (scroll == true)
	{
		ScrollWheel();
		}
}

You are positioning your camera exactly at the hit point, I think you should offset it away from the hit point and a bit closer to the target to avoid near clipping issues.
transform.position = hit.point + (target.position - transform.position).normalized * smallOffset;

2 Likes
  1. What Partel said

  2. Move your code to LateUpdate

Thanks for replying! However, there is still some near clipping issues right before the camera collides, since the hit requires a point contact.
I’m new to programming, so I might have it wrong.

Regards

  • Johan

[EDIT] - Solved it, thanks!

var value_offSet = 1.3;
var position_offSet = rotation * Vector3(0.0, height, -distance*value_offSet) + target.position;

if (Physics.Linecast(target.position, position_offSet, hit, layermask))
{
transform.position = hit.point + (target.position - transform.position).normalized;
}

I use Raycast instead, this is my code (in c#):

Ray r = new Ray(orbit.position,cam.transform.position);
RaycastHit hit;
if(Physics.Raycast(r,out hit)) //Something is in the way, gotta adjust the camera.
{
    cam.transform.position = hit.point + hit.normal*0.1f;   //put it at the position that it hit
}

Physics.SphereCast can be useful in these situations.