Hi everybody,
actually developing a third character controller,
I try to implement a collision/occlusion camera system.
(To avoid collision and character occlusion by other objects.)
The way I choose to do this system is by casting a virtual sphere from character head to camera position.
If the virtual sphere cast something then the camera move forward or backward to his initial position if anything is between camera and character. (If the virtual sphere cast nothing.)
And it works pretty well, but I have a little problem with this system.
(I already tried another system with 5 rays from character head to the camera 4 points near plane and the fifth one backward the camera position. But I don’t really like this system.)
So yes I have a problem when the player is really near some walls, the camera move forward and when she is at the character head position she move backward through the walls.
I hope there is a solution so this is my code.
using UnityEngine;
using System.Collections;
public class TP_CtrlCam_V2 : MonoBehaviour {
private Vector3 cameraActualPosition;
private Vector3 cameraPreviousPosition;
private Vector3 playerActualPosition;
private Vector3 playerPreviousPosition;
private float rotSpeed = 2.0f;
private float occlusionDistanceStep;
private float preOccludedDistance;
private int maxOcclusionChecks = 10;
private int count;
private Vector2 padDeadZone = new Vector2(0.1f, -0.1f);
private GameObject playerRoot;
private GameObject playerHead;
private GameObject debugSphere;
public static GameObject cameraRoot;
public static GameObject cameraGO;
public static float sphereRadius = 1.1f;
void Start()
{
cameraRoot = GameObject.Find("Ctrl_Cam_World");
playerRoot = GameObject.Find("Ctrl_Char_World");
playerHead = GameObject.Find("Ctrl_Char_Head");
cameraGO = GameObject.Find("GO_Camera");
playerActualPosition = playerRoot.transform.position;
playerPreviousPosition = playerRoot.transform.position;
cameraActualPosition = cameraRoot.transform.position;
//cameraPreviousPosition = cameraRoot.transform.position;
debugSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
debugSphere.transform.renderer.material.color = new Color(0.5f, 0.0f, 0.0f, 0.5f);
debugSphere.transform.name = "debugSphere";
debugSphere.transform.localScale = new Vector3(sphereRadius, sphereRadius, sphereRadius);
debugSphere.transform.collider.enabled = false;
debugSphere.transform.position = cameraRoot.transform.position;
preOccludedDistance = Vector3.Distance(playerHead.transform.position, cameraRoot.transform.position);
occlusionDistanceStep = preOccludedDistance / maxOcclusionChecks;
}
void FixedUpdate()
{
float plLR = TP_PadManager.GamePadStick_B_Manager_LR();
float plUD = TP_PadManager.GamePadStick_B_Manager_UD();
///--Cam Follow--///
playerActualPosition = playerRoot.transform.position;
cameraActualPosition = cameraRoot.transform.position;
cameraRoot.transform.position = cameraActualPosition + (playerActualPosition - playerPreviousPosition);
playerPreviousPosition = playerActualPosition;
//cameraPreviousPosition = cameraActualPosition;
///--Cam Rotate--///
if(plLR >= padDeadZone.x || plLR <= padDeadZone.y)
{
//Decentralize Quaternion Rotation Left/Right
cameraRoot.transform.position = QRotateAround(playerHead.transform.position,cameraRoot.transform.position,plLR,rotSpeed,Vector3.up);
}
if(plUD >= padDeadZone.x || plUD <= padDeadZone.y)
{
//Decentralize Quaternion Rotation Up/Down
cameraRoot.transform.position = QRotateAround(playerHead.transform.position,cameraRoot.transform.position,-plUD,rotSpeed,cameraRoot.transform.right);
}
///--Cam collision/occlusion--///
//DebugSphere
debugSphere.transform.position = cameraRoot.transform.position;
//DebugRay
Debug.DrawRay(playerHead.transform.position, -cameraRoot.transform.forward * preOccludedDistance, Color.white);
//ApplyNewPosition;
cameraRoot.transform.position = CalculateDesiredPosition();
///--Cam LookAt--///
Vector3 direction = playerHead.transform.position;
Quaternion targetRotation = Quaternion.LookRotation(direction - cameraRoot.transform.position);
float i = Time.deltaTime * 100.0f;
cameraRoot.transform.rotation = Quaternion.Slerp(cameraRoot.transform.rotation, targetRotation, i);
}
Vector3 QRotateAround(Vector3 centerRotation, Vector3 vector, float padAxis, float rotSpeed, Vector3 rotDirection)
{
Quaternion rotation = Quaternion.AngleAxis (padAxis * rotSpeed, rotDirection);
Vector3 vector2 = (vector - centerRotation);
vector2 = rotation * vector2;
vector = centerRotation + vector2;
return vector;
}
float CheckCameraPoints (Vector3 startPos, Vector3 endPos)
{
RaycastHit hitInfo;
float distance = Vector3.Distance(startPos, endPos);
float nearDistance = -1.0f;
if(Physics.SphereCast(startPos, sphereRadius/2.0f, -cameraRoot.transform.forward, out hitInfo, distance) hitInfo.collider.tag != "Player")
{
nearDistance = hitInfo.distance;
Debug.DrawRay(startPos, -cameraRoot.transform.forward * nearDistance, Color.blue);
}
return nearDistance;
}
bool CheckPreOccludedState(Vector3 startPos, Vector3 endPos, float distance)
{
RaycastHit hitInfo;
float currentdistance = Vector3.Distance(startPos, endPos);
bool preOccludedPosFree = true;
if(Physics.SphereCast(startPos, sphereRadius/2.0f, -cameraRoot.transform.forward, out hitInfo, distance) hitInfo.collider.tag != "Player")
{
if(currentdistance < distance)
preOccludedPosFree = false;
}
return preOccludedPosFree;
}
Vector3 CalculateDesiredPosition()
{
bool preOccludedState = CheckPreOccludedState(playerHead.transform.position, cameraRoot.transform.position, preOccludedDistance);
float currentDistance = CheckCameraPoints(playerHead.transform.position, cameraRoot.transform.position);
Vector3 newPosition = cameraRoot.transform.position;
//Move camera backward
if(currentDistance == -1.0f preOccludedState == true)
{
if(count > 0 count <= maxOcclusionChecks)
{
newPosition -= cameraRoot.transform.forward * occlusionDistanceStep;
count--;
}
}
//Move camera forward
if(currentDistance != -1.0f)
{
if(count >= 0 count < maxOcclusionChecks)
{
newPosition += cameraRoot.transform.forward * occlusionDistanceStep;
count++;
}
}
return newPosition;
}
}