Search Unity

New physics issues after migrating from Unity 3.5.7 to 4.1.2

Discussion in 'Editor & General Support' started by Gamieon, Apr 29, 2013.

  1. Gamieon

    Gamieon

    Joined:
    Mar 20, 2010
    Posts:
    38
    I developed a pinball game with Unity 3.5 Pro, and recently made a branch of it and opened it with Unity 4.1.2 Free. Some issues that came up while playing it in 4.1.2:
    • Sometimes the ball gets stuck in a flipper
    • Sometimes a flipper goes off its pivot when the ball makes contact with it
    I made a video that shows the issues in action.

    http://www.youtube.com/watch?v=fWEzdNVV4L4

    Does anyone have any thoughts as to why this would be happening all of a sudden? The game was in development for the better part of a year in Unity 3.x, and I've never had those problems. I also didn't change anything in the project before opening it in the latest version of Unity. I verified that all the physics settings are identical (that I know of).
     
  2. Gamieon

    Gamieon

    Joined:
    Mar 20, 2010
    Posts:
    38
    Minor update: I reduced the Solver Iteration Count from 15 to 6, and it got a little better (only happens 20% of the time from before). I have no explanation why. I also made it so my pre-existing code to "push a ball out of a penetration" will make the ball collider a trigger until it's out of the collider.

    I think the difference is between how Unity 3 and 4 deal with object penetration.
     
  3. Gamieon

    Gamieon

    Joined:
    Mar 20, 2010
    Posts:
    38
    Update #2: I fixed it through a number of methods:

    1. Forcing the ball out of the collider if it penetrates through a certain depth

    My solution is to make the ball into a trigger and change its velocity to match the contact tangent to force it out. This is handled in OnCollisionStay and OnCollisionExit. I don't believe it will handle multiple contact points properly.

    2. Restrict the ball to the world bounds

    Imagine a rectangle that surrounds your playfield. The code in FixedUpdate will make sure it won't leave the region.

    3. Ball won't go through the flippers

    This one took weeks of tweaking to figure out, and is handled in FixedUpdate. Every flipper in my simulation has a box collider; and immediately above it, another box collider that is a trigger which I call the "flipper tangent." The "FlipperBuffer" objects are box colliders that surround the flippers and are triggers. My logic is this: If the ball is inside a flipper buffer, and behind a tangent; then it needs to bring itself back up to the tangent and start moving up and away. Of course there are sometimes false positives, but it's better than the alternative of the ball going through.

    4. Nuclear option: Self-destruct if the ball doesn't move for three seconds

    This is handled in LateUpdate, and there's one exception to this rule besides the ball at rest over the pin: The "FlipperBuffer" objects are boxes that surround the flippers. Pinball players often use flippers to steady the ball, so we want to make sure it won't self destruct like that.


    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class BallPhysics : MonoBehaviour
    6. {
    7.     float samePositionLastTime = 0;
    8.     Vector3 samePositionPos = Vector3.zero;
    9.  
    10.     Transform myTransform; // Cache the transform (optimization)
    11.     Rigidbody myRigidbody;
    12.    
    13.     bool restrictToWorldBounds;
    14.  
    15.     FlipperBuffer[] flipperBuffers;
    16.    
    17.    
    18.     void OnBallReleaseLockTriggerExit()
    19.     {
    20.         Debug.Log("Restricting to world bounds");
    21.         restrictToWorldBounds = true;
    22.     }
    23.        
    24.     void Awake()
    25.     {
    26.         rigidbody.mass = PinballPhysics.BallMass;
    27.         myRigidbody = rigidbody;
    28.         // Initialize our mandatory variables
    29.         flipperBuffers = (FlipperBuffer[])FindObjectsOfType(typeof(FlipperBuffer));    
    30.         myTransform = transform;
    31.         samePositionPos = myRigidbody.position;
    32.         samePositionLastTime = Time.time;
    33.         restrictToWorldBounds = false;
    34.     }
    35.    
    36.     void FixedUpdate()
    37.     {  
    38.         // Restrict to world bounds
    39.         if (restrictToWorldBounds)
    40.         {
    41.             Vector3 adjustedPosition = myTransform.position;
    42.             bool mustAdjust = false;
    43.             if (adjustedPosition.x < -64.2f) {
    44.                 adjustedPosition.x = -64.2f;
    45.                 mustAdjust = true;
    46.             }
    47.             else if (adjustedPosition.x > 56  adjustedPosition.y < 42) {
    48.                 adjustedPosition.x = 53.6f;
    49.                 mustAdjust = true;
    50.             }
    51.             else if (adjustedPosition.y > 90.3f)
    52.             {
    53.                 adjustedPosition.y = 90.3f;
    54.                 mustAdjust = true;
    55.             }
    56.            
    57.             if (mustAdjust)
    58.             {
    59. #if UNITY_EDITOR
    60.                 Debug.Log("Emergency adjustment!");
    61. #endif
    62.                 myTransform.position = adjustedPosition;
    63.             }
    64.         }
    65.        
    66.         // If the ball has a collision layer, then we're OK to begin flipper correction checking
    67.         if (myRigidbody.velocity.y < 0  gameObject.layer > 0)
    68.         {
    69.             foreach (FlipperBuffer buffer in flipperBuffers)
    70.             {
    71.                 // Begin by seeing if the ball is within the "flipper buffer" which is a rectangular region of space near the flipper.
    72.                 // No point in doing correction calculations if the ball is far away.                  
    73.                 if (buffer.collider.bounds.Contains(myTransform.position))
    74.                 {                  
    75.                     // We're near the flipper and it's in motion. Do a further check to see if the flipper is moving anywhere but to its resting position
    76.                     if ((buffer.flipper.IsGoingToPressedRotation() || buffer.flipper.IsAtRest())  Mathf.Abs(buffer.flipper.rigidbody.angularVelocity.z) > 2.0f)
    77.                     {
    78.                         // Yes, conditions are right for doing corrections though we're not sure if we need to do one yet
    79.                    
    80.                         // Cast a ray from behind the ball toward the tangent. If it hits, then try to put the ball above the flipper
    81.                         RaycastHit hitInfo;
    82.                         // if (Physics.Raycast(myTransform.position, Vector3.Normalize(-myRigidbody.velocity), out hitInfo, Mathf.Infinity, layerMask))
    83.                         // if (Physics.Raycast(myTransform.position, Vector3.Normalize(-myRigidbody.velocity), out hitInfo, Mathf.Infinity, (1 << 14)))
    84.                         if (buffer.flipperTangent.collider.Raycast( new Ray(myTransform.position, Vector3.Normalize(-myRigidbody.velocity)), out hitInfo, Mathf.Infinity))
    85.                         {
    86.                             // Move the ball up to the tangent point
    87.                             // (c.haag 2011-02-28) - If you uncomment this out, sometimes the ball is jerked to a place it shouldn't
    88.                             // be. This is because the ball may have already started going in the opposite direction by a regular
    89.                             // Unity collision and the flipper tangent may not be in the direction it was when the ball actually penetrated
    90.                             // it by this juncture.
    91.                             //transform.position = hitInfo.point;
    92.                            
    93.                             Debug.Log("Fixing flipper fall-through");
    94.                            
    95.                             Vector3 surfaceNormal = -hitInfo.normal;
    96.                             Vector3 ballRay = Vector3.Normalize(myRigidbody.velocity);
    97.                             Vector3 angleOfReflection = Vector3.Reflect(ballRay, surfaceNormal);
    98.                             // Also apply an extra velocity so it doesn't stick or loiter around the boundary of the flipper
    99.                             Vector3 extraVelocity = (myRigidbody.velocity.y < 0  myRigidbody.velocity.y > -60) ? new Vector3(0,400,0) : new Vector3(0,100,0);
    100.                             //Vector3 extraVelocity = Vector3.Normalize(surfaceNormal) * (( Vector3.Magnitude(rb.velocity) < 60 ) ? 100.0f : 500.0f);
    101.                             myRigidbody.velocity = angleOfReflection * Vector3.Magnitude(myRigidbody.velocity);
    102.                             // Ensure the current velocity is always positive
    103.                             if (myRigidbody.velocity.y < 0) { myRigidbody.velocity = new Vector3(myRigidbody.velocity.x, -myRigidbody.velocity.y, myRigidbody.velocity.z); }
    104.                             myRigidbody.velocity += extraVelocity;
    105.                         }
    106.                        
    107.                         break;
    108.                     }
    109.                 }
    110.             }  
    111.         }
    112.     }
    113.    
    114.     void OnCollisionStay(Collision collision)
    115.     {
    116.         foreach (ContactPoint contact in collision.contacts)
    117.         {
    118.             float f = Vector3.SqrMagnitude(contact.point - transform.position);
    119.             if (f < 2.0f) // Pick this "out of a hat"
    120.             {
    121. #if UNITY_EDITOR
    122.                 Debug.Log("Correcting " + f);
    123. #endif
    124.                 collider.isTrigger = true;
    125.                 float av = rigidbody.velocity.magnitude;
    126.                 rigidbody.velocity = contact.normal * av;
    127.             }
    128.         }
    129.     }
    130.    
    131.     void OnCollisionExit(Collision collision)
    132.     {
    133.         if (collider.isTrigger)
    134.         {
    135.             collider.isTrigger = false;
    136. #if UNITY_EDITOR
    137.             Debug.Log("Correction done");
    138. #endif
    139.         }
    140.     }
    141.    
    142.     void LateUpdate()
    143.     {
    144.         if (myRigidbody.useGravity)
    145.         {
    146.             // I got the ball stuck on a wall once. This should prevent that from happening again.
    147.             float e = 0.002f;
    148.             if (myRigidbody.position.x >= samePositionPos.x - e  myRigidbody.position.x <= samePositionPos.x + e  myRigidbody.position.y >= samePositionPos.y - e  myRigidbody.position.y <= samePositionPos.y + e)
    149.             {
    150.                 if (Time.time - samePositionLastTime > 3  
    151.                     !(myRigidbody.position.x > 59  myRigidbody.position.y < -44) /* isReadyToLaunch */
    152.                     )
    153.                 {
    154.                     bool destroyBall = true;
    155.                     foreach (FlipperBuffer buffer in flipperBuffers)
    156.                     {
    157.                         // Begin by seeing if the ball is within the "flipper buffer" which is a rectangular region of space near the flipper.
    158.                         // If it's in there, don't destroy the ball because the player is just holding on to it.
    159.                         if (buffer.collider.bounds.Contains(myTransform.position))
    160.                         {
    161.                             destroyBall = false;
    162.                             break;
    163.                         }
    164.                     }
    165.                    
    166.                     if (destroyBall)
    167.                     {
    168.                         // If we get here, the ball is probably stuck because it's well above the flippers
    169.                         // and it hasn't moved in 4 seconds. We have no choice but to destroy it and credit
    170.                         // the player a ball.
    171.                         Debug.Log("Destroying stuck ball");
    172.                         GameObject p = GameObject.Find("Player");
    173.                         p.SendMessage("ReplayStuckBall", GetComponent("Ball"));
    174.                     }
    175.                 }
    176.             }
    177.             else
    178.             {
    179.                 // Object is in motion
    180.                 samePositionLastTime = Time.time;
    181.                 samePositionPos = myRigidbody.position;
    182.             }
    183.         }
    184.         else
    185.         {
    186.             // This can't be the player's ball if it's not using graviry. It's probably
    187.             // a boss ball.
    188.         }
    189.     }  
    190. }
    191.