Search Unity

What approach should I take to calculating collisions and collision resolutions?

Discussion in 'Physics' started by DroidifyDevs, Apr 26, 2020.

  1. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    I'm making a game where a car races down a track, with some fairly unrealistic scenarios planned (loops and ramps that defy gravity). Here's what it looks like right now:
    upload_2020-4-26_14-54-6.png

    I tried to do the car's physics with add force, wheel colliders, using the kart controller, rgidbody.velocity changes, but I never liked the result so I decided to do my own custom physics since my use case is unique, but also not too demanding. Basically, the way it works is I have raycasts going down from the front and back of the car, I set its angle to match the angle of the road and set transform.position manually to move along the track. Later on I'll add gravity where if it's high enough from the road for some time I can move it down. In case you're wondering about the specifics, here's the code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CarVelocityController : MonoBehaviour
    6. {
    7.     public LayerMask layerMask;
    8.     public float normalDistance;
    9.     public float moveSpeed;
    10.     public float sideMoveSpeed;
    11.     float speed;
    12.     Vector3 calculatedPosition;
    13.     Vector3 startOffset;
    14.  
    15.     Rigidbody rigid;
    16.     public Vector3 rigidVelocity;
    17.  
    18.     public List<Transform> rayOrigins;
    19.     public Ray flRay;
    20.     public Vector3 flRayHit;
    21.     public Ray frRay;
    22.     public Vector3 frRayHit;
    23.     public Ray blRay;
    24.     public Vector3 blRayHit;
    25.     public Ray brRay;
    26.     public Vector3 brRayHit;
    27.     public Transform fmPoint; //front middle point
    28.     public Transform mmPoint;
    29.     public Transform bmPoint; //back middle point
    30.     Vector3 fmRayHit;
    31.     Vector3 bmRayHit;
    32.     Vector3 midPoint;
    33.     Vector3 crossVector;
    34.     Vector3 editedCrossVector;
    35.  
    36.     public float xAngle;
    37.     public float zAngle;
    38.  
    39.     // Start is called before the first frame update
    40.     void Start()
    41.     {
    42.         rigid = GetComponent<Rigidbody>();
    43.         //get offset between the point that's between the 2 raycast origins and the actual transform position
    44.         startOffset = transform.position - (new Vector3(transform.position.x, (fmPoint.position.y + bmPoint.position.y) / 2f, (fmPoint.position.z + bmPoint.position.z) / 2f));
    45.     }
    46.  
    47.     // Update is called once per frame
    48.     private void FixedUpdate()
    49.     {
    50.         Vector3 lastPosition = transform.position;
    51.         rigidVelocity = new Vector3(Mathf.Round(rigid.velocity.x), Mathf.Round(rigid.velocity.y), Mathf.Round(rigid.velocity.z));
    52.         Vector3 nextFrameInput = new Vector3(0, 0, Input.GetAxis("Vertical"));
    53.         UpdateRaycasts();
    54.         UpdateRotation();
    55.         UpdatePosition();
    56.     }
    57.  
    58.     void UpdateRaycasts()
    59.     {
    60.         int i = 0;
    61.         RaycastHit rayHit;
    62.         foreach(Transform rayOrigin in rayOrigins)
    63.         {
    64.             if (Physics.Raycast(rayOrigins[i].position, -transform.up, out rayHit, Mathf.Infinity, layerMask))
    65.             {
    66.                 if (i == 0)
    67.                     flRayHit = rayHit.point;
    68.                 if (i == 1)
    69.                     frRayHit = rayHit.point;
    70.                 if (i == 2)
    71.                     blRayHit = rayHit.point;
    72.                 if (i == 3)
    73.                     brRayHit = rayHit.point;
    74.             }
    75.             i++;
    76.         }
    77.         if (Physics.Raycast(fmPoint.position, -transform.up, out rayHit, Mathf.Infinity, layerMask))
    78.             fmRayHit = rayHit.point;
    79.         if (Physics.Raycast(bmPoint.position, -transform.up, out rayHit, Mathf.Infinity, layerMask))
    80.             bmRayHit = rayHit.point;
    81.     }
    82.  
    83.     void UpdatePosition()
    84.     {
    85.         //midpoint is the point between the 2 raycast hits
    86.         midPoint = new Vector3(transform.position.x, (fmRayHit.y + bmRayHit.y) / 2f, (fmRayHit.z + bmRayHit.z) / 2f);
    87.         //backToFrontPointVector is the orientation of the car
    88.         Vector3 backToFrontPointVector = fmPoint.position - bmPoint.position;
    89.         Vector3 btfPointVectorDirection = backToFrontPointVector / (backToFrontPointVector.magnitude);
    90.         Vector3 editedBTFVectorDirection = new Vector3(Input.GetAxis("Horizontal") * sideMoveSpeed, btfPointVectorDirection.y, btfPointVectorDirection.z) * moveSpeed;
    91.         Debug.DrawRay(transform.position, backToFrontPointVector * 5, Color.cyan);
    92.  
    93.         //crossVector is used to keep car a certain distance from the ground
    94.         crossVector = Vector3.Cross(backToFrontPointVector.normalized, transform.right).normalized;
    95.         editedCrossVector = new Vector3(crossVector.x * normalDistance, crossVector.y * normalDistance, crossVector.z * normalDistance);
    96.         calculatedPosition = midPoint + (editedBTFVectorDirection * Time.fixedDeltaTime) + editedCrossVector + startOffset;
    97.  
    98.         transform.position = calculatedPosition;
    99.     }
    100.  
    101.     void UpdateRotation()
    102.     {
    103.         xAngle = -1 * Mathf.Atan((fmRayHit.y - bmRayHit.y) / (fmRayHit.z - bmRayHit.z));
    104.         //for each quadrant we need to come up with a 360 degree angle
    105.         if (fmRayHit.y <= bmRayHit.y && fmRayHit.z < bmRayHit.z)
    106.             xAngle = 180 + ((xAngle * 180f) / Mathf.PI);
    107.         if (fmRayHit.y > bmRayHit.y && fmRayHit.z < bmRayHit.z)
    108.             xAngle = 180 + ((xAngle * 180f) / Mathf.PI);
    109.         if (fmRayHit.y < bmRayHit.y && fmRayHit.z > bmRayHit.z)
    110.             xAngle = (xAngle * 180f) / Mathf.PI;
    111.         if (fmRayHit.y >= bmRayHit.y && fmRayHit.z >= bmRayHit.z)
    112.             xAngle = 360 + ((xAngle * 180f) / Mathf.PI);
    113.  
    114.         zAngle = ((Mathf.Atan(flRayHit.y - frRayHit.y) / (Mathf.Abs(flRayHit.x - frRayHit.x))) * -180f) / Mathf.PI;
    115.         transform.rotation = Quaternion.Euler(xAngle, 0, 0);
    116.     }
    117.  
    118.     private void OnDrawGizmos()
    119.     {
    120.         Gizmos.color = Color.yellow;
    121.         //Gizmos.DrawSphere(fmRayHit, 0.2f);
    122.         //Gizmos.DrawSphere(bmRayHit, 0.2f);
    123.         //Gizmos.DrawSphere(midPoint, 0.2f);
    124.         Gizmos.DrawSphere(editedCrossVector, 0.3f);
    125.         Gizmos.DrawSphere(new Vector3(transform.position.x, midPoint.y + (crossVector.y * 1.25f), transform.position.z), 0.2f);
    126.     }
    127. }
    However, I have an interesting upcoming dilemma. I need the car to have collisions with other cars, and also I might have issues with clipping if the frame rate dips to low. If the car moves and due to a performance issue the front raycast ends up behind the road, the car goes right through.

    Could anyone with knowledge of how collisions and collision resolution give me some guidance as to how to calculate how the cars should move after colliding, and also what to do if there is clipping? So far my only idea is to keep a collider on the car that's only a trigger, and if it collides with the road I can get that collision point from Unity's physics system... however I'm not sure if mixing so much custom physics with Unity's physics is a good idea. Thank you for any guidance!