3

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!

Programmer
  • 121,791
  • 22
  • 236
  • 328
Kete
  • 70
  • 1
  • 7
  • What's your Unity version and can you post animated gif or video of the flickering? I just want to see how bad it is and if it is even possible to fix – Programmer Sep 28 '17 at 19:38
  • Thanks @Programmer. Added GIF image and Unity´s version is 2017.1.1f1 – Kete Sep 29 '17 at 05:46
  • Hi, can you try unity 2017.2 before I put an answer? This feature is new and I want to make sure it's not a bug. Also, what are you doing with the `futurePosition` variable – Programmer Sep 29 '17 at 05:50
  • With version 2017.2.0f1 happens the same thing. I want the future position to program some AI. – Kete Sep 29 '17 at 22:31
  • Did you find a fix? I've been experimenting for days and I think I have a fix – Programmer Oct 01 '17 at 10:43
  • Hi. I was thinking something this weekend and looks like it works but... I don't think is a good solution but tell me what you think: **1.** Cloned the ball and called it "Ghost ball". **2.** Removed its mesh renderer (so it is invisible) and TestBall script. **3.** Put the two balls in a new layer call "Balls" which don't collide between them (Edit > Project settings > Physics). **4.** Create a new property of type rigidbody in TestBall script and attach the Ghost ball in it. **5.** Applied the simulation to the ghost ball (which is the same game object as "ball"). Please, tell me your fix. – Kete Oct 01 '17 at 19:52
  • Sorry, I couldn't test it. I will do it as soon as I get back to my place. I appreciate your help very much and I will give you feedback as soon as I can. Thank you. – Kete Oct 02 '17 at 08:11

1 Answers1

3

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
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • It works! I thought that I shouldn't mess up with the transorm if I'm using physics but your explanation make all sense. Thank you very much for your time and help! – Kete Oct 02 '17 at 16:07
  • Yes, you shouldn't move rigidbody object with its transform if you want it to detect collision or trigger but this is different since you are just resetting its position not just moving it around. You are welcome! – Programmer Oct 02 '17 at 16:29