Predict the position of a Rigidbody Object in x se

2019-01-26 01:50发布

Let's say that you have a Rigidbody Object that moves. Force is added to this Object via Rigidbody.AddForce or Rigidbody.velocity. The Object can roll hit another Object and change direction.

I know about Extrapolation but in this case, it's nearly impossible to use some formula to obtain the position of the object in x seconds, since the Object can hit another object and change speed/direction in the process.

Unity 2017 introduced Physics.autoSimulation and Physics.Simulate to solve this problem. For 2D physics, that is Physics2D.autoSimulation and Physics2D.Simulate. All I did was first set Physics.autoSimulation to false then call the Physics.Simulate function.


In my example, I wanted to know where a Rigidbody would be in 4 seconds after adding force to it, it seems to work fine for tiny seconds like 1. The problem is that when I pass in bigger numbers like 5 and above, to the Simulate function, the predicted position is not accurate. It's way way off.

Why is this happening and how can I fix it? This problem is worse on Android devices.

My current Unity version is Unity 2017.2.0b5.

Below is the sample code I am using. The guide GameObject is simply used to display/show where that predicted position is.

public GameObject bulletPrefab;
public float forceSpeed = 50;

public GameObject guide;

// Use this for initialization
IEnumerator Start()
{
    //Disable Physics AutoSimulation
    Physics.autoSimulation = false;

    //Wait for game to start in the editor before moving on(NOT NECESSARY)
    yield return new WaitForSeconds(1);

    //Instantiate Bullet
    GameObject obj = Instantiate(bulletPrefab);

    Rigidbody bulletRigidbody = obj.GetComponent<Rigidbody>();

    //Calcuate force speed. (Shoot towards the x + axis)
    Vector3 tempForce = bulletRigidbody.transform.right;
    tempForce.y += 0.4f;
    Vector3 force = tempForce * forceSpeed;

    //Addforce to the Bullet
    bulletRigidbody.AddForce(force, ForceMode.Impulse);

    //yield break;
    //Predict where the Rigidbody will be in 4 seconds
    Vector3 futurePos = predictRigidBodyPosInTime(bulletRigidbody, 4f);//1.3f
    //Show us where that would be
    guide.transform.position = futurePos;
}

Vector3 predictRigidBodyPosInTime(Rigidbody sourceRigidbody, float timeInSec)
{
    //Get current Position
    Vector3 defaultPos = sourceRigidbody.position;

    Debug.Log("Predicting Future Pos from::: x " + defaultPos.x + " y:"
        + defaultPos.y + " z:" + defaultPos.z);

    //Simulate where it will be in x seconds
    Physics.Simulate(timeInSec);

    //Get future position
    Vector3 futurePos = sourceRigidbody.position;

    Debug.Log("DONE Predicting Future Pos::: x " + futurePos.x + " y:"
        + futurePos.y + " z:" + futurePos.z);

    //Re-enable Physics AutoSimulation and Reset position
    Physics.autoSimulation = true;
    sourceRigidbody.velocity = Vector3.zero;
    sourceRigidbody.useGravity = false;
    sourceRigidbody.position = defaultPos;

    return futurePos;
}

1条回答
祖国的老花朵
2楼-- · 2019-01-26 02:21

You are even lucky that the value of 1 worked at-all. You shouldn't pass any value above 0.03 to the Physics.Simulate or Physics2D.Simulate function.

When the value is above 0.03, you have to it into pieces then use the Simulate function in a loop. Decrementing the x time while checking if it is still more or equals to Time.fixedDeltaTime should do it.

Replace

Physics.Simulate(timeInSec);

with

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

Your new complete predictRigidBodyPosInTime function should look something like this:

Vector3 predictRigidBodyPosInTime(Rigidbody sourceRigidbody, float timeInSec)
{
    //Get current Position
    Vector3 defaultPos = sourceRigidbody.position;

    Debug.Log("Predicting Future Pos from::: x " + defaultPos.x + " y:"
        + defaultPos.y + " z:" + defaultPos.z);

    //Simulate where it will be in x seconds
    while (timeInSec >= Time.fixedDeltaTime)
    {
        timeInSec -= Time.fixedDeltaTime;
        Physics.Simulate(Time.fixedDeltaTime);
    }

    //Get future position
    Vector3 futurePos = sourceRigidbody.position;

    Debug.Log("DONE Predicting Future Pos::: x " + futurePos.x + " y:"
        + futurePos.y + " z:" + futurePos.z);

    //Re-enable Physics AutoSimulation and Reset position
    Physics.autoSimulation = true;
    sourceRigidbody.velocity = Vector3.zero;
    sourceRigidbody.useGravity = false;
    sourceRigidbody.position = defaultPos;

    return futurePos;
}
查看更多
登录 后发表回答