Search Unity

WheelCollider.GetGroundHit(...) Hit.Point value one frame behind.

Discussion in 'Physics' started by zezba9000, Aug 16, 2015.

  1. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    While making the visual wheel object transform offset in relationship to their collides hit point I've run into a problem. As a suggestion to the Unity team, add visual wheels that interact with the ground in your demo and you will see the issue exists: http://docs.unity3d.com/Manual/WheelColliderTutorial.html

    When I call "WheelColliderFL.GetGroundHit(out hit)" the "hit.point" value is ONE frame behind making this method totally useless. It also makes it impossible to "correctly" offset the Wheel onto the ground.

    I did a test drawing line via: "Debug.DrawLine(hit.point, WheelColliderFL.transform.position);".
    When the car doesn't move the line is perfectly perpendicular to the ground.
    When the care is moving fast, the line is at an angle because the HitPoint is wrong.


    Code (csharp):
    1. void Update()
    2. {
    3.     WheelHit hit;
    4.     if (WheelColliderFL.GetGroundHit(out hit)) Debug.DrawLine(hit.point, WheelColliderFL.transform.position);
    5. }



    As you can see this is way to simple to mess up.
    I must have this hit point be valid to correctly offset the wheel from its origin. (Any work arounds?)
    Using Windows Editor 5.1.2f x64, Windows 10, x64.
    NOTE: Still messed up in Unity 5.2.0b6.
     
    Last edited: Aug 17, 2015
  2. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    I confirmed my assumption is in fact the issue.
    Ran another test with Unity 5.2 but this time drew the line using the current hit.point with the last WheelCollider transform position:

    Code (csharp):
    1. WheelHit hit;
    2. if (WheelColliderFL.GetGroundHit(out hit))
    3. {
    4.     Debug.DrawLine(hit.point, lastPos);
    5.     lastPos = WheelColliderFL.transform.position;
    6. }


    As you can see the line is strait confirming "GetGroundHit" is totally out of sync with the current frame making its use impractical.
     
  3. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    A whole frame! OH NOES!!!

    Its fine from my experience.

    Dont know what you're trying to do that makes one frame 'impractical/totally useless"
     
  4. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    ...yes a frame lag is bad, was making the issue clear :\

    I'm not doing anything special, its very simple and easy to repo with the code I provided. You can see my scene object is set up correctly. I set up a basic rig just as the Unity docs suggest, everything is fine. The frame lag happens both in the Update method and FixedUpdate method. I highly doubt its working for you in the context I provide. What context did you use the "GetGroundHit" method in? Also what Unity version? If memory serves, this was also an issue in Unity4.6.

    I can hack a fix with RayCast but thats not the correct way to do this. So not the end of the world, just annoying. At first I thought something was wrong with my code but after running some tests I find its a Unity bug.
     
    Last edited: Aug 16, 2015
  5. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Well, you better explain what you're trying to use it for, because I use it for making my wheels look like they are responding to suspension, and it works perfectly for that.

    The fact that your drawing between the position of the wheel last frame and current wheel position (current frame) makes me wonder what you're trying to achieve? Of course the last pos is going to be one frame behind. That's what your code does. Takes the wheel position last frame and draws it to current frame.
     
  6. SonicBloomEric

    SonicBloomEric

    Joined:
    Sep 11, 2014
    Posts:
    1,088
    Have you tried running your code in FixedUpdate() rather than Update()?
     
  7. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
  8. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    Yes both methods had the same results.
     
  9. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    Good suggestions but it gives the same results.
    The Code below is being ran in the "FixedUpdate" method:
    Code (csharp):
    1. Vector3 pos;
    2. Quaternion rot;
    3. WheelColliderFL.GetWorldPose(out pos, out rot);
    4. if (WheelColliderFL.GetGroundHit(out hit)) wheelSuspensionDisFL = (hit.point - pos).magnitude - (WheelColliderFL.radius + ExtraWheelRadius);
    5. else wheelSuspensionDisFL += (WheelColliderFL.suspensionDistance - wheelSuspensionDisFL) * .1f;
    6.  
    7. WheelColliderFR.GetWorldPose(out pos, out rot);
    8. if (WheelColliderFR.GetGroundHit(out hit)) wheelSuspensionDisFR = (hit.point - pos).magnitude - (WheelColliderFR.radius + ExtraWheelRadius);
    9. else wheelSuspensionDisFR += (WheelColliderFR.suspensionDistance - wheelSuspensionDisFR) * .1f;
    10.  
    11. WheelColliderBL.GetWorldPose(out pos, out rot);
    12. if (WheelColliderBL.GetGroundHit(out hit)) wheelSuspensionDisBL = (hit.point - pos).magnitude - (WheelColliderBL.radius + ExtraWheelRadius);
    13. else wheelSuspensionDisBL += (WheelColliderBL.suspensionDistance - wheelSuspensionDisBL) * .1f;
    14.  
    15. WheelColliderBR.GetWorldPose(out pos, out rot);
    16. if (WheelColliderBR.GetGroundHit(out hit)) wheelSuspensionDisBR = (hit.point - pos).magnitude - (WheelColliderBR.radius + ExtraWheelRadius);
    17. else wheelSuspensionDisBR += (WheelColliderBR.suspensionDistance - wheelSuspensionDisBR) * .1f;
    Now if I use the code below it works when the car is moving fast but the calculation is a frame behind even though this is hard to see on flat planes:
    Code (csharp):
    1. Vector3 pos;
    2. Quaternion rot;
    3. WheelColliderFL.GetWorldPose(out pos, out rot);
    4. if (WheelColliderFL.GetGroundHit(out hit)) wheelSuspensionDisFL = (hit.point - pos).magnitude - (WheelColliderFL.radius + ExtraWheelRadius);
    5. else wheelSuspensionDisFL += (WheelColliderFL.suspensionDistance - wheelSuspensionDisFL) * .1f;
    6.  
    7. WheelColliderFR.GetWorldPose(out pos, out rot);
    8. if (WheelColliderFR.GetGroundHit(out hit)) wheelSuspensionDisFR = (hit.point - pos).magnitude - (WheelColliderFR.radius + ExtraWheelRadius);
    9. else wheelSuspensionDisFR += (WheelColliderFR.suspensionDistance - wheelSuspensionDisFR) * .1f;
    10.  
    11. WheelColliderBL.GetWorldPose(out pos, out rot);
    12. if (WheelColliderBL.GetGroundHit(out hit)) wheelSuspensionDisBL = (hit.point - pos).magnitude - (WheelColliderBL.radius + ExtraWheelRadius);
    13. else wheelSuspensionDisBL += (WheelColliderBL.suspensionDistance - wheelSuspensionDisBL) * .1f;
    14.  
    15. WheelColliderBR.GetWorldPose(out pos, out rot);
    16. if (WheelColliderBR.GetGroundHit(out hit)) wheelSuspensionDisBR = (hit.point - pos).magnitude - (WheelColliderBR.radius + ExtraWheelRadius);
    17. else wheelSuspensionDisBR += (WheelColliderBR.suspensionDistance - wheelSuspensionDisBR) * .1f;
    Here is the Update and FixedUpdate code in context. As you can see with these simple tests it suggests a frame lag bug:
    Code (csharp):
    1.  
    2. void Update()
    3.     {
    4.         // input >>>
    5.         steerAngleInputTweenTo = 0;
    6.         accelInputTweenTo = 0;
    7.         breakInputTweenTo = 0;
    8.  
    9.         // <<< keyboard input
    10.         if (Input.GetKey(KeyCode.A)) steerAngleInputTweenTo -= SteerAngleMaxValue;
    11.         if (Input.GetKey(KeyCode.D)) steerAngleInputTweenTo += SteerAngleMaxValue;
    12.         if (Input.GetKey(KeyCode.W)) accelInputTweenTo += 1;
    13.         if (Input.GetKey(KeyCode.S)) accelInputTweenTo -= 1;
    14.  
    15.         // <<< make sure input is within bounds
    16.         if (steerAngleInputTweenTo < -SteerAngleMaxValue) steerAngleInputTweenTo = -SteerAngleMaxValue;
    17.         if (steerAngleInputTweenTo > SteerAngleMaxValue) steerAngleInputTweenTo = SteerAngleMaxValue;
    18.         if (accelInputTweenTo < -1) accelInputTweenTo = -1;
    19.         if (accelInputTweenTo > 1) accelInputTweenTo = 1;
    20.  
    21.         // <<< calculate break input
    22.         if (accelInputTweenTo < 0)
    23.         {
    24.             if (breakMode == 0 || breakMode == 1) breakMode = (rigidbody.velocity.magnitude >= 4) ? 1 : 2;
    25.         }
    26.         else
    27.         {
    28.             breakMode = 0;
    29.         }
    30.  
    31.         if (breakMode == 1)
    32.         {
    33.             breakInputTweenTo = Mathf.Abs(accelInputTweenTo);
    34.             accelInputTweenTo = 0;
    35.         }
    36.  
    37.         // offset wheel to suspension distance
    38.         var down = -transform.up;
    39.         var posFL = wheelColliderTransformFL.position;
    40.         var posFR = wheelColliderTransformFR.position;
    41.         var posBL = wheelColliderTransformBL.position;
    42.         var posBR = wheelColliderTransformBR.position;
    43.  
    44.         WheelFL.position = posFL + (down * wheelSuspensionDisFL);
    45.         WheelFR.position = posFR + (down * wheelSuspensionDisFR);
    46.         WheelBL.position = posBL + (down * wheelSuspensionDisBL);
    47.         WheelBR.position = posBR + (down * wheelSuspensionDisBR);
    48.  
    49.         // rotate wheen turn
    50.         WheelFL.localRotation = Quaternion.Euler(0, WheelColliderFL.steerAngle - 90, -wheelRotFL);
    51.         WheelFR.localRotation = Quaternion.Euler(0, WheelColliderFR.steerAngle + 90, wheelRotFR);
    52.         WheelBL.localRotation = Quaternion.Euler(0, -90, -wheelRotBL);
    53.         WheelBR.localRotation = Quaternion.Euler(0, 90, wheelRotBR);
    54.  
    55.         // rotate wheel to its rpm
    56.         wheelRotFL += (WheelColliderFL.rpm / 60) * 360 * Time.deltaTime;
    57.         wheelRotFR += (WheelColliderFR.rpm / 60) * 360 * Time.deltaTime;
    58.         wheelRotBL += (WheelColliderFL.rpm / 60) * 360 * Time.deltaTime;
    59.         wheelRotBR += (WheelColliderBR.rpm / 60) * 360 * Time.deltaTime;
    60.     }
    61.  
    62. void FixedUpdate()
    63.     {
    64.         // calculate boost
    65.         if (accelInputTweenTo > 0 && rigidbody.velocity.magnitude <= 10)
    66.         {
    67.             rigidbody.AddForce(transform.forward * rigidbody.mass * 20);
    68.         }
    69.  
    70.         // tween input values
    71.         steerAngleInput += (steerAngleInputTweenTo - steerAngleInput) * .05f;
    72.         accelInput += (accelInputTweenTo - accelInput) * .1f;
    73.         if (breakInputTweenTo != 0) breakInput += (breakInputTweenTo - breakInput) * .1f;
    74.         else breakInput = 0;
    75.  
    76.         // apply input values
    77.         WheelColliderFL.motorTorque = accelInput * MotorPower;
    78.         WheelColliderFR.motorTorque = accelInput * MotorPower;
    79.         WheelColliderBL.motorTorque = accelInput * MotorPower;
    80.         WheelColliderBR.motorTorque = accelInput * MotorPower;
    81.  
    82.         WheelColliderFL.brakeTorque = breakInput * MotorPower * 2;
    83.         WheelColliderFR.brakeTorque = breakInput * MotorPower * 2;
    84.         WheelColliderBL.brakeTorque = breakInput * MotorPower * 2;
    85.         WheelColliderBR.brakeTorque = breakInput * MotorPower * 2;
    86.  
    87.         WheelColliderFL.steerAngle = steerAngleInput;
    88.         WheelColliderFR.steerAngle = steerAngleInput;
    89.  
    90.         // apply wheel hit
    91.         var posFL = wheelColliderTransformFL.position;
    92.         var posFR = wheelColliderTransformFR.position;
    93.         var posBL = wheelColliderTransformBL.position;
    94.         var posBR = wheelColliderTransformBR.position;
    95.  
    96.         WheelHit hit;
    97.         // NOTE: use this method after Unity fixes the bug!
    98.         /*if (WheelColliderFL.GetGroundHit(out hit)) wheelSuspensionDisFL = (hit.point - posFL).magnitude - (WheelColliderFL.radius + ExtraWheelRadius);
    99.         else wheelSuspensionDisFL += (WheelColliderFL.suspensionDistance - wheelSuspensionDisFL) * .1f;
    100.  
    101.         if (WheelColliderFR.GetGroundHit(out hit)) wheelSuspensionDisFR = (hit.point - posFR).magnitude - (WheelColliderFR.radius + ExtraWheelRadius);
    102.         else wheelSuspensionDisFR += (WheelColliderFR.suspensionDistance - wheelSuspensionDisFR) * .1f;
    103.  
    104.         if (WheelColliderBL.GetGroundHit(out hit)) wheelSuspensionDisBL = (hit.point - posBL).magnitude - (WheelColliderBL.radius + ExtraWheelRadius);
    105.         else wheelSuspensionDisBL += (WheelColliderBL.suspensionDistance - wheelSuspensionDisBL) * .1f;
    106.  
    107.         if (WheelColliderBR.GetGroundHit(out hit)) wheelSuspensionDisBR = (hit.point - posBR).magnitude - (WheelColliderBR.radius + ExtraWheelRadius);
    108.         else wheelSuspensionDisBR += (WheelColliderBR.suspensionDistance - wheelSuspensionDisBR) * .1f;*/
    109.  
    110.         // NOTE: May want to use this method when bug is fixed instead!
    111.         /*Vector3 pos;
    112.         Quaternion rot;
    113.         WheelColliderFL.GetWorldPose(out pos, out rot);
    114.         if (WheelColliderFL.GetGroundHit(out hit)) wheelSuspensionDisFL = (hit.point - pos).magnitude - (WheelColliderFL.radius + ExtraWheelRadius);
    115.         else wheelSuspensionDisFL += (WheelColliderFL.suspensionDistance - wheelSuspensionDisFL) * .1f;
    116.  
    117.         WheelColliderFR.GetWorldPose(out pos, out rot);
    118.         if (WheelColliderFR.GetGroundHit(out hit)) wheelSuspensionDisFR = (hit.point - pos).magnitude - (WheelColliderFR.radius + ExtraWheelRadius);
    119.         else wheelSuspensionDisFR += (WheelColliderFR.suspensionDistance - wheelSuspensionDisFR) * .1f;
    120.  
    121.         WheelColliderBL.GetWorldPose(out pos, out rot);
    122.         if (WheelColliderBL.GetGroundHit(out hit)) wheelSuspensionDisBL = (hit.point - pos).magnitude - (WheelColliderBL.radius + ExtraWheelRadius);
    123.         else wheelSuspensionDisBL += (WheelColliderBL.suspensionDistance - wheelSuspensionDisBL) * .1f;
    124.  
    125.         WheelColliderBR.GetWorldPose(out pos, out rot);
    126.         if (WheelColliderBR.GetGroundHit(out hit)) wheelSuspensionDisBR = (hit.point - pos).magnitude - (WheelColliderBR.radius + ExtraWheelRadius);
    127.         else wheelSuspensionDisBR += (WheelColliderBR.suspensionDistance - wheelSuspensionDisBR) * .1f;*/
    128.  
    129.         // HACK: WheelCollider.GetGroundHit one frame behind!
    130.         if (WheelColliderFL.GetGroundHit(out hit)) wheelSuspensionDisFL = (hit.point - wheelColliderLastPosFL).magnitude - (WheelColliderFL.radius + ExtraWheelRadius);
    131.         else wheelSuspensionDisFL += (WheelColliderFL.suspensionDistance - wheelSuspensionDisFL) * .1f;
    132.  
    133.         if (WheelColliderFR.GetGroundHit(out hit)) wheelSuspensionDisFR = (hit.point - wheelColliderLastPosFR).magnitude - (WheelColliderFR.radius + ExtraWheelRadius);
    134.         else wheelSuspensionDisFR += (WheelColliderFR.suspensionDistance - wheelSuspensionDisFR) * .1f;
    135.  
    136.         if (WheelColliderBL.GetGroundHit(out hit)) wheelSuspensionDisBL = (hit.point - wheelColliderLastPosBL).magnitude - (WheelColliderBL.radius + ExtraWheelRadius);
    137.         else wheelSuspensionDisBL += (WheelColliderBL.suspensionDistance - wheelSuspensionDisBL) * .1f;
    138.  
    139.         if (WheelColliderBR.GetGroundHit(out hit)) wheelSuspensionDisBR = (hit.point - wheelColliderLastPosBR).magnitude - (WheelColliderBR.radius + ExtraWheelRadius);
    140.         else wheelSuspensionDisBR += (WheelColliderBR.suspensionDistance - wheelSuspensionDisBR) * .1f;
    141.  
    142.         wheelColliderLastPosFL = posFL;
    143.         wheelColliderLastPosFR = posFR;
    144.         wheelColliderLastPosBL = posBL;
    145.         wheelColliderLastPosBR = posBR;
    146.     }
    For now I will add in a RayCast method as it will be the most actuate per frame instead of the hack I have now. It just probably wouldn't be as smooth over bumpy terrain.
     
    Last edited: Aug 17, 2015
  10. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,508
    As far as I've tested, this behavior existed since Unity 2.x at least. But it's not a bug. It's a consequence of the iteration with the physics engine in Unity. Check out the script lifecycle flowchart here:

    http://docs.unity3d.com/Manual/ExecutionOrder.html

    upload_2015-8-17_10-59-8.png

    The FixedUpdate event is called before the internal physics update is executed for a given iteration step. So when your code in FixedUpdate is reached you're reading the physic values from the previous iteration step.

    I wouldn't call this "critical" nor "useless method" at all. My vehicle physics package (EVP) includes an optimization parameter for positioning the wheels using GetGroundHit only (no raycast). The trick is to use hit.point for calculating the actual compression of the suspension, then using that height for positioning the wheel. Even running 1 frame behind, this is hardly noticeable at mid-low speeds. It's not noticeable at all at high speeds.

    Still, I think that having a chance of using the physics values updated right after the internal physics update would be very useful and makes a lot of sense in certain situations. My suggestion to Unity Team would be adding a new yield statement like yield WaitForEndOfFixedFrame (a kind of WaitForEndOfFrame on physics) to be executed right after the physics update has been completed.
     
    yant likes this.
  11. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    What you're saying doesn't make sense as to why the value is behind when used in the Update method. If you look at the full diagram you will see FixedUpdate and all physics calculations are done BEFORE the Update method is called. Because collision calculations happen before Update is called the new WheelCollider hit point should be valid on that frame if retrived within the Update method.

    Remember this "also" fails in the Update method not just the FixedUpdate. I should be able to layout things visually in the Update method after there state changes were made in the physics engine. This still looks like a long standing Unity bug.
     
  12. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    596
    With the help of some stronger comparisons in this thread, I can clearly see how unexpected this could be.

    Though, I'm afraid, there are no easy steps to correct what you just outlined. As Edy mentioned, this has been like that since forever, and, truth to be told, the probability of this being addressed soon is not very high. But let me assure you that if a solution happens to emerge, there would be no reasons to hold it from being delivered to you guys.

    Now, for the curious ones, I'll explain some of the technical details behind the walls.

    As you know, physics are simulated at a fixed frequency. Every physics frame, FixedUpdate is invoked, and the purpose of this is to let the scripts do the final adjustments right before the simulation step runs.

    When simulation runs it does this:
    - perform suspension raycasts
    - update vehicles (spring forces, tire forces, etc)
    - update everything else (Rigidbodies, colliders, triggers, etc)

    That means every frame all wheels are put on the ground, and their positions are later used to work out the springs compressions, tire forces and so on. After that, everything else gets updated.

    Returning back to the GetGroundHit question. You might be able to guess already that GetGroundHit simply returns the cached raycast hit position computed last physics frame. Since that time, of course, the vehicle has moved a little, so did all surrounding bodies. Thus the inaccuracy you observe. GetGroundHit works this way for performance reasons, where you sacrifice some fair bit of accuracy to save on additional raycasts.

    Because the only linear degree of freedom of wheels is moving along the suspension travel direction, GetWorldPose reuses only the suspension compression from the last physics frame. For that reason, I guess, you could try approximating the ground hit position with the base point of the wheel. That would still lag one frame along the suspension travel direction, but, at the very least, is not expected to stay behind your moving vehicle as you get with GetGroundHit.

    The only way to get the exact ground hit point without any lag, that I have right on top of my mind, is to put together a script that would update wheels' touch positions by performing additional raycasts every time after FixedUpdate was called. Probably having your own GetGroundHit implementation that would return the cached ground hit until FixedUpdate was called, and after that cast rays the very first time it gets called, etc.

    Hope this helps. Anthony.

    --
    If it was useful, you might also get interested in this document by me as well: http://www.docdroid.net/vd5x/wc-info.pdf.html
     
    zezba9000 likes this.
  13. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    So you're saying that "forces" are calculated then "movement" is applied. I guessed I had a logic fail here. Because the force being applied on the wheel comes from its position before it moves, after it actually moves doing an extra RayCast when "me" the dev may not need it would be bad. So if I want I could do an extra RayCast or just live with the delay. I'll have to see what visually looks best I guess.
     
  14. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,508
    @zezba9000 Note that there's no 1:1 correspondence among Update and FixedUpdate. The flowchart is a simplification that doesn't mean that each FixedUpdate call will be immediately followed by an Update call. They are called from two separate execution flows. Update is not a good place to put code that critically relies on physics values.

    Still, I can understand your issue: FixedUpdate has the values from the previous iteration. The solution would be Unity to give us some method for accessing the values updated right after the internal physics update. This is where I see the point of "yield WaitForEndOfFixedFrame", or maybe "yield WaitForPhysicsUpdate", you name it.

    I've came to a workaround that proves my point and gives the updated physic values correctly:
    1. Create a big trigger collider that encloses all your scene.
    2. Rename your method FixedUpdate to OnTriggerStay.
    3. At the beginning of that method, write new code that verifies that the time has actually advanced (as the OnTriggerStay method may me invoked several times per physics update):
    Code (csharp):
    1.     float m_lastFixedTime = -1.0f;
    2.  
    3.     // void FixedUpdate ()
    4.     void OnTriggerStay ()
    5.         {
    6.         if (Time.fixedTime == m_lastFixedTime) return;
    7.         m_lastFixedTime = Time.fixedTime;
    8.  
    9.         // ... Your FixedUpdate code here
    10.         }
    OnTriggerStay is called right after the internal physics update, and within the physics execution flow. So the values are updated.

    So I still think that the problem is not a bug, but a feature lack instead. The solution would be having an event or yield instruction that allows us to execute code right after the internal physics update has been completed. In fact, a new yield instruction would be a very elegant solution allowing to write FixedUpdate functions like this:

    Code (csharp):
    1. // (pseudo-code / fictitious)
    2. void FixedUpdate ()
    3.     {
    4.     // This is the regular FixedUpdate code.
    5.     // Reads the values from the previous physics iteration.
    6.  
    7.     return yield new WaitForPhysicsUpdate();
    8.  
    9.     // Post-physics update code here.
    10.     // Reads the values just updated in the internal physics update.
    11.     }
    @yant I've just read your reply. What do you think on the yield solution? I've tested it using OnTriggerStay instead of FixedUpdate in EVP and I can confirm that the values read there are perfectly updated. I hadn't sent that before, ever! In fact, now I see many improvements I could do using the updated values.
     
    Last edited: Aug 17, 2015
  15. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    I don't think a yield would work. I now more understand why this is happening on a technical level. The force used to apply motion to the car object through the wheel is done before the car actually moves. Thus after the car moves with those forces applied the values are no longer valid until the next frame. A simple solution is to use RayCast or live with the frame lag. Not sure what visually looks better.
     
    Last edited: Aug 17, 2015
  16. zezba9000

    zezba9000

    Joined:
    Sep 28, 2010
    Posts:
    985
    I do suggest having a PreUpdate method that gets called before FixedUpdate does so we can handle special events before the physics engine startings processing states for that frame. This would be very useful.
     
  17. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,508
    In physics you have an state x0, then calculate data, forces, etc using that state. Finally apply the result of those calculations for iterating to the new state x1, and the cycle repeats. I can't see how a PreUpdate method called before FixedUpdate could help (isn't that FixedUpdate already?).

    However, having access to the data right after it has been calculated is really useful, as you can use that fresh data for configuring visual stuff within the actual state. For instance, you can accurately set the position of the visual wheel using GetGroundHit alone, without any raycast, by applying the OnTriggerStay workaround I described above. Try it! I've tested that there's no one-frame lag anymore that way.
     
  18. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    596
    @Edy: I think your idea of using OnTriggerStay to emulate something like 'LatePhysicsUpdate' (a callback right after physics simulation is over) should work. Though I'd try to minimise the cost of it by reducing the number of possible overlaps. Maybe it could work by having two small identical trigger cubes overlap somewhere out of the player observed scene? Seems a trigger overlapping the whole scene is likely to put some pressure on PhysX.
     
  19. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,508
    @yant The OnTriggerStay trick is just a "proof of concept" on how a callback invoked right after the physics simulation would be useful. I've found the GetGroundHit values read there to be perfectly precise for visual stuff, without suffering the "one frame behind" issue that can be observed when a vehicle is moving fast.

    I'd like to suggest to add such callback to Unity, so we could have code to be executed right after the physics update is over. Either a "LateFixedUpdate" callback or a yield instruction "WaitForPhysicsUpdate" would be good solutions. What do you think?