基于体素场景的摄像机穿模处理

基于上一篇一种基于体素的射线检测
使用射线处理第三人称摄像头穿模问题

基于体素的第三人称摄像机拉近简单处理

摄像机移动至碰撞点处

简单的从角色身上发射一条射线到摄像机,中途遇到碰撞就把摄像机移动至该碰撞点

    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 minSpeed = 5;
    public float maxSpeed = 50;
    Transform cameraRoot;
    Transform player;
    float distance;
    float curDistance;
    RaycastHit hitInfo;

    Camera mainCamera;
    Transform virtualCamera;
    Vector3[] corners = new Vector3[4];
    float width;
    float height;
    float speed ;
    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;
        speed = maxSpeed;
    }

    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;
        }

        if(distance > curDistance)
            speed = maxSpeed;
        else
            speed = minSpeed;

        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;

    }
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼游戏开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值