【问题标题】:Rigidbody velocity is zero sometimes while the rigidbody is moving刚体移动时,刚体速度有时为零
【发布时间】:2019-02-20 16:09:39
【问题描述】:

我正在尝试向我的可推送对象添加声音,并且只有一个简单的 if 语句来检查可推送对象是否正在移动。这个概念很简单,如果物体正在移动,则应该播放声音,而当它不移动时,则不应该播放。然而问题是,当我调试该值时,每 5 帧左右就有一个 0。这会导致声音工作不一致。我的脚本真的很简单,我试过改成fixedupdate,但是没用。我在某处读到物理计算是在 fixedUpdate 中完成的。

public class PushableObject : MonoBehaviour
{
    Rigidbody rb;
    AudioSource audioS;

    bool rbIsMoving = false;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        audioS = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        if (rb.velocity == Vector3.zero)
        {
            Debug.Log("Not moving");
            audioS.volume = 0f;
        }
        else
        {
            Debug.Log("Moving");
            audioS.volume = 0.1f;
        }
    }
}

编辑:我刚刚发现如果玩家将可推动的物体推入墙壁,声音仍在播放,因此我认为我必须改变方式来确定物体是否在移动。

【问题讨论】:

    标签: c# unity3d velocity rigid-bodies


    【解决方案1】:

    这是因为single precision floating point

    您永远(不应该)直接比较两个 float 值,因为它们在逻辑上可能相等(例如 1 = 5*0.2),但对于计算机而言,它们可能相差一个小的“epsilon”

    所以 Unity 决定 Vector3 仅使用 0.00001 的精度来实现 == 的相等性


    宁可使用Mathf.Approximately,而是使用非常小的Epsilon


    比比较rb.velocity0 的每个组件更容易的是,您真正想要的是rb.velocity.magnitude,这实际上是整体速度。

    if (Mathf.Approximately(rb.velocity.magnitude, 0))
    

    更新

    或者使用Vector3.Distance 或者再次使用Mathf.Approximately 来存储最后一个位置并将其与当前位置进行比较

    private Vector3 lastPosition;
    
    private void LateUpdate()
    {
        if(Mathf.Approximately(Vector3.Distance(lastPosition, transform.position), 0))
        {
            //...
        }
        else
        {
            //...
            lastPosition = transform.positiom;
        }
    }
    

    或使用自定义阈值

    if(Vector3.Distance(lastPosition, transform.position) <= someThreshold)
    

    或者这次你实际上可以使用==,如果你想要0.00001的阈值

    if(lastPosition == transform.position)
    

    【讨论】:

    • 由于某种原因,声音的断断续续仍在发生,但我确实明白这背后的想法。是否可以更改范围?即介于 -0.1 和 0.1 之间?
    • 查看我的更新以与最后一个位置进行比较。在那里你可以添加一个阈值
    • 感谢您的深入解释,但由于某种原因,声音卡顿仍然存在。这很奇怪,就好像在一帧或某事中多次检查位置一样。
    • 您可以尝试在LateUpdate 中使用它。这样可以确保它总是在所有物理和用户交互都已经完成之后完成
    • 我刚刚尝试使用LateUpdate,但不幸的是没有任何成功。奇怪的是,当可推动对象移动时,它仍然时不时地返回,就好像它没有移动一样。即 lastPosition 和 currentPosition 是相同的。当 x 帧的 lastPos 和 currentPos 相同而不是音量应该为 0 时,是否可以做类似的事情?
    【解决方案2】:

    您可以检查 RigidBody 是否处于睡眠状态:

    if (!rb.IsSleeping()
    {
    //it's moving
    }
    else
    {
    //it's not
    }
    

    或者检查变换位置自上一帧以来是否移动过:

    Vector3 lastposition;
    Gameobject go = somegameobject; 
    
    function Update()
    {
        if (lastposition == go.transform.position)
        {
         //not moving
        }
        else
        {
         //moving
        }
        lastposition = go.transform.position;
    }
    

    【讨论】:

    • 哈,我不知道这个,但是当玩家碰撞时它仍然会注册。在我的游戏中,玩家必须按下一个按钮才能按下,所以它还不能正常工作。
    • 您还可以将变换的坐标存储在变量中,并检查它们是否在 Update/FixedUpdate 中发生变化。如果它们与上一帧相同,则对象没有移动,否则它正在移动。
    • 我目前稍微更改了 if 语句以检查玩家是否按住按钮。当我将物体推入墙壁时,可推动物体没有移动但声音仍然是播放器,我认为是由于按住了按钮并且刚体不再睡觉。我如何检查职位是否像您的评论那样发生了变化?
    • 用位置比较更新了我的答案。
    • 现在两个答案都是关于相同的技术,感谢您提供有用的见解!
    【解决方案3】:

    您可以使用Transform.hasChanged检查上次更新时玩家位置是否发生变化

            if (!this.transform.hasChanged)
            {
                print("Player is not moving");
            }
            transform.hasChanged = false;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      相关资源
      最近更新 更多