- Home /
Dark Souls like camera going through walls, please help
Hello guys, I am working on third person camera like the one in Dark Souls, which orbits around the player adapting its distance to avoid hiding the player.
I found this post about a similar question but there were no suitable answers. We are casting four rays from the camera's position to each border of its near clipping plane. If any of these rays hit a wall, our script corrects the camera's position.
Easier said than done, the camera is behaving quite erratically. Can anyone here help me to understand whats going on or have a better suggestion to solve this problem? The script is right below.
The camera is "erratically" because it works pretty good sometimes and it fails in certain cases. For example, when the player is close of a wall we can move the camera around the player without letting it going through the wall directly. Instead of that the camera is moved like we show in the next picture (orbiting the player until find the obstacle, them moving touching the wall, passing through the player and starting the cycle again), however when getting close of the player it starts to move a bit weirdly and sometimes it goes a bit inside the wall (the objective of the raycasting was to avoiding this kind of behavior).
Thanks in advance.
Our most recent (and problematic) camera script:
using UnityEngine;
using System.Collections;
public class MonkeyCamera : MonoBehaviour
{
public Transform Target;
public float Distance = 5.0f;
public float xSpeed = 250.0f;
public float ySpeed = 120.0f;
public float yMinLimit = -20.0f;
public float yMaxLimit = 80.0f;
public float freeSpeed = 3;
public float invisibleWallPrevent = 0.7f;
public float charClippingPrevent = 2.0f;
private Rigidbody targetRigidBody;
private string headstr = "Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 Spine1/Bip001Neck/Bip001 Head";
private float x;
private float y;
private Transform head;
private Camera camera;
private float fovx;
private float fovy;
void Awake()
{
Vector3 angles = transform.eulerAngles;
x = angles.x;
y = angles.y;
targetRigidBody = (Rigidbody)Target.GetComponent("Rigidbody");
head = Target.transform.FindChild(headstr);
camera = transform.camera;
fovy = camera.fieldOfView;
fovx = Mathf.Rad2Deg * ( 2 * Mathf.Atan( Mathf.Tan( (fovy * Mathf.Deg2Rad) / 2 )
* camera.aspect
)
);
}
void LateUpdate()
{
if(Target != null)
{
Quaternion rotation;
if( Input.GetKeyDown(KeyCode.E) )
{
//centralizar camera
y = Target.transform.eulerAngles.y;
//rotation = Quaternion.Euler(0, desiredAngle, 0);
x = Target.transform.eulerAngles.x;
}
else
{
x += (float)(Input.GetAxis("Mouse X") * xSpeed);
y -= (float)(Input.GetAxis("Mouse Y") * ySpeed);
}
y = ClampAngle(y, yMinLimit, yMaxLimit);
rotation = Quaternion.Euler(y, x, 0);
Vector3 position = rotation * (new Vector3(0.0f, 0.0f, -Distance)) + head.position;
transform.rotation = rotation;
transform.position = position;
Vector3 rayDirection = Vector3.Normalize(transform.position - head.position);
RaycastHit[] hits = Physics.RaycastAll(head.position, rayDirection, Distance);
float minDist = Distance;
Vector3 hitPoint = new Vector3();
for(int i = 0; i < hits.Length; i++)
{
if( !hits[i].transform.Equals(Target.transform) && hits[i].distance < minDist )
{
minDist = hits[i].distance;
hitPoint = hits[i].point;
}
}
if(minDist < Distance)
{
Vector3 d = (hitPoint - head.transform.position)
* ( 1 + (camera.nearClipPlane / minDist) );
transform.position = head.transform.position + d;
}
Vector3 ncp = Vector3.Normalize(transform.forward) * camera.nearClipPlane;
float semiw = Vector3.Magnitude(ncp) * Mathf.Tan(fovx * Mathf.Deg2Rad / 2);
float semih = Vector3.Magnitude(ncp) * Mathf.Tan(fovy * Mathf.Deg2Rad / 2);
Vector3 rv = Vector3.Normalize(transform.right) * semiw + ncp;
Vector3 lv = Vector3.Normalize(-1 * transform.right) * semiw + ncp;
Vector3 uv = Vector3.Normalize(transform.up) * semih + ncp;
Vector3 dv = Vector3.Normalize(-1 * transform.up) * semih + ncp;
wallClipAjust(rv);
wallClipAjust(lv);
wallClipAjust(uv);
wallClipAjust(dv);
transform.LookAt(head.position);
Debug.DrawLine(transform.position, transform.position + rv);
Debug.DrawLine(transform.position, transform.position + lv);
Debug.DrawLine(transform.position, transform.position + dv);
Debug.DrawLine(transform.position, transform.position + uv);
}
}
private void wallClipAjust(Vector3 vector)
{
RaycastHit[] hits = Physics.RaycastAll( transform.position - vector, Vector3.Normalize(vector), Vector3.Magnitude(vector * 2) );
//assumindo que o maximo de hits para o near clip plane eh 1
if(hits.Length != 0)
{
//Vector3 d = ((2 * Vector3.Magnitude(vector)) - hits[0].distance) * Vector3.Normalize(hits[0].normal);
float d = (Vector3.Magnitude(vector * 2) - hits[0].distance)
* Mathf.Cos(Vector3.Angle( (-1 * hits[0].normal),
vector
)
);
transform.position += d * Vector3.Normalize(hits[0].normal);
}
}
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);
}
}
"The camera bevaves quite eratically" is not a very precise description of the issue. What exactly is the problem with the camera's behavior?
Sorry, I added more details about its behavior to try to show the problem in a better way.
Your answer
Follow this Question
Related Questions
Limiting vertical camera rotation 0 Answers
Why is the camera going crazy when the player moves? 0 Answers
Change from 3D to 2D 1 Answer