低帧速率下的慢速碰撞检测

时间:2016-06-12 12:22:12

标签: c# unity3d collision-detection

我的碰撞检测遇到了一个奇怪的问题。我使用Update方法移动播放器(我不想使用FixedUpdate,因为这会产生不希望的怪异动作)。固定的时间步长设置为默认值0.02(我尝试使用时间设置进行播放但是也没有工作)。我将两个物体的刚体的碰撞检测设置为"连续动态"。此外,我将目标帧速率设置为300,并且没有改变任何内容......

当帧速率较低或设备本身较慢时,碰撞检测并不总是有效。玩家可以很容易地穿过它应该与之碰撞的物体,尽管有时它并没有。

请告诉我我可以做些什么来解决这个问题,因为我发布了一款游戏,很多用户都报告了这个(严重的)错误。感谢您的支持。

这是应该发生的事情:

enter image description here

这是实际发生的事情:

enter image description here

(正如你所看到的那样,立方体离开墙壁到另一边)

当用户释放鼠标按钮时我移动播放器:

脚本1:

public Script2 Jumper;
public float TimeToJump;

public void Update()
{
        if (Input.GetMouseButtonUp(0)) 
    {
            StartCoroutine (Delay (1f/50f)); //Don't mind the time.
    }
}

IEnumerator Delay(float waitTime) 
{
    yield return new WaitForSeconds (waitTime);
    if (Jumper != null) 
    {
        Jumper.SetVelocityToJump (gameObject, TimeToJump);
    }
}

脚本2附加到播放器(立方体):

public class Script2 : MonoBehaviour {

    GameObject target;
    private float timeToJump;
    public bool isJumping = false;

    public void SetVelocityToJump(GameObject goToJumpTo, float timeToJump)
    {
        StartCoroutine(jumpAndFollow(goToJumpTo, timeToJump));
        this.timeToJump = timeToJump;
        this.target = goToJumpTo;
    }

    private IEnumerator jumpAndFollow(GameObject goToJumpTo, float timeToJump)
    {
        var startPosition = transform.position;
        var targetTransform = goToJumpTo.transform;
        var lastTargetPosition = targetTransform.position;
        var initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);

        var progress = 0f;
        while (progress < timeToJump)
        {
            progress += Time.deltaTime;
            if (targetTransform.position != lastTargetPosition)
            {
                lastTargetPosition = targetTransform.position;
                initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
            }

            float percentage = progress * 100 / timeToJump;  
            GetComponent<Rigidbody>().isKinematic = percentage < 100.0f;  

            transform.position = startPosition + (progress * initialVelocity) + (0.5f * Mathf.Pow(progress, 2) * _gravity);
            yield return null;
        }

        OnFinishJump (goToJumpTo, timeToJump);
    }


    private void OnFinishJump(GameObject target, float timeToJump)
    {
        if (stillJumping)
        {
            this.isJumping = false;
        }
    }

    private Vector3 getInitialVelocity(Vector3 toTarget, float timeToJump)
    {
        return (toTarget - (0.5f * Mathf.Pow(timeToJump, 2) * _gravity)) / timeToJump;
    }
}

立方体的目标是较大立方体(墙壁)的孩子。

如果您需要澄清,请在下面留言。如果您需要更多细节,我可以给我的游戏链接。

引自here(感谢@Logman):&#34;即使您使用连续动态碰撞检测,问题仍然存在,因为快速移动的物体移动速度太快,以至于它们距离自身太远一帧到下一个直接帧。就像它们被传送一样,并且不会触发任何碰撞检测,因为从每个帧的角度来看都不存在碰撞,因此也就是所有处理过的计算都没有。&#34;

在我的情况下,立方体不会快速,但你得到了这个概念。

2 个答案:

答案 0 :(得分:7)

您的代码存在一些问题。

  1. 你要求Coroutine屈服1/50秒。产量必须达到的最小时间是一帧。如果Time.deltaTime > 0.02f这已成为问题之一。
  2. 您正在使用Coroutines和yield return null来计算物理计算。从本质上讲,你是在Update()中计算物理,每帧只调用一次null相当于new WaitForEndOfFrame():如(1)中所述,一个正在运行的Coroutine不能在帧之间屈服)。在低帧速率下,对象在两帧之间进行的运动量可能超过目标触发器的碰撞范围。假设线性,非加速运动:∆S = v∆t其中v =速度,ΔS是在当前帧中覆盖的运动,Δt是Time.deltaTime。如您所见,ΔS与Δt成比例。

  3. 你在循环中有GetComponent<T>()个调用。始终避免这样做:将引用存储为成员变量(在Start()中初始化)。

  4. 我对最快的工作黑客的建议是不要过于担心“干净”,而是创建从FixedUpdate()调用的子例程,并(创建和)使用成员bool有条件地测试哪个子程序“执行”以及哪个“跳过”。您还可以使用成员boolenum作为触发器在各种“状态”之间切换。

    更好的解决方案是让Unity处理运动学而你使用rigidbody mutators(而不是transform.position s),但对于街机情况来说这可能是完全没必要的,你可能会这样做是。在那种情况下坚持上面的黑客。

    如果确实希望手动控制运动,请使用SFML之类的引擎。粒子系统教程将是一个很好的起点。

答案 1 :(得分:4)

这是你的浮动百分比等等。

“如果isKinematic已启用,则力,碰撞或关节将不再影响刚体。”

这是来自Unity文档的isKinematic页面。当进度达到100时,你将其设置为true。因此,在较低的帧速率下,由于Time.deltaTime步骤高得多,突然进展&gt; = 100,isKinematic设置为true并且玩家不再受到碰撞的影响。

我认为你将不得不在这里重新考虑很多代码并做一些重要的优化。但是其他海报已经把它们放好了,所以我不需要。

编辑:误解了最初的问题,认为这意味着你试图检测碰撞,但你的代码并不总是检测到它们。没有意识到它实际上意味着首先发生碰撞。