【问题标题】:Slow collision detection at low frame rates低帧速率下的慢速碰撞检测
【发布时间】:2016-06-12 12:22:12
【问题描述】:

我的碰撞检测遇到了一个奇怪的问题。我正在使用 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);
    }
}

脚本 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):“即使您使用连续动态碰撞检测,问题仍然存在,因为快速移动的对象可以移动得如此之快,以至于它们从一帧到下一帧与自身相距太远立即帧。就好像它们被传送并且不会触发任何碰撞检测,因为从每个帧的角度来看不存在碰撞,因此从所有处理的计算来看。”

在我的例子中,立方体的速度并不快,但你明白了这个概念。

【问题讨论】:

  • 使用 FixedUpdate() 并修复实际的错误(“不受欢迎的奇怪运动”)。如果问题仍然存在,请在慢速设备上放大碰撞器或设置最低硬件要求。或者如果可以的话优化代码。哦!现在使用固定时间戳确实有效,因为(不足为奇)这与 FixedUpdate() 处理有关
  • 您的大纲“延迟”是错误的。只需在 Unity 中使用 Invoke 作为计时器。
  • 您是否有理由不使用内置的碰撞事件?如果只是不受欢迎的奇怪动作,那么我同意其他海报应该解决它们。之后,您可以简单地使用 FixedUpdate()、连续动态碰撞检测以及 MonoBehavior 对象模型中内置的标准碰撞事件来实现您想要的。另外一个好处是,如果您正确设置场景,您可能可以通过选择更简单的碰撞检测模型来节省一些资源。您最好的选择可能是针对您的“不受欢迎的奇怪动作”提出不同的问题。
  • 您无法使用统一物理引擎解决此问题。看看这个answers.unity3d.com/questions/55179/…
  • 是的,但这种情况非常罕见。我真正的意思是“按照设计的实现必要吗?”换句话说,我同意对于非常快速移动的物体,物理速度不够快......但最初的问题并没有说明存在是快速移动的物体。事实上,最初的问题表明它的 player(通常)不会移动得那么快。

标签: c# unity3d collision-detection


【解决方案1】:

您的代码存在几个问题。

  1. 您要求协程让步 1/50 秒。必须发生屈服的最短时间是一帧。如果Time.deltaTime &gt; 0.02f 这已经是问题之一了。
  2. 您正在使用协程和yield return null 来计算物理计算。本质上,您是在 Update() 中计算物理,每帧只调用 一次null 等效于 new WaitForEndOfFrame():如(1)中所述,正在运行的协程不能产生帧之间)。在低帧率下,物体在两帧之间进行的运动量可能会超出目标触发器的碰撞范围。假设线性、非加速运动:∆S = v∆t 其中 v = 速度,ΔS 是当前帧中要覆盖的运动,Δt 是Time.deltaTime。如您所见,ΔS 与 Δt 成正比。

  3. 你在循环中有GetComponent&lt;T&gt;() 调用。始终避免这样做:将引用存储为成员变量(在 Start() 中对其进行初始化)。

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

更好的解决方案是让 Unity 处理运动学,而您可以使用 rigidbody 突变体(而不是 transform.positions),但这对于街机情况可能完全没有必要,而您可能就是这样。在这种情况下,请坚持上面的技巧。

如果您真的想手动控制运动学,请使用 SFML 之类的引擎。粒子系统教程是一个很好的起点。

【讨论】:

    【解决方案2】:

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

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

    来自 Unity 文档的 isKinematic 页面。当进度达到 100 时,您将其设置为 true。因此,在较低的帧速率下,由于 Time.deltaTime 步数要高得多,会突然跳跃,进度突然 >= 100,isKinematic 设置为 true 并且播放器不再受碰撞影响。

    我认为您将不得不重新考虑这里的大量代码并进行一些重大优化。不过其他的海报已经摆好了,我就不需要了。

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

    【讨论】:

      猜你喜欢
      • 2020-03-26
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多