我有一个简单的脚本,当与某些东西碰撞时将速度设置为零,但似乎不会立即起作用:
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来证明这个问题。
在发生碰撞后立即停止球是非常重要的。
怎么做?
答案 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;
或在文字模式中= \ 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并得到两个值,因为给定方向有两个完美的碰撞位置(一个在第二个对象之前,一个在之后)。
你得到这个等式 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;
}
}
希望这会有所帮助:)