Search code examples
c#unity-game-enginegame-physics

Can we detect when a rigid body collides using Physics.Simulate in unity


public void RunSimulation()
{
        Physics.autoSimulation = false;
        simulatedBodies = FindObjectsOfType<Rigidbody>().Select(rb => new SimulatedBody(rb)).ToArray();
        striker.GetComponent<Rigidbody> ().AddForce (new Vector3 (200.0f, 0.0f, 200.0f));

        for (int i = 0; i < maxIterations; i++)
        {
            Physics.Simulate(0.02f);
            //GET COLLISIONS HERE 
            if (simulatedBodies.All(body => body.rigidbody.IsSleeping()))
            {
                print(i);
                break;
            }
        }
        Physics.autoSimulation = true;
}

I am trying to use physics.simulate in unity. According to unity documentation "simulation includes all the stages of collision detection, rigidbody and joints integration, and filing of the physics callbacks (contact, trigger and joints" I need to get at which iteration does my striker gameobject collides with other gameobject. If I user auto simulation, OnCollisionEnter works. However it doesn't work when I use Physics.Simulate it doesn't register. What am I doing wrong or how do I get when my object collides with other objects.


Solution

  • Can we detect when a rigid body collides using Physics.Simulate in unity

    Yes, it can.

    I once did an experiment on this and it worked, I opened the-same sample scene used and made the test again and it worked again. Here is an statement from the doc which is also in your question:

    Simulation includes all the stages of collision detection, rigidbody and joints integration, and filing of the physics callbacks (contact, trigger and joints).

    So, it can detect collision.

    If I user auto simulation, OnCollisionEnter works. However it doesn't work when I use Physics.Simulate it doesn't register.

    You can't use IsSleeping() to get collision. Assuming you have collider and Rigidbody properly setup then the problem is that the maxIterations value is too small leading to the simulation not being finished and not reaching the destination where it is supposed to collide with another object. If this collision is not done, OnCollisionEnter will not be called.

    Increase your maxIterations to give it more distance movement in the simulation and OnCollisionEnter should be called. As for detecting which iteration your GameObject collides with other GameObject, use a boolean variable as flag and set it to true in the OnCollisionEnter function then break out of the for loop when this boolean variable is true.

    const int maxIterations = 60;
    int collideIteration = 0;
    bool collided = false;
    
    public void RunSimulation()
    {
        //RESET OLD VALUES
        collided = false;
        collideIteration = 0;
    
        Physics.autoSimulation = false;
    
        Rigidbody rbdy = GetComponent<Rigidbody>();
        rbdy.AddForce(new Vector3(200.0f, 0.0f, 200.0f));
    
    
        for (int i = 0; i < maxIterations; i++)
        {
            Physics.Simulate(0.02f);
    
            //Check if collided then break out of the loop 
            if (collided)
            {
                collideIteration = i;
    
                print(i);
                break;
            }
        }
        Physics.autoSimulation = true;
    }
    
    public int GetCollideIteration()
    {
        return collideIteration;
    }
    
    void OnCollisionEnter(Collision collision)
    {
        collided = true;
        Debug.Log("Collision: " + collision.collider.name);
    }
    

    If increasing maxIterations did not solve your issue then look into time based simulations. It will do the simulation in x seconds. 5 seconds is used in the code below and the only thing changes is the RunSimulation() function. The rest of the code above is still valid:

    public void RunSimulation()
    {
        //RESET OLD VALUES
        collided = false;
        collideIteration = 0;
    
        Physics.autoSimulation = false;
    
        Rigidbody rbdy = GetComponent<Rigidbody>();
        rbdy.AddForce(new Vector3(200.0f, 0.0f, 200.0f));
    
        //Simulate where it will be in 5 seconds
        float timeInSec = 5f;
        int i = 0;
    
        while (timeInSec >= Time.fixedDeltaTime)
        {
            timeInSec -= Time.fixedDeltaTime;
            Physics.Simulate(Time.fixedDeltaTime);
    
            //Check if collided then break out of the loop 
            if (collided)
            {
                collideIteration = i;
    
                print(i);
                break;
            }
            i++;
        }
        Physics.autoSimulation = true;
    }