【问题标题】:How to prevent colliders from passing through each other?如何防止对撞机相互穿过?
【发布时间】:2012-03-13 16:39:52
【问题描述】:

我无法将游戏对象保存在封闭空间内。当他们到达边缘时,会有一些短暂的后退,但他们会直接穿过墙壁。

我在播放器上使用了 Box Collider,在关卡的墙壁上使用了 Mesh Collider。我对移动由玩家控制的玩家角色(太空船)都有问题。还有射弹,它们是火的,不会以恒定的速度移动。

这是我的播放器的移动代码。它正在FixedUpdate() 函数中运行。

//Movement
    haxis = Input.GetAxis("Horizontal") * speed;
    vaxis = Input.GetAxis("Vertical") * speed;

    moveVector.x = haxis;
    moveVector.z = vaxis;

    if(moveVector.magnitude > 1)
    {
        moveVector.Normalize();
    }

    rigidbody.MovePosition(transform.position + moveVector * speed);

使用子弹,它们被赋予一个速度,引擎计算它们的动画。他们正在使用 Box Collider,它被设置为触发器,所以他们没有物理。但我使用OnTriggerEnter 来销毁它们。

//Projectiles without physics collisiions
function OnTriggerEnter (other : Collider) {
    Destroy(gameObject);
}

一些但不是所有的子弹在击中网格对撞机墙壁时会被摧毁。玩家有时会撞到它并停下来,但通常可以通过它。如何使与网格碰撞器的碰撞每次都能正常工作?

【问题讨论】:

  • 我什至用墙的网格创建了一个更简单的网格对撞机,但没有帮助。
  • 您在屏幕上使用的网格碰撞器有多宽,子弹和玩家移动的速度有多快?如果子弹或玩家在一帧中可以移动比对撞机更长的距离,那么你就有了。另外,我会避免手动移动刚体。这只会混淆物理引擎并阻止其优化。而是移动游戏对象的变换。
  • @Elideb 对撞机大约有 8 个字符和 20 个子弹宽。它们并没有在单个帧中完全穿过对撞机。如果你移动变换 Unity 将忽略所有物理,rigidbody.MovePosition 会考虑物理。
  • 你说得对,我的关系正好相反。计算速度时是否考虑了 deltaTime?你的角色会反复撞墙直到它通过吗?你的角色在 OnCollision 上做了什么?
  • @Elideb MovePosition() 调用是作为 FixedUpdate() 的结果调用的,因此不需要 deltaTime。我无法找到碰撞工作/失败的真实行为模式。

标签: unity3d game-physics


【解决方案1】:

与快速移动的物体发生碰撞始终是个问题。确保检测到所有碰撞的一个好方法是使用 Raycasting 而不是依赖物理模拟。这对子弹或小物体效果很好,但对大物体不会产生好的效果。 http://unity3d.com/support/documentation/ScriptReference/Physics.Raycast.html

伪代码(我这里没有代码完成,记性不好):

void FixedUpdate()
{
    Vector3 direction = new Vector3(transform.position - lastPosition);
    Ray ray = new Ray(lastPosition, direction);
    RaycastHit hit;
    if (Physics.Raycast(ray, hit, direction.magnitude))
    {
        // Do something if hit
    }

    this.lastPosition = transform.position;
}

【讨论】:

  • 提交了一个编辑,但它应该是 Ray(lastPosition, direction) 否则你可能在网格碰撞器内部并且光线不会从碰撞器内部击中。看起来不错的方法,我去看看。
  • 我能想到的唯一问题是我们可能会看到很多移动元素,我怀疑对所有这些元素进行 Raycast 会比使用复合碰撞器更重。如果我有机会对此进行测试,我会告诉你性能差异是什么。
  • collider.Raycast 检查单个对撞机 - 我编辑到 Physics.Raycast。关于性能,我认为它不会有很大的影响,但它需要测试。您可以使用 Physics.Raycast 的 layerMask 参数来确保它只针对您想要的内容进行测试(请参阅文档)。
  • Raycasting 是要走的路,因为很多理论内容我无法在评论中添加,但这就是答案。
  • 你先生,是我的新神!谢谢!
【解决方案2】:

我有一个弹球原型,在相同的领域也给我带来了很多麻烦。这些是我几乎(但尚未完全)解决这些问题所采取的所有步骤:

对于快速移动的物体:

  • 将刚体的 Interpolate 设置为“Interpolate”(这不会影响实际的物理模拟,但会正确更新对象的渲染 - 从渲染的角度来看,仅在重要的对象上使用它,例如玩家,或弹球,但不适用于弹丸)

  • 将碰撞检测设置为连续动态

  • 将脚本 DontGoThroughThings (https://www.auto.tuwien.ac.at/wordpress/?p=260) 附加到您的对象。该脚本巧妙地使用了我在其他答案中发布的光线投射解决方案,将违规对象拉回碰撞点之前。

Edit -> Project Settings -> Physics:

  • 将罚球的最小穿透设置为非常低的值。我已将我的设置为 0.001

  • 将求解器迭代计数设置为更高的值。我已经把我的设置为 50,但你可能用更少的钱就可以了。

所有这些都会在性能上受到惩罚,但这是不可避免的。默认值对性能的影响不大,但并不是真正用于正确模拟小型和快速移动的对象。

【讨论】:

  • 您好,我找不到 DontGoThroughtThings 脚本,您能提供吗?可能是用户已从上述链接中删除。
  • 我找到了 DontGoThroughtThings Scripte 的链接 code.google.com/p/lava-in-antarctica/source/browse/trunk/…
  • 我正在使用 Unity 5,在阅读完这篇文章后,虽然我找不到“惩罚的最小穿透”,但我尝试将“默认接触偏移”更改为 1。现在,正在检测快速移动的物体:)
  • Collision Detection: Dynamic 为我工作。使用 Unity 2017。
【解决方案3】:

如何将刚体的碰撞检测设置为ContinuousContinuous Dynamic

http://unity3d.com/support/documentation/Components/class-Rigidbody.html

【讨论】:

  • 我很确定如果网格对撞机的数量超过一定数量,则无法将其设置为连续。即便如此,在角色/子弹甚至网格上设置连续/连续动态也不起作用。
【解决方案4】:

所以我无法让 Mesh Colliders 工作。我使用简单的盒子碰撞器创建了一个复合碰撞器,它的工作方式完全符合预期。

其他使用简单网格碰撞器的测试也得出了相同的结果。

看起来最好的答案是用简单的盒子/球体碰撞器构建一个复合碰撞器。

对于我的具体情况,我编写了一个创建管形复合碰撞器的向导。

@script AddComponentMenu("Colliders/Pipe Collider");
class WizardCreatePipeCollider extends ScriptableWizard
{
    public var outterRadius : float = 200;
    public var innerRadius : float = 190;
    public var sections : int = 12;
    public var height : float = 20;

    @MenuItem("GameObject/Colliders/Create Pipe Collider")
    static function CreateWizard()
    {
        ScriptableWizard.DisplayWizard.<WizardCreatePipeCollider>("Create Pipe Collider");
    }

    public function OnWizardUpdate() {
        helpString = "Creates a Pipe Collider";
    }

    public function OnWizardCreate() {
        var theta : float = 360f / sections;
        var width : float = outterRadius - innerRadius;

        var sectionLength : float = 2 * outterRadius * Mathf.Sin((theta / 2) * Mathf.Deg2Rad);

        var container : GameObject = new GameObject("Pipe Collider");
        var section : GameObject;
        var sectionCollider : GameObject;
        var boxCollider : BoxCollider;

        for(var i = 0; i < sections; i++)
        {
            section = new GameObject("Section " + (i + 1));

            sectionCollider = new GameObject("SectionCollider " + (i + 1));
            section.transform.parent = container.transform;
            sectionCollider.transform.parent = section.transform;

            section.transform.localPosition = Vector3.zero;
            section.transform.localRotation.eulerAngles.y = i * theta;

            boxCollider = sectionCollider.AddComponent.<BoxCollider>();
            boxCollider.center = Vector3.zero;
            boxCollider.size = new Vector3(width, height, sectionLength);

            sectionCollider.transform.localPosition = new Vector3(innerRadius + (width / 2), 0, 0);
        }
    }
}

【讨论】:

    【解决方案5】:

    1.) 切勿使用 MESH COLLIDER。使用盒子和胶囊碰撞器的组合。

    2.) 检查刚体中的约束。如果您勾选 Freeze Position X,它将通过 X 轴上的对象。 (对于 y 轴相同)。

    【讨论】:

    • 为什么不使用网格对撞机?
    • Mesh collider 很难处理,更多的mesh意味着更多的处理器使用,这个问题很老了,当时mesh在廉价手机上更难处理,特别是android手机。
    【解决方案6】:

    老问题,但也许对某人有帮助。

    转到项目设置>时间并尝试将固定时间步长和最大允许时间步长除以二或四。

    我遇到的问题是我的玩家能够从比玩家对撞机更小的开口中挤过去,这解决了这个问题。它还有助于阻止快速移动的物体。

    【讨论】:

      【解决方案7】:
      • Edit ---> Project Settings ---> Time ...减少“Fixed Timestep”值..这将解决问题,但会对性能产生负面影响。

      • 另一种解决方案是计算坐标(例如,你有一个球和墙。球会撞到墙。所以计算墙的坐标并根据这些坐标设置击球过程)

        李>

      【讨论】:

        【解决方案8】:

        尝试将模型设置为环境和静态。这解决了我的问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多