Unity3D 自制摄像机不穿墙/不穿物体(射线cast方法)

墙和物体都要加上collider(碰撞体),一般注意以下几点:

  • 墙加mesh collider,在Convex上打勾
  • 物体加box collider
  • 加刚体(rigidbody)的物体不要选Is Kenimatic

基础 最简单的Raycast方法

void Update()
{

    RaycastHit hitInfo;
    Vector3 fwd = Camera.main.transform.TransformDirection(Vector3.forward);
    if (Physics.Raycast(Camera.main.transform.position, fwd, out hitInfo, 0.5f))
    {
        float dis = hitInfo.distance;
        Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(Vector3.back)) * dis;
        Camera.main.transform.position += correction;
    }
}

 上面的代码,在撞墙时会有抖动的效果,如果要平滑后退,可以把

Vector3.Normalize(Camera.main.transform.TransformDirection(Vector3.back)) * dis;

改为:

Vector3.Normalize(Camera.main.transform.TransformDirection(Vector3.back)) * dis * Time.deltaTime;

但是,这样可能会出现“移动物体过快导致穿墙”的问题。

关于 TransformDirection 参考资料: Unity TransformPoint、InverseTransformPoint、TransformDirection 

Vector3 worldPosition = transform.TransformPoint ( Vector3 relativePosition ) 
        将相对 “当前游戏对象” 的坐标转化为基于世界坐标系的坐标
Vector3 relativePosition = transform.InverseTransformPoint ( Vector3 worldPosition ) 
        将世界坐标转化为相对 “当前游戏对象” 的基于世界坐标系的坐标
Vector3 worldDirection = Transform.TransformDirection ( Vector3 relativeDirection ) 
        将相对 “当前游戏对象” 的方向转化为基于世界坐标系的方向(不受父对象缩放的影响)

进阶1 四个方向Raycast

在上一种方法中,显然,只有一个方向(前方)能不穿墙,没有考虑如下情况:

  • 摄像机倒着走,从背后穿墙
  • 摄像机横着向左/右走,从左/右穿墙

这里用了一个比较蠢的方法,就是往前、后、左、右四个方向投射线,作四次检测。把这个检测过程封装成函数detect()。

Vector3.forward实际是一个固定值,unity 内置的vector3还有以下一些方向:

参考资料 Unity - Scripting API: Vector3

backShorthand for writing Vector3(0, 0, -1).
downShorthand for writing Vector3(0, -1, 0).
forwardShorthand for writing Vector3(0, 0, 1).
leftShorthand for writing Vector3(-1, 0, 0).
negativeInfinityShorthand for writing Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity).
oneShorthand for writing Vector3(1, 1, 1).
positiveInfinityShorthand for writing Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity).
rightShorthand for writing Vector3(1, 0, 0).
upShorthand for writing Vector3(0, 1, 0).
zeroShorthand for writing Vector3(0, 0, 0).
void detect(Vector3 dir, Vector3 inverse_dir)
{
    RaycastHit hitInfo;
    Vector3 _dir = Camera.main.transform.TransformDirection(dir);
    if (Physics.Raycast(Camera.main.transform.position, _dir, out hitInfo, 0.5f))
    {
        float dis = hitInfo.distance;
        Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(inverse_dir)) * dis;
        Camera.main.transform.position += correction;
    }
}

// Update is called once per frame
void Update()
{
    detect(Vector3.forward, Vector3.back);
    detect(Vector3.back, Vector3.forward);
    detect(Vector3.left, Vector3.right);
    detect(Vector3.right, Vector3.left);
}

进阶2 SphereCast

这里,射线只有一个像素点,尽管已经覆盖四个方向,但是可能还是会有疏漏的情况。可以用Physics.SphereCast()方法。

可参考下列指南:http://www.ceeger.com/Script/Physics/Physics.SphereCast.html

void detect(Vector3 dir, Vector3 inverse_dir)
{
    RaycastHit hitInfo;
    Vector3 _dir = Camera.main.transform.TransformDirection(dir);
    if (Physics.SphereCast(Camera.main.transform.position, 0.5f, _dir, out hitInfo, 0.5f))
    {
        float dis = hitInfo.distance;
        Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(inverse_dir)) * dis;
        Camera.main.transform.position += correction;
    }
}

// Update is called once per frame
void Update()
{
    detect(Vector3.forward, Vector3.back);
    detect(Vector3.back, Vector3.forward);
    detect(Vector3.left, Vector3.right);
    detect(Vector3.right, Vector3.left);
}

进阶3 层遮罩LayerMask

某些碰撞体(collider) 可能只被用作trigger,但是却受到了碰撞检测。

一个比较简单的方法:

把物体放置到 Ignore Raycast层

或者用LayerMask,以下为示例:

void detect(Vector3 dir, Vector3 inverse_dir)
{
    RaycastHit hitInfo;
    Vector3 _dir = Camera.main.transform.TransformDirection(dir);
    LayerMask _ignoreLayer = 1 << LayerMask.NameToLayer("mylayer");
    if (Physics.Raycast(Camera.main.transform.position, _dir, out hitInfo, 0.5f, _ignoreLayer))
    {
        float dis = hitInfo.distance;
        Vector3 correction = Vector3.Normalize(Camera.main.transform.TransformDirection(inverse_dir)) * dis;
        Camera.main.transform.position += correction;
    }
}

更详细资料可参考:U3D开发学习之路--RayCast中layerMask的使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

iteapoy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值