Unity3D设置速度不会立即在OnCollisionEnter中生效

时间:2017-12-08 11:12:49

标签: c# unity3d rigid-bodies

我有一个简单的脚本,当与某些东西碰撞时将速度设置为零,但似乎不会立即起作用:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveController : MonoBehaviour {

    [SerializeField]
    private float shootVelocity=30f;

    void Start () {

    }

    private void OnGUI()
    {
        if(GUI.Button(new Rect(0,0,100,100), "shoot"))
        {
            GetComponent<Rigidbody>().velocity = shootVelocity * Vector3.forward;
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        GetComponent<Rigidbody>().velocity = Vector3.zero;
        GetComponent<Rigidbody>().isKinematic = true;
    }

}

我也做了一个youtube video来证明这个问题。

在发生碰撞后立即停止球是非常重要的。

怎么做?

2 个答案:

答案 0 :(得分:1)

你遇到这个问题是因为在进行物理计算之后会有特定的时间步骤,你可以在这里阅读更多相关信息https://docs.unity3d.com/Manual/class-TimeManager.html

想象一下这种情况,物体没有碰撞这个帧,下一帧第一个物体移动一段距离它现在正在与第二个物体碰撞,但它已经在该物体内的某个地方,因为它移动得非常快,然后检测到碰撞但已经太晚了。有时如果对象非常小和/或移动非常快,则不会检测到碰撞,因为当它首先检查对象时,一个是在对象2之前,当它检查第二个对象时,一个已经传递了对象二,因此没有检测到碰撞。

实现这一目标的一种方法是让这个时间步长更小,以便Unity更频繁地检查碰撞,但这会增加开销,特别是如果你有大量的物理计算,并且球体停止时的位置永远不会是完美的。我建议你用自己的计算来编写Unity的模拟。

我将举例说明如何计算完美的停止位置(两个物体在一点接触的位置),如果物体在通过其他物体时始终沿同一方向移动,并且您正在使用两个球体碰撞器,你知道世界规模的半径。我们将它们命名为radius1和radius2,参数“t”表示向前/向后对象需要移动多少才能获得完美的碰撞位置。

检测到切割时,您可以进行以下计算。

Vector3 firstVector = transform.position-collidedObject.transform.position; Vector3 secondVector = direction; float r = radius1+radius2;

(firstVector - secondVector * t).sqrMagnitude = r ^ 2;

扩展你的等式 enter image description here

或在文字模式中= \ sqrt {2x_2 ^ 2t ^ 2 + z_2 ^ 2t ^ 2-2x_1x_2t-2z_1x_2t-2z_1z_2t + x_1 ^ 2 + 2z_1 ^ 2}

现在求解t并得到两个值,因为给定方向有两个完美的碰撞位置(一个在第二个对象之前,一个在之后)。

你得到这个等式 enter image description here Link to Symbolab equations

它可能看起来很复杂但你可以看到这两个解决方案中只有一个区别(在减号中),其余部分相同,你可以将它分成3个变量,使它看起来很简单,这个只关注一个等式,所以看起来很多。

现在假设你有“t1”和“t2”的等式值,你现在可以做了

float finalT = Mathf.Abs(t1) < Mathf.Abs(t2) ? t1 : t2;
transform.position += dir*t;

只有在检测到碰撞时才需要计算所有这些,我还建议仅在以下情况下执行此操作:

if((transform.position-collidedObject.transform.position).sqrMagnitude > (radius1+radius2 + someThresholdValue)^2)

如果Unity引擎已经很好地定位了对象,那么你就忽略了计算。让我知道它是怎么回事,希望这有帮助!

注意:即使对象没有恒定方向,您也可以通过获取最后的已知方向来近似它,并且为了计算,使其在该值上保持不变。你几乎没有差别,它可以极大地解决计算问题。

答案 1 :(得分:0)

这就是物理学的工作原理。

如果移动物体具有更高的速度,它将具有更高的动量,因此需要更多时间来停止。

您可以尝试更新这样的代码,以提高效率。

public class MoveController : MonoBehaviour 
{

    [SerializeField]
    private float shootVelocity=30f;

    Rigidbody rb;
    void Start () 
    {
        rb = GetComponent<Rigidbody>();
    }

    private void OnGUI()
    {
        if(GUI.Button(new Rect(0,0,100,100), "shoot"))
        {
            rb.velocity = shootVelocity * Vector3.forward;
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        rb.isKinematic = true;  
        rb.velocity = Vector3.zero;
        rb.angularVelocity = Vector3.zero;
    }
}

希望这会有所帮助:)