简体   繁体   中英

Rigidbody velocity is zero sometimes while the rigidbody is moving

I'm trying to add sound to my pushable object and just have a simple if statement for checking if the pushable object is moving. The concept is quite simple, if the object is moving the sound should play and when it's not moving it shouldn't. The problem however is, that when I debug the value there is a 0 every 5 frames or so. This causes the sound to work inconsistently. The script I have is really simple, and I have tried changing to fixedupdate, but it didn't work. I had read somewhere that physics calculations are done in 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;
        }
    }
}

Edit: I have just figured out that if the player pushes the pushable object into the wall the sound is still playing, for this reason I think I have to change the way too determine if the object is moving or not.

This happens due to single precision floating point .

You never (should) compare two float values directly since they might logically be equal (eg 1 = 5*0.2 ) but for the computer they might be different by a small "epsilon"

So Unity decides that Vector3 simply uses only a precision of 0.00001 for equality for ==


Rather use Mathf.Approximately which uses a very small Epsilon instead.


Than easier than comparing each component of the rb.velocity against 0 what you actually want is therb.velocity.magnitude which is actually the overall speed.

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

Update

Alternatively store the last position and compare it to the current one using Vector3.Distance either again with Mathf.Approximately

private Vector3 lastPosition;

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

Or with a custom threshold

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

or this time you actually can use == if a threshold of 0.00001 is what you want

if(lastPosition == transform.position)

You can check if the RigidBody is sleeping:

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

Or check if the transform position has moved since last frame:

Vector3 lastposition;
Gameobject go = somegameobject; 

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

You can use Transform.hasChanged to check if the player position has changed on the last update

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM