基于上一篇一种基于体素的射线检测
使用射线处理第三人称摄像头穿模问题
基于体素的第三人称摄像机拉近简单处理
摄像机移动至碰撞点处
简单的从角色身上发射一条射线到摄像机,中途遇到碰撞就把摄像机移动至该碰撞点
public void UpdateDistance(float defaultDistance)
{
Vector3 from = player.position;
Vector3 to = cameraRoot.position;
Vector3 forward = (to - from).normalized;
Debug.DrawLine(from, to, Color.red);
if (BlockPhysics.Raycast(BlockWorld.CurWorld, from, forward, (to - from).magnitude, out hitInfo))
{
distance = cameraRoot.InverseTransformPoint(hitInfo.point).z;
}
else
{
distance = 0;
}
curDistance = Mathf.Lerp(curDistance, distance, speed * Time.fixedDeltaTime);
curDistance = Mathf.Clamp(curDistance, 0, defaultDistance);
transform.localPosition = Vector3.forward * curDistance;
}
可以明显看到摄像机一半在外面,一半在墙里面体验感非常差
可以通过检测摄像机近裁剪面的4个顶点是否在方块或体素内
如果有一个顶点产生碰撞,那么就把摄像机向前移动
float GetDistance(float distance)
{
int i = 0;
int loop = 666;
virtualCamera.localPosition = Vector3.forward * distance;
var wrold = BlockWorld.CurWorld;
while (loop-- > 0)
{
UpdateNearClipPlane();
for (i = 0; i < 4; i++)
{
if (wrold.HasBlockCollider(Vector3Int.RoundToInt(corners[i])) || wrold.HasVoxelCollider(corners[i]))
{
break;
}
}
if (i == 4)
break;
distance += 0.25f;
virtualCamera.localPosition = Vector3.forward * distance;
}
return distance;
}
通过前移规避穿模问题
当然如果夹角非常小或者在一个狭窄的通道内,并不推荐拉近摄像头。
因为拉近已经不能解决问题。这种情况下推荐摄像机观察中心直接固定在方块中心。
从方块中心出发就不用担心角色过于靠近墙壁导致的拉近修复无效
完整代码
using UnityEngine;
public class CameraOffset : MonoBehaviour
{
public float speed = 10;
Transform cameraRoot;
Transform player;
float distance;
float curDistance;
RaycastHit hitInfo;
Camera mainCamera;
Transform virtualCamera;
Vector3[] corners = new Vector3[4];
float width;
float height;
private void Start()
{
cameraRoot = transform.parent;
player = GameObject.FindGameObjectWithTag("Player").transform;
player = player.transform.Find("cameraFollow");
mainCamera = Camera.main;
virtualCamera = new GameObject("virtualCamera").transform;
virtualCamera.transform.SetParent(mainCamera.transform.parent);
virtualCamera.localPosition = Vector3.zero;
virtualCamera.localRotation = Quaternion.identity;
virtualCamera.localScale = Vector3.zero;
float halfFOV = (mainCamera.fieldOfView * 0.5f) * Mathf.Deg2Rad;
float aspect = mainCamera.aspect;
height = mainCamera.nearClipPlane * Mathf.Tan(halfFOV);
width = height * aspect;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(hitInfo.point, 0.1f);
}
public void UpdateDistance(float defaultDistance)
{
Vector3 from = player.position;
Vector3 to = cameraRoot.position;
Vector3 forward = (to - from).normalized;
Debug.DrawLine(from, to, Color.red);
if (BlockPhysics.Raycast(BlockWorld.CurWorld, from, forward, (to - from).magnitude, out hitInfo))
{
distance = GetDistance(cameraRoot.InverseTransformPoint(hitInfo.point).z);
}
else
{
distance = 0;
}
curDistance = Mathf.Lerp(curDistance, distance, speed * Time.fixedDeltaTime);
curDistance = Mathf.Clamp(curDistance, 0, defaultDistance);
transform.localPosition = Vector3.forward * curDistance;
}
float GetDistance(float distance)
{
int i = 0;
int loop = 666;
virtualCamera.localPosition = Vector3.forward * distance;
var wrold = BlockWorld.CurWorld;
while (loop-- > 0)
{
UpdateNearClipPlane();
for (i = 0; i < 4; i++)
{
if (wrold.HasBlockCollider(Vector3Int.RoundToInt(corners[i])) || wrold.HasVoxelCollider(corners[i]))
{
break;
}
}
if (i == 4)
break;
distance += 0.25f;
virtualCamera.localPosition = Vector3.forward * distance;
}
return distance;
}
void UpdateNearClipPlane()
{
corners[0] = virtualCamera.position - (virtualCamera.right * width);
corners[0] += virtualCamera.up * height;
corners[0] += virtualCamera.forward * mainCamera.nearClipPlane;
corners[1] = virtualCamera.position + (virtualCamera.right * width);
corners[1] += virtualCamera.up * height;
corners[1] += virtualCamera.forward * mainCamera.nearClipPlane;
corners[2] = virtualCamera.position - (virtualCamera.right * width);
corners[2] -= virtualCamera.up * height;
corners[2] += virtualCamera.forward * mainCamera.nearClipPlane;
corners[3] = virtualCamera.position + (virtualCamera.right * width);
corners[3] -= virtualCamera.up * height;
corners[3] += virtualCamera.forward * mainCamera.nearClipPlane;
}
}