为什么全面测试会忽略冲突(请阅读编辑内容)?

时间:2019-12-20 05:23:09

标签: c# unity3d collision-detection

我有一个根据字符箭头移动的对象,例如字符控制器。清除测试每移动一次Update()都会引发一次,但是如果您持续按下该按钮,它将通过。顺便说一句,我没有使用Unity物理学,所以任何暗示使用OnCollision()空隙的解决方案都不起作用

void Update()
{
    float horizontal = Input.GetAxisRaw("Horizontal") * moveSpeed; 
    velocity.x = horizontal;//y axis adds gravity, but is omitted here to focus on the relevant

    Vector3 movement = velocity * Time.deltaTime;

    if (movement != Vector3.zero)
    {
        RaycastHit hit;
        if (rb.SweepTest(movement, out hit, movement.magnitude))
        {
            if (hit.collider)
            {
                Vector3 moveToColl = movement / movement.magnitude * hit.distance;//gives hit.distance vector                 
                transform.position += moveToColl;

                movement = Vector3.zero;//because movement has already been done.
                velocity = movement;
            }
        }
    }

    this.transform.Translate(movement, Space.World);
}

由于我以为问题是游戏对象与对撞机重叠,因此我尝试在进行扫掠测试之前将游戏对象移到后面(由于扫掠测试,由于某种原因,不允许使用图层蒙版),然后增加其长度以保持相同的碰撞。

Vector3 behindVector = movement / 100;

transform.position -= behindVector;
float maxDistance = movement.magnitude;
maxDistance += maxDistance / 100;// same as movement.magnitude + behindVector.magnitude

//相同,但是maxDistance取代了扫描测试长度中的motion.magnitude。 虽然这可以大大减少问题,但仍有一些物体可以穿过墙壁的框架,特别是在距离很近或方向改变并跳跃后(很抱歉:不确定)。我试图找出是什么原因。它,但我并没有真正了解它。如果您靠近墙壁并尝试通过,然后朝相反方向移动并再次尝试,尤其是在对speed.y也进行了修改的情况下,这种情况似乎更常见我认为这与向量的大小有关。 我尝试过的另一个解决方案是:

if (hit.collider)
{
   Vector3 moveToColl = movement / movement.magnitude * hit.distance;//gives hit.distance vector
   moveToColl *= 0.9f;
   float minimum = 0.001f;
   if (moveToColl.magnitude > minimum)
   {
       transform.position += moveToColl;
   }
   movement = Vector3.zero;//these two are kept outside
   velocity = movement;
}

因此,将对象移动到小于其预期的位置将防止重叠,但是如果0.9f较大(如0.999f),则碰撞基本上将再次为零。如果我们改用0.6f,它几乎可以正常工作,但这有两个问题。首先,我什至不能完全确定我是否解决了这个问题,事实似乎如此。其次,这种运动是错误的,并且如果该浮标较小(这又可以改善碰撞),那么两个对象之间的间隙就更明显,即使不是那么大。 我什至一次尝试了这两种方法,但是它使对象产生了怪异的振动,因为这两行修改了其位置:transform.position -= behindVector; //和transform.position += moveToColl;

编辑:由于问题是由float的精度不足和对撞机重叠引起的,所以我想到了另一种解决方案,但最后一部分需要帮助。 这是关于从moveToColl向量中减去Vector3偏移量。首先,我们假设X轴上为0.1f(方向必须与moveToColl.x相反,因此,如果该方向为负,则为正),并使用以下公式获得Y值:0.1f * moveToColl.y / moveToColl.x。因此,我们以Vector3结尾(例如0.1f,Y值,weIgnoreZAxisInThisExample)。我们从moveToColl向量中减去这个向量,我们得到一个新的向量,它保持相同的方向,但是不会在X轴上移动那些额外的0.1f,而在Y轴上保持该方向不变。如果我们要另一个轴,则公式相同,但是我们将所有轴取反。第一个实例与墙壁配合良好。因为无论y速度如何都减去0.1f,所以它永远不会经历(假设该值足够)。横扫测试在跳跃时更频繁地错过碰撞的原因是,y的变化会减小x偏移(因为我们将向量乘以0.9f,所以0.1f被减去了x方向,而不是x轴。现在是不变)。但这仅适用于完美的垂直或水平线。如果是斜率,则偏移是错误的。我发现偏移量必须垂直于曲面,因为最接近的距离始终是垂直的。由于x轴垂直,因此靠墙工作良好,就像直接减去偏移量并保持方向一样简单。但是现在不是轴对齐的。 enter image description here

对不起,偏移量是-0.1、0.1。我忘了放-,它朝相反的方向。例如,该公式将给出紫色矢量以获取偏移量(我知道我确实绘制了x和y偏移量,因为我确实想要这些)。正确的向量将从点击开始。绿线和黑线相交的点和终点(这就是要寻找的东西)。绿色偏移量内还有一个Orage点,这是错误的,因为它与蓝线之间的距离明显小于应有的距离。因此,如果公式给出了这一点,那就不好了(尽管不能完全确定是否可行,所以请告诉我您是否知道)。无论如何,距离越大,越容易看到不碰撞。这就是为什么我想要最接近的距离。顺便说一下,0.1f只是一个例子。有谁知道您可以安全地检测到碰撞的最近距离是什么?

2 个答案:

答案 0 :(得分:0)

即使这可能不是解决OP问题的确切答案,但对于那些在这里到达谷歌搜索的人来说可能很有用。

这是很难调试的问题。但这可能是由于某些情况下SweepTest的已知问题引起的。

当扫描测试在一个已经“碰撞”的对象上开始时...意味着它从碰撞器内部已经存在的对象开始,即使它只是很小的一部分,扫描测试也会失败。

结合“ float”类型的精度问题,您可以在游戏中找到完美的错误。

以下讨论可能有助于理解问题:SweepTest issue

可以通过使用ComputePenetrationClosestPoint或其组合来解决或缓解该问题。

可以通过更改刚体上的CollisionDetectionMode来解决其他碰撞问题。

另一个想法(不确定是否有帮助)试图在碰撞对象中设置EdgeRadius ???值得一试。

答案 1 :(得分:0)

实际上,我向后移动对象的第一个解决方案很好,但是像这样更好:

void Update()
{
    if (movement != Vector3.zero)
    {
        float maxDistance = movement.magnitude;
        Vector3 behindVector = movement * (0.003f / maxDistance);
        transform.position -= behindVector;
        maxDistance += 0.003f;//movement.magnitude + behindVector.magnitude
        //then do the sweepTest
    }
}

但是,现在我看到在某些情况下我需要增加0.003f的偏移量。为了防止与播放器后面的物体发生碰撞,您需要检查hit.distance是否> offset并且忽略其余的碰撞。