How to use Physics.autoSimulation correctly?

Viewed 1646

I'm trying to predict the position of a game object using Physics.autoSimulation (the game object has a rigidbody attached). The problem is that the object has some kind of flickering when I trigger the simulation. Like this:

enter image description here

I read this thread. I'm starting to think that I'm not using autoSimulation correctly. Here's my code:

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

public class TestBall : MonoBehaviour {


    public float timeCheck = 3f;
    public float force = 10f;

    private Rigidbody rb;

    void Start () {

        rb = GetComponent<Rigidbody>();

    }//end Start



    void Update () {

        if (Input.GetKeyDown(KeyCode.Space)) {

            //Starts the simulation...
            Physics.autoSimulation = false;
            rb.AddForce(Vector3.right * force, ForceMode.Impulse);
            Vector3 futurePosition = CheckPosition(rb);    
            print("Future position at " + futurePosition);

            //...and move the GameObject for real
            rb.AddForce(Vector3.right * force, ForceMode.Impulse);

        }//end if

    }//end Update



    Vector3 CheckPosition (Rigidbody defaultRb) {

        Vector3 defaultPos = defaultRb.position;
        Quaternion defaultRot = defaultRb.rotation;

        float timeInSec = timeCheck;

        while (timeInSec >= Time.fixedDeltaTime) {

            timeInSec -= Time.fixedDeltaTime;
            Physics.Simulate(Time.fixedDeltaTime);

        }//end while

        Vector3 futurePos = defaultRb.position;

        Physics.autoSimulation = true;

        defaultRb.velocity = Vector3.zero;
        defaultRb.position = defaultPos;
        defaultRb.rotation = defaultRot;

        return futurePos;

    }//end CheckPosition

}//end TestBall

It is like for a tiny moment, the game object moves at the end of the simulation and then comes back because the positions resets in CheckPosition function.

Thanks for the help!

1 Answers

You are using Physics.autoSimulation correctly. The problem is how you reset the position.

Modifying Rigidbody's position and rotation is different from modiying its transform's position and rotation. You want to reset the Object's transform with with the transform not the Rigidbody so that the move will happen right away not over multiple frames as that will then make it visible to the screen.

Replace:

defaultRb.position = defaultPos;
defaultRb.rotation = defaultRot;

with

defaultRb.transform.position = defaultPos;
defaultRb.transform.rotation = defaultRot;

Also, since this is a rolling ball, setting the velocity isn't a enough to stop it. You must also set its angularVelocity too because the ball is rotating and also translating:

defaultRb.velocity = Vector3.zero;
defaultRb.angularVelocity = Vector3.zero;

This is your new CheckPosition function with no flickering:

Vector3 CheckPosition(Rigidbody defaultRb)
{

    Vector3 defaultPos = defaultRb.position;
    Quaternion defaultRot = defaultRb.rotation;

    float timeInSec = timeCheck;

    while (timeInSec >= Time.fixedDeltaTime)
    {

        timeInSec -= Time.fixedDeltaTime;
        Physics.Simulate(Time.fixedDeltaTime);

    }//end while

    Vector3 futurePos = defaultRb.position;


    Physics.autoSimulation = true;

    defaultRb.velocity = Vector3.zero;
    defaultRb.angularVelocity = Vector3.zero;

    defaultRb.transform.position = defaultPos;
    defaultRb.transform.rotation = defaultRot;

    return futurePos;

}//end CheckPosition

Finally, your current code does not add force on top of the old force. This means that if you press the space key and press it again again, force is not added on top of the old force. Instead, the object is stopped then a new force is added. The old one is lost.

If you want to add new force on top of the old force, you have to store the current velocity and angularVelocity before the simulation then apply them back to the Rigidbody after the simulation. The object speed should increase over time.

This has nothing to do with your problem but is just another bug in your code. Below is the fix for this(new Update and CheckPosition function) :

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        //Get current velocity and angularVelocity
        Vector3 defaultVelocity = rb.velocity;
        Vector3 defaultAngularVelocity = rb.angularVelocity;

        //Starts the simulation...
        Physics.autoSimulation = false;
        rb.AddForce(Vector3.right * force, ForceMode.Impulse);
        Vector3 futurePosition = CheckPosition(rb, defaultVelocity, defaultAngularVelocity);
        print("Future position at " + futurePosition);

        //...and move the GameObject for real
        rb.AddForce(Vector3.right * force, ForceMode.Impulse);

    }//end if

}//end Update



Vector3 CheckPosition(Rigidbody defaultRb, Vector3 defaultVelocity, Vector3 defaultAngularVelocity)
{

    Vector3 defaultPos = defaultRb.position;
    Quaternion defaultRot = defaultRb.rotation;

    float timeInSec = timeCheck;

    while (timeInSec >= Time.fixedDeltaTime)
    {
        timeInSec -= Time.fixedDeltaTime;
        Physics.Simulate(Time.fixedDeltaTime);

    }//end while

    Vector3 futurePos = defaultRb.position;


    Physics.autoSimulation = true;

    //Stop object force
    defaultRb.velocity = Vector3.zero;
    defaultRb.angularVelocity = Vector3.zero;

    //Reset Position
    defaultRb.transform.position = defaultPos;
    defaultRb.transform.rotation = defaultRot;

    //Apply old velocity and angularVelocity
    defaultRb.velocity = defaultVelocity;
    defaultRb.angularVelocity = defaultAngularVelocity;

    return futurePos;

}//end CheckPosition
Related