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

Din*_*ovr 8 c# collision-detection unity-game-engine

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

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

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

这是应该发生的事情:

在此输入图像描述

这是实际发生的事情:

在此输入图像描述

(正如你所看到的,立方体从墙上走到另一边)

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

脚本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);
    }
}
Run Code Online (Sandbox Code Playgroud)

脚本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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

这里引用(感谢@Logman):"即使你使用连续动态碰撞检测也存在问题,因为快速移动的物体移动速度太快,以至于它们从一帧到下一帧的距离太远.就像它一样.它们被传送,并且不会触发任何碰撞检测,因为从每个帧的角度来看都不存在碰撞,因此也不会从所有处理过的计算中发生碰撞.

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

kar*_*age 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()).

我对最快的工作黑客的建议是不要太担心"干净",而是创建你调用的子程序FixedUpdate(),并且(创建和)使用成员bools来有条件地测试哪个子程序"执行"以及哪个"跳跃".您还可以使用成员bools或enums作为触发器在各种"状态"之间切换.

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

如果您真的想要手动控制运动,请使用像SFML这样的引擎.粒子系统教程将是一个很好的起点.