Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question NaN error with no console error?

Discussion in 'Editor & General Support' started by SawyerK, Aug 6, 2023.

  1. SawyerK

    SawyerK

    Joined:
    Feb 11, 2016
    Posts:
    53
    Hi!
    I have a pretty big drivetrain script for my truck simulator that have a weird rpm related NaN error.
    This error doesn't show up in the Unity console as an error.

    What happens is basically when the engine stalls (falls below 20 rpm) and the player tries to start the engine again the rpm immediately goes to NaN and the engine can no longer can be started again. Only solution for this is to save game and load it back (changing scenes basically) which is less than ideal for a commercial product.



    Can a Unity master find what is the problem in this script that makes the rpm go NaN after starting the engine after stalling?
    I'm willing to pay for help also if that is needed to fix this issue.

    Unity version: 2019.04.40f1

    Thank you!

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections; // needed for IEnumerator
    3. using System.Collections.Generic; //needed for List class
    4.  
    5. [RequireComponent (typeof (Axles))]
    6. public class Drivetrain : MonoBehaviour {
    7.     [HideInInspector]
    8.     public bool engineTorqueFromFile=false;
    9.     [HideInInspector]
    10.     public int torqueRPMValuesLen=0;
    11.     [HideInInspector]
    12.     public float[,] torqueRPMValues = new float[0,0];
    13.  
    14.     [HideInInspector]
    15.     public Clutch clutch;
    16.     [HideInInspector]
    17.     public CarController carController;
    18.     Setup setup;
    19.     Axles axles;
    20.     [HideInInspector]
    21.     public FuelTank[] fuelTanks;
    22.  
    23.     // Cached rigidbody
    24.     Rigidbody body;  
    25.     Transform myTransform;
    26.  
    27.     // All the wheels the drivetrain should power
    28.     [HideInInspector]
    29.     public Wheel[] poweredWheels;
    30.          
    31.     // engine's maximal power (in HP) and relative RPM
    32.     public float maxPower = 210;
    33.     public float maxPowerRPM=5000;
    34.  
    35.     // engine's maximal torque (in Nm) and relative RPM
    36.     public float maxTorque=360;
    37.     public float maxTorqueRPM=2500;
    38.  
    39.     [HideInInspector]
    40.     public float originalMaxPower = 210;  
    41.  
    42.     [HideInInspector]
    43.     public float maxNetPower;
    44.     [HideInInspector]
    45.     public float maxNetPowerRPM;
    46.     [HideInInspector]
    47.     public float maxNetTorque;
    48.     [HideInInspector]
    49.     public float maxNetTorqueRPM;
    50.  
    51.     [HideInInspector]
    52.     public float torque;
    53.     float netTorque;
    54.     float netTorqueImpulse;
    55.     [HideInInspector]
    56.     public float wheelTireVelo;
    57.  
    58.     // powerband RPM range
    59.     public float minRPM = 1000;
    60.     public float maxRPM=6000;
    61.     // can engine stall?
    62.     public bool canStall = false;
    63.     [HideInInspector]
    64.     public bool startEngine=false;
    65.  
    66.     public bool revLimiter=false;
    67.     public float revLimiterTime = 0.1f; //50ms
    68.     [HideInInspector]
    69.     public bool revLimiterTriggered=false;
    70.     [HideInInspector]
    71.     public bool revLimiterReleased=false;
    72.     float timer;
    73.      
    74.     // engine inertia (how fast the engine spins up), in kg*m^2
    75.     public float engineInertia = 0.3f;
    76.  
    77.     // drivetrain inertia in kg*m^2
    78.     public float drivetrainInertia=0.02f;
    79.     float rotationalInertia;
    80.  
    81.     //engine's friction coefficient.
    82.     //this cause the engine to slow down, and cause engine braking
    83.     public float engineFrictionFactor=0.25f;
    84.  
    85.     // Engine orientation (typically either Vector3.forward or Vector3.right).
    86.     // This determines how the car body moves as the engine revs up.  
    87.     public Vector3 engineOrientation = Vector3.forward;
    88.  
    89.     public enum Transmissions {RWD, FWD, AWD, XWD};
    90.     public Transmissions transmission;
    91.  
    92.     // The gear ratios, including neutral (0) and reverse (negative) gears
    93.     public float[] gearRatios={-2.66f, 0f, 2.66f, 1.91f, 1.39f, 1f, 0.71f};
    94.     [HideInInspector]
    95.     public int neutral=1; //index of neutral gear;
    96.     [HideInInspector]
    97.     public int first=0;
    98.     [HideInInspector]
    99.     public int firstReverse=0;
    100.  
    101.     // The final drive ratio, which is multiplied to each gear ratio
    102.     public float finalDriveRatio = 6.09f;
    103.  
    104.     // Coefficient determining how much torque is Transferred between the wheels when they move at
    105.     // different speeds, to simulate differential locking.
    106.     public float differentialLockCoefficient = 80;
    107.  
    108.     // shifter with one button for every gear
    109.     public bool shifter=false;  
    110.     // shift gears automatically?
    111.     public bool automatic = true;
    112.     // shift in reverse automatically? (with automatic mode)
    113.     public bool autoReverse = true;  
    114.     public float shiftDownRPM=2000;
    115.     public float shiftUpRPM;
    116.     // How long the car takes to shift gears in secs
    117.     public float shiftTime = 0.5f;
    118.     [HideInInspector]
    119.     public float clutchMaxTorque=0; // clutch max torque. If left to zero, its calculated automatically from engine torque
    120.  
    121.     // engage and disengage clutch automatically?
    122.     public bool autoClutch = true;
    123.  
    124.     //clutch engaging and disengaging RPMs with autoclutch
    125.     public float engageRPM=1500;
    126.     public float disengageRPM=1000;
    127.  
    128.     public float _fuelConsumptionAtCostantSpeed = 4.3f; // liters per 100km driving at costant speed
    129.     public float _fuelConsumptionSpeed=130; // speed (km/h) at which vehicle consumes fuelConsumptionAtCostantSpeed value
    130.     public float currentConsumption; // liters per 100km
    131.     [HideInInspector]
    132.     public float istantConsumption;
    133.     [HideInInspector]
    134.     public float RPMAtSpeedInLastGear;
    135.     float secondsToCover100Km; // driving at fuelConsumptionSpeed
    136.  
    137.     [HideInInspector]
    138.     public float clutchEngageSpeed;
    139.     float clutchPosition;
    140.  
    141.     [HideInInspector]
    142.     public float throttle;
    143.     [HideInInspector]
    144.     public float idlethrottle;
    145.     float idlethrottleMinRPMDown;
    146.     float idleNetTorque;
    147.     [HideInInspector]
    148.     public float startTorque;
    149.     float startThrottle;
    150.     float nextStartImpulse;
    151.     float duration;
    152.  
    153.     [HideInInspector]
    154.     public bool shiftTriggered=false;
    155.     [HideInInspector]
    156.     public bool soundPlayed=false;
    157.     [HideInInspector]
    158.     public float clutchDragImpulse;  
    159.     float wheelImpulse;
    160.     float TransferredTorque;
    161.     //float driveShaftSpeed;
    162.     [HideInInspector]
    163.     public float differentialSpeed;
    164.     float clutchSpeed;
    165.     [HideInInspector]
    166.     public bool engaging=false;
    167.  
    168.     float shiftTimer;
    169.     float TimeToShiftAgain;
    170.     [HideInInspector]
    171.     public bool CanShiftAgain=true;
    172.     float ShiftDelay = -1;
    173.     float lastShiftTime = -1;
    174.  
    175.     // state
    176.     public int gear = 1;
    177.     public float rpm;
    178.     float slipRatio;
    179.     float idealSlipRatio;
    180.  
    181.     float engineAngularVelo;
    182.     [HideInInspector]
    183.     public     float angularVelo2RPM=30/Mathf.PI;
    184.     [HideInInspector]
    185.     public     float RPM2angularVelo=Mathf.PI/30;
    186.     [HideInInspector]
    187.     public     float KW2CV=1.359f;
    188.     [HideInInspector]
    189.     public     float CV2KW=0.7358f;
    190.  
    191.     [HideInInspector]
    192.     public float maxPowerDriveShaft;
    193.     [HideInInspector]
    194.     public float currentPower;
    195.  
    196.     float maxPowerKW;
    197.     float maxPowerAngVel;
    198.     float maxPowerEngineTorque;  
    199.     float P1,P2,P3;
    200.  
    201.     [HideInInspector]
    202.     public float curveFactor;  
    203.  
    204.     [HideInInspector]
    205.     public float frictionTorque;
    206.     [HideInInspector]
    207.     public float powerMultiplier=1;  
    208.     [HideInInspector]
    209.     public float externalMultiplier=1;  
    210.     [HideInInspector]
    211.     public float ratio;
    212.     [HideInInspector]
    213.     public float lastGearRatio;
    214.  
    215.     [HideInInspector]
    216.     public bool changingGear=false;
    217.     bool shiftImmediately=false;
    218.     int nextGear;
    219.     float lockingTorqueImpulse;
    220.     float max_power;
    221.     [HideInInspector]  
    222.     public float drivetrainFraction;
    223.  
    224.     [HideInInspector]
    225.     public float velo;
    226.     [HideInInspector]
    227.     bool fuel=true;
    228.  
    229.     [HideInInspector]
    230.     public float RPMAt130Kmh;
    231.      
    232.     float Sqr (float x) { return x*x; }
    233.  
    234.     // // Fuel consumption in liters/100km. If fuel consumption == 0 ---> The engine doesn't consume any fuel at all
    235.     public float fuelConsumptionAtCostantSpeed{
    236.         get{return _fuelConsumptionAtCostantSpeed;}
    237.         set{
    238.             if (value < 0f){
    239.                 _fuelConsumptionAtCostantSpeed = 0f;
    240.             }
    241.             else{
    242.                 _fuelConsumptionAtCostantSpeed = value;
    243.             }
    244.         }
    245.     }
    246.  
    247.     public float fuelConsumptionSpeed{
    248.         get{return _fuelConsumptionSpeed;}
    249.         set{
    250.             if (value < 1f){
    251.                 _fuelConsumptionSpeed = 1f;
    252.             }
    253.             else{
    254.                 _fuelConsumptionSpeed = value;
    255.             }
    256.         }
    257.     }  
    258.  
    259.     void Awake(){
    260.         engineTorqueFromFile=false;
    261.         torqueRPMValuesLen=0;
    262.         body=GetComponent<Rigidbody>();  
    263.         myTransform=transform;
    264.         clutch = new Clutch();
    265.         carController = GetComponent <CarController>();
    266.         setup= GetComponent <Setup>();
    267.         axles=GetComponent <Axles>();
    268.         fuelTanks=GetComponentsInChildren<FuelTank>();
    269.         poweredWheels=axles.rearAxle.wheels; // to avoid NullReferenceException
    270.     }
    271.  
    272.     IEnumerator Start() {
    273.  
    274.         if (setup!=null && setup.enabled==true) {while (setup.loadingSetup==true) yield return new WaitForSeconds(0.02f);}
    275.      
    276.         CalcValues(1,engineTorqueFromFile);
    277.         if (shiftUpRPM==0) shiftUpRPM=maxPowerRPM;
    278.      
    279.         bool found=false;
    280.         for (int i =0; i<gearRatios.Length; i++ ){
    281.             if (gearRatios[i]==0) {neutral=i; first=neutral+1; firstReverse=neutral-1; found=true;}
    282.         }
    283.         if (found==false) Debug.LogError("UnityCar: Neutral gear (a gear with value 0) is missing in gearRatios array. Neutral gear is mandatory" + " (" +myTransform.name+ ")");
    284.      
    285.         SetTransmission(transmission);
    286.      
    287.         if (clutch.maxTorque==0) CalcClutchTorque();
    288.         if (shiftTime==0) shiftTime=0.5f;
    289.  
    290.         lastGearRatio=gearRatios[gearRatios.Length-1]*finalDriveRatio;
    291.      
    292.         // Make sure the property getters methods are called
    293.         fuelConsumptionAtCostantSpeed = fuelConsumptionAtCostantSpeed;
    294.         fuelConsumptionSpeed=fuelConsumptionSpeed;
    295.         RPMAtSpeedInLastGear=CalcRPMAtSpeedInLastGear(fuelConsumptionSpeed);
    296.         secondsToCover100Km=(100/fuelConsumptionSpeed)*3600;
    297.         //-------------------------------------------------------------------------
    298.         // 2769 seconds = (100km / 130km/h) * 3600 seconds/hour
    299.         //-------------------------------------------------------------------------          
    300.      
    301.         CalcIdleThrottle();
    302.         DisengageClutch();
    303.         StartEngine();
    304.     }
    305.  
    306.     public float CalcRPMAtSpeedInLastGear(float speed){
    307.         if (speed>0) return speed/(axles.frontAxle.leftWheel.radius*2*0.1885f/(Mathf.Abs(gearRatios[gearRatios.Length-1])*finalDriveRatio)); // Mathf.PI*3.6f/60 -> 0.1885  
    308.         else return 0;
    309.     }
    310.      
    311.     public void CalcClutchTorque(){
    312.         clutchMaxTorque=Mathf.Round(maxNetTorque*1.6f)*powerMultiplier;
    313.         clutch.maxTorque=clutchMaxTorque;  
    314.     }
    315.  
    316.     public void SetTransmission(Transmissions transmission){
    317.         foreach(Wheel w in axles.allWheels){
    318.             w.lockingTorqueImpulse=0;
    319.             w.drivetrainInertia=0;
    320.             w.isPowered=false;
    321.         }
    322.         if (transmission==Transmissions.FWD){
    323.             foreach(Wheel w in axles.frontAxle.wheels){
    324.                 w.isPowered=true;
    325.             }
    326.             poweredWheels=axles.frontAxle.wheels;
    327.             axles.frontAxle.powered=true;
    328.             axles.rearAxle.powered=false;
    329.             foreach(Axle axle in axles.otherAxles){
    330.                 axle.powered=false;
    331.             }
    332.         }
    333.         else if (transmission==Transmissions.RWD){
    334.             foreach(Wheel w in axles.rearAxle.wheels){
    335.                 w.isPowered=true;
    336.             }
    337.             poweredWheels=axles.rearAxle.wheels;
    338.             axles.frontAxle.powered=false;
    339.             axles.rearAxle.powered=true;
    340.             foreach(Axle axle in axles.otherAxles){
    341.                 axle.powered=false;
    342.             }
    343.         }
    344.         else if (transmission==Transmissions.XWD){
    345.             List<Wheel> wheelsList = new List<Wheel>();
    346.             if (axles.frontAxle.powered==true){
    347.                 foreach(Wheel w in axles.frontAxle.wheels){
    348.                     w.isPowered=true;
    349.                     wheelsList.Add(w);
    350.                 }
    351.             }
    352.             if (axles.rearAxle.powered==true){
    353.                 foreach(Wheel w in axles.rearAxle.wheels){
    354.                     w.isPowered=true;
    355.                     wheelsList.Add(w);
    356.                 }
    357.             }          
    358.             foreach(Axle axle in axles.otherAxles){
    359.                 if (axle.powered==true){
    360.                     foreach(Wheel w in axle.wheels){
    361.                         w.isPowered=true;
    362.                         wheelsList.Add(w);
    363.                     }
    364.                 }
    365.             }
    366.             poweredWheels=wheelsList.ToArray();//new Wheel[axles.frontAxle.wheels.Length + axles.rearAxle.wheels.Length + OtherAxlesWheels.Length];
    367.             //axles.frontAxle.wheels.CopyTo(poweredWheels, 0);
    368.             //axles.rearAxle.wheels.CopyTo(poweredWheels, axles.frontAxle.wheels.Length);
    369.             //OtherAxlesWheels.CopyTo(poweredWheels, axles.rearAxle.wheels.Length);
    370.         }
    371.         else if (transmission==Transmissions.AWD){
    372.             foreach(Wheel w in axles.allWheels){
    373.                 w.isPowered=true;
    374.             }              
    375.             poweredWheels=axles.allWheels;
    376.             axles.frontAxle.powered=true;
    377.             axles.rearAxle.powered=true;
    378.             foreach(Axle axle in axles.otherAxles){
    379.                 axle.powered=true;
    380.             }          
    381.         }
    382.      
    383.         drivetrainFraction = 1f/poweredWheels.Length;
    384.     }
    385.  
    386.     public float CalcEngineTorque(float factor, float rpm){
    387.         if (engineTorqueFromFile==true)
    388.             return CalcEngineTorqueExt(factor, rpm);
    389.         else
    390.             return CalcEngineTorqueInt(factor, rpm);
    391.     }  
    392.  
    393.  
    394.      float CalcEngineTorqueExt(float factor, float RPM){
    395.         if(torqueRPMValuesLen!=0)
    396.         {
    397.             int rp= FindRightPoint(RPM);
    398.             if(rp==0 || rp==torqueRPMValuesLen) // To the left of the graph or to the right of the graph; use 0
    399.                 return 0;
    400.             else
    401.             {
    402.                 float result=(RPM-torqueRPMValues[rp,0])/(torqueRPMValues[rp-1,0] - torqueRPMValues[rp,0])*torqueRPMValues[rp-1,1] - (RPM - torqueRPMValues[rp-1,0])/(torqueRPMValues[rp-1,0] - torqueRPMValues[rp,0])*torqueRPMValues[rp,1];
    403.                 return result*factor;
    404.             }
    405.         }
    406.         else
    407.             return 0;
    408.     }  
    409.  
    410.      int FindRightPoint(float RPM)
    411.     {
    412.         int i;
    413.         for(i=0; i<=torqueRPMValuesLen-1; i++){
    414.             if(torqueRPMValues[i,0]>RPM) break;
    415.         }    
    416.       return i;  
    417.     }
    418.      
    419.     float CalcEngineTorqueInt(float factor, float rpm)
    420.     {
    421.         float result;
    422.         if(rpm < maxTorqueRPM)
    423.             result = maxTorque*(-Sqr(rpm/maxTorqueRPM - 1) + 1);
    424.         else {
    425.             float maxPowerTorque = (maxPower*CV2KW*1000)/maxPowerAngVel;
    426.             float aproxFactor = (maxTorque - maxPowerTorque)/(2*maxTorqueRPM*maxPowerRPM - Sqr(maxPowerRPM) - Sqr(maxTorqueRPM));
    427.             float torque = aproxFactor*Sqr(rpm - maxTorqueRPM) + maxTorque;
    428.             result=torque>0?torque:0;
    429.         }
    430.      
    431.         if(rpm<0 || result<0) result=0;
    432.      
    433.         return result*factor;
    434.     }      
    435.          
    436.     // torque curve is calculated from maxPower and maxPowerRPM using a polynomial expression given in Motor Vehicle Dynamics, Genta (1997)
    437.     public float CalcEngineTorqueInt_reference(float factor, float RPM)
    438.     {
    439.         float result;      
    440.         float currentAngularVelo=RPM*RPM2angularVelo;
    441.              
    442.         result=P1 + P2*currentAngularVelo + P3*(currentAngularVelo*currentAngularVelo);
    443.      
    444.         if (RPM<maxTorqueRPM) result*=1 - Sqr(RPM/maxTorqueRPM - 1);
    445.      
    446.         return result*1000*factor;
    447.     }  
    448.  
    449.     public float CalcEngineFrictionTorque(float factor,float rpm)
    450.     {
    451.         float static_friction = 0.1f;
    452.         if (rpm<minRPM) static_friction=1 - 0.9f*(rpm/minRPM); // rpm=minRPM -> static_friction=0.1f; rpm=0 -> static_friction=1;
    453.         float frictionTorque = maxPowerEngineTorque*factor*engineFrictionFactor*(static_friction + (1.0f - static_friction)*rpm/maxRPM);
    454.  
    455.         return frictionTorque;
    456.     }
    457.  
    458.     // engine power in CV
    459.     float CalcEnginePower(float rpm,bool total, float factor){
    460.         if (total)
    461.             return (CalcEngineTorque(factor,rpm) - CalcEngineFrictionTorque(factor,rpm))*rpm*RPM2angularVelo*0.001f*KW2CV;
    462.         else
    463.             return CalcEngineTorque(factor,rpm)*rpm*RPM2angularVelo*0.001f*KW2CV;
    464.     }
    465.  
    466.     public void StartEngine() {
    467.         engineAngularVelo=(minRPM*RPM2angularVelo)*1.5f;
    468.         //rpm=engineAngularVelo*angularVelo2RPM;
    469.     }
    470.  
    471.     void CalcEngineMaxPower(float powerMultiplier, bool setMaxPower){
    472.         for (float m_maxPowerRPM = minRPM; m_maxPowerRPM < maxRPM; m_maxPowerRPM++)
    473.         {
    474.             float tmpPower1=CalcEnginePower(m_maxPowerRPM ,true,powerMultiplier);
    475.             float tmpPower2=CalcEnginePower(m_maxPowerRPM+1,true,powerMultiplier);
    476.  
    477.             if (tmpPower2> tmpPower1)
    478.             {
    479.                 maxNetPowerRPM=m_maxPowerRPM+1;
    480.                 maxNetPower=tmpPower2;
    481.             }
    482.          
    483.             if (setMaxPower==true){
    484.                 tmpPower1=CalcEnginePower(m_maxPowerRPM ,false,powerMultiplier);
    485.                 tmpPower2=CalcEnginePower(m_maxPowerRPM+1,false,powerMultiplier);
    486.  
    487.                 if (tmpPower2> tmpPower1)
    488.                 {
    489.                     maxPowerRPM=m_maxPowerRPM+1;
    490.                     maxPower=tmpPower2;
    491.                 }
    492.             }
    493.         }
    494.     }
    495.  
    496.     void CalcengineMaxTorque(float powerMultiplier, bool setMaxTorque){
    497.         for (float m_maxTorqueRPM = minRPM; m_maxTorqueRPM< maxRPM; m_maxTorqueRPM++)
    498.         {
    499.             float tmpTorque1=CalcEngineTorque(powerMultiplier,m_maxTorqueRPM) - CalcEngineFrictionTorque(powerMultiplier,m_maxTorqueRPM);
    500.             float tmpTorque2=CalcEngineTorque(powerMultiplier,m_maxTorqueRPM+1) - CalcEngineFrictionTorque(powerMultiplier,m_maxTorqueRPM+1);
    501.          
    502.             if (tmpTorque2 > tmpTorque1)
    503.             {
    504.                 maxNetTorqueRPM=m_maxTorqueRPM+1;
    505.                 maxNetTorque=tmpTorque2;
    506.             }
    507.          
    508.             if (setMaxTorque==true) {
    509.                 tmpTorque1=CalcEngineTorque(powerMultiplier,m_maxTorqueRPM);
    510.                 tmpTorque2=CalcEngineTorque(powerMultiplier,m_maxTorqueRPM+1);
    511.              
    512.                 if (tmpTorque2 > tmpTorque1)
    513.                 {
    514.                     maxTorqueRPM=m_maxTorqueRPM+1;
    515.                     maxTorque=tmpTorque2;
    516.                 }
    517.             }
    518.         }
    519.     }  
    520.  
    521.     public void CalcIdleThrottle(){
    522.         float idleFrictionTorque=CalcEngineFrictionTorque(powerMultiplier,minRPM);
    523.         float idleTorque=CalcEngineTorque(powerMultiplier,minRPM);
    524.         idleNetTorque=idleTorque - idleFrictionTorque;
    525.         for (idlethrottle = 0f; idlethrottle < 1.0f; idlethrottle += 0.0001f){
    526.             if (idleTorque*idlethrottle >= idleFrictionTorque) break;
    527.         }
    528.         idlethrottleMinRPMDown=idlethrottle;
    529.     }
    530.      
    531.     public void CalcValues(float externalFactor, bool setMaxPower){
    532.         maxPowerAngVel=maxPowerRPM*RPM2angularVelo;  
    533.         maxPowerKW=maxPower*CV2KW*externalFactor;
    534.         maxPowerDriveShaft=maxPower*externalFactor;
    535.         P1=maxPowerKW/maxPowerAngVel;
    536.         P2=maxPowerKW/(maxPowerAngVel*maxPowerAngVel);
    537.         P3=(-maxPowerKW)/(maxPowerAngVel*maxPowerAngVel*maxPowerAngVel);      
    538.         maxPowerEngineTorque=CalcEngineTorque(1,maxPowerRPM);
    539.         CalcengineMaxTorque(1,setMaxPower);
    540.         CalcEngineMaxPower(1,setMaxPower);
    541.         originalMaxPower=maxPower;
    542.         curveFactor=externalFactor;
    543.     }      
    544.      
    545.     void FixedUpdate ()
    546.     {      
    547.         if (clutch==null) {clutch= new Clutch();CalcClutchTorque();}
    548.      
    549.         if (shifter==true) automatic=false;
    550.  
    551.         ratio = gearRatios[gear]*finalDriveRatio;
    552.      
    553.         if (rpm <= minRPM+maxRPM*0.05f)
    554.             idlethrottle=idlethrottleMinRPMDown*((minRPM+500 - Mathf.Clamp(rpm,minRPM,rpm))*0.002f);
    555.         else
    556.             idlethrottle=0;
    557.  
    558.         currentPower=CalcEnginePower(rpm,true,powerMultiplier);
    559.              
    560.         float m_engineInertia=engineInertia*powerMultiplier*externalMultiplier;
    561.         float m_drivetrainInertia=drivetrainInertia*powerMultiplier*externalMultiplier;
    562.      
    563.         velo=Mathf.Abs(myTransform.InverseTransformDirection(body.velocity).z);
    564.                      
    565.         if ((rpm >= engageRPM || engaging==true) && autoClutch==true && carController.clutchInput==0 && clutch.GetClutchPosition()!=1 && carController.handbrakeInput==0 && ratio!=0)  {
    566.             EngageClutch();
    567.         }
    568.          
    569.         if ((rpm <= disengageRPM && engaging==false) && autoClutch==true) {
    570.             DisengageClutch();
    571.         }
    572.      
    573.         // execute Gear shifting
    574.         if (changingGear==true) DoGearShifting();
    575.         else lastShiftTime = 0;
    576.          
    577.         // Automatic gear shifting
    578.         if (automatic)
    579.         {
    580.             autoClutch=true;
    581.             if (CanShiftAgain==false){
    582.                 TimeToShiftAgain= Mathf.Clamp01((Time.time - ShiftDelay)/(shiftTime + shiftTime/2));
    583.                 if (TimeToShiftAgain>=1) {
    584.                     CanShiftAgain=true;
    585.                 }  
    586.             }
    587.          
    588.             if (changingGear==false){      
    589.                 if (rpm >= shiftUpRPM){
    590.                     if (gear >= 0 && gear < gearRatios.Length - 1 && Mathf.Abs(slipRatio/idealSlipRatio)<=1 && clutch.GetClutchPosition()!=0 && clutch.speedDiff<50 && engaging==false){
    591.                         if (CanShiftAgain==true && OnGround()) {
    592.                             if (gearRatios[gear]>0) Shift(gear+1);
    593.                             else Shift(gear-1);
    594.                         }
    595.                         CanShiftAgain=false;
    596.                         ShiftDelay = Time.time;  
    597.                     }
    598.                 }
    599.                 else if (rpm <= shiftDownRPM ){
    600.                     if (gear !=first && gear!=firstReverse && gear!=neutral && gear > 0 && gear < gearRatios.Length && clutch.GetClutchPosition()!=0 && Mathf.Abs(clutch.speedDiff)<50 && engaging==false) {    // we dont shiftdown if we are in the first gear
    601.                         if (CanShiftAgain==true && OnGround()) {
    602.                             if (velo<3) { // if speed < 10 km/h we shift directly to the first gear
    603.                                 Shift(first);
    604.                             }
    605.                             else{
    606.                                 if (gearRatios[gear]>0) Shift(gear-1);
    607.                                 else Shift(gear+1);
    608.                             }
    609.                             CanShiftAgain=false;
    610.                             ShiftDelay= Time.time;  
    611.                         }
    612.                     }  
    613.                 }
    614.             }
    615.         }
    616.              
    617.         float averageWheelsAngularVelo = 0;
    618.         wheelImpulse=0;
    619.         rotationalInertia=0;
    620.         wheelTireVelo=0;
    621.         //TODO to be moved in differential class
    622.         foreach(Wheel w in poweredWheels){
    623.             averageWheelsAngularVelo += w.angularVelocity;
    624.             wheelImpulse+=w.wheelImpulse;
    625.             rotationalInertia+=w.rotationalInertia;
    626.             wheelTireVelo+=w.wheelTireVelo; // used in dashboard to calc actual wheel speed
    627.         }
    628.      
    629.         averageWheelsAngularVelo*=drivetrainFraction;
    630.         wheelTireVelo*=drivetrainFraction;
    631.      
    632.         float totalRotationalInertia=m_drivetrainInertia+rotationalInertia;
    633.              
    634.         //driveShaftSpeed=averageWheelsAngularVelo*finalDriveRatio;
    635.         // we assume the engine stalled if rpm<20
    636.         if (rpm<20 && startTorque==0) {differentialSpeed=0;wheelImpulse=0;}
    637.         clutchSpeed=differentialSpeed*ratio;
    638.      
    639.         fuel=true;
    640.         if (fuelTanks.Length!=0){
    641.             float velokmh=velo*3.6f;
    642.             // check the fuel consumption...
    643.             float speedFactor=Mathf.Clamp(velokmh,50,velokmh)/fuelConsumptionSpeed; // speed (drag) influece on consumption normalized at fuelConsumptionSpeed (130km/h) (clamped at 50km/h cause at lower speed drag is negligible)
    644.             speedFactor*=speedFactor; // drag force grows with square of speed
    645.             if (RPMAtSpeedInLastGear!=0) istantConsumption=rpm*throttle*fuelConsumptionAtCostantSpeed/(RPMAtSpeedInLastGear*secondsToCover100Km)*speedFactor;
    646.             if (velo>1) currentConsumption=(istantConsumption/velo)*100000; // velo*0.001f -> km/s ; 100/velo*0.001f -> (1/velo)*100000 seconds taken to travel 100 kms
    647.             else currentConsumption=0;
    648.             fuel=false;
    649.             foreach(FuelTank fuelTank in fuelTanks){
    650.                 if (fuelTank.currentFuel!=0) fuel=true;
    651.             }
    652.         }
    653.         torque = CalcEngineTorque(powerMultiplier,rpm)*(throttle + startThrottle)*(fuel?1:0);
    654.         frictionTorque=CalcEngineFrictionTorque(powerMultiplier,rpm);
    655.         startThrottle=0;
    656.         startTorque=0;
    657.         netTorque = torque - frictionTorque;
    658.         if (rpm<20 && startTorque==0) netTorque=0;
    659.         if (rpm<minRPM) startThrottle=1 - rpm/minRPM;
    660.      
    661.         if (startEngine==true && rpm<minRPM &&  Time.time>nextStartImpulse) {
    662.             if (duration==0) duration=Time.time + 0.1f; // duration if start impulse 0.1 secs
    663.             if (Time.time>duration) nextStartImpulse = Time.time + 0.2f; // every 0.2 secs
    664.             startThrottle=1;
    665.             startTorque=idleNetTorque;
    666.         }
    667.         else{
    668.             duration=0;
    669.         }
    670.         netTorqueImpulse = (netTorque+startTorque)*Time.deltaTime;
    671.      
    672.         if (engineAngularVelo >=maxRPM*RPM2angularVelo) {
    673.             if (revLimiterTime==0 && revLimiter==true)
    674.                 engineAngularVelo=maxRPM*RPM2angularVelo;
    675.             else
    676.                 revLimiterTriggered=true;
    677.         }
    678.         else if (engineAngularVelo <=minRPM*RPM2angularVelo && canStall==false) {
    679.             engineAngularVelo=minRPM*RPM2angularVelo;
    680.         }
    681.         else if (engineAngularVelo<0) {
    682.             engineAngularVelo=0;
    683.         }
    684.      
    685.         rpm = engineAngularVelo*angularVelo2RPM;
    686.  
    687.         if (rpm > 8300)
    688.             engineAngularVelo = maxRPM * RPM2angularVelo;
    689.  
    690.         if (ratio == 0 || clutch.GetClutchPosition()==0)
    691.         {
    692.             clutchDragImpulse=0;
    693.             differentialSpeed=averageWheelsAngularVelo;
    694.             if (autoClutch) DisengageClutch();
    695.          
    696.             // Apply torque to car body
    697.             body.AddTorque(-engineOrientation*Mathf.Min(Mathf.Abs(netTorque),2000)*Mathf.Sign(netTorque));
    698.         }
    699.         else //clutch engaged or clutch engaging
    700.         {  
    701.             clutchDragImpulse = clutch.GetDragImpulse(engineAngularVelo, clutchSpeed, m_engineInertia, totalRotationalInertia, ratio,wheelImpulse, netTorqueImpulse);
    702.         }
    703.  
    704.         engineAngularVelo += (netTorqueImpulse - clutchDragImpulse)/(m_engineInertia);
    705.         differentialSpeed += (wheelImpulse + clutchDragImpulse*ratio)/(totalRotationalInertia);
    706.         if (float.IsNaN(differentialSpeed)) differentialSpeed=0; // to avoid NAN errors with zero powered wheels
    707.         float deltaDifferentialSpeed=differentialSpeed - averageWheelsAngularVelo;
    708.      
    709.         slipRatio = 0;
    710.         idealSlipRatio=0;
    711.         float maxAngVel=maxRPM/(Mathf.Abs(ratio)*angularVelo2RPM);
    712.         foreach(Wheel w in poweredWheels)
    713.         {
    714.             if (revLimiter==true && w.angularVelocity>maxAngVel) w.angularVelocity = maxAngVel;
    715.             lockingTorqueImpulse = (averageWheelsAngularVelo - w.angularVelocity)*differentialLockCoefficient*Time.deltaTime;
    716.             w.drivetrainInertia =  m_drivetrainInertia*ratio*ratio*drivetrainFraction*clutch.GetClutchPosition(); // should be clutch.GetClutchPosition()*(engineInertia + gearInertia[curGear])*Sqr(ratio)
    717.             w.angularVelocity +=deltaDifferentialSpeed;
    718.             w.lockingTorqueImpulse = lockingTorqueImpulse;
    719.             slipRatio += w.slipRatio*drivetrainFraction;
    720.             idealSlipRatio += w.idealSlipRatio*drivetrainFraction;
    721.         }
    722.      
    723.         if (revLimiter==true){
    724.             if (revLimiterTriggered==true) {
    725.                 revLimiterReleased=false;
    726.                 timer+=Time.deltaTime;
    727.                 if (timer >=revLimiterTime) {
    728.                     timer=0;
    729.                     revLimiterTriggered=false;
    730.                     revLimiterReleased=true;
    731.                 }
    732.             }
    733.             else
    734.                 revLimiterReleased=false;
    735.         }
    736.         else{
    737.             revLimiterTriggered=false;
    738.             revLimiterReleased=false;
    739.         }
    740.     }
    741.  
    742.     void DoGearShifting(){
    743.  
    744.          //from 1st reverse to neutral or from neutral to 1st gear
    745.         if (shiftImmediately==true)
    746.         {
    747.             gear=nextGear;
    748.             if (nextGear !=neutral ) shiftTriggered=true; // in order to trigger the shift sound
    749.             changingGear=false;
    750.         }
    751.         else
    752.         {
    753.             if (throttle<=idlethrottle*1.1f || automatic==false) {
    754.                 if (lastShiftTime==0) lastShiftTime = Time.time;
    755.                 shiftTimer = (Time.time - lastShiftTime)/shiftTime;
    756.              
    757.                 // disengage clutch
    758.                 if (shiftTimer <1f) DisengageClutch();
    759.              
    760.                 // put in neutral
    761.                 if (shiftTimer >=0.33f) gear=neutral;
    762.              
    763.                 // select next gear and play sound
    764.                 if (shiftTimer >=0.66f) {
    765.                     gear=nextGear;
    766.                     if (soundPlayed==false) shiftTriggered=true;
    767.                 }
    768.              
    769.                 // engage clutch
    770.                 if (shiftTimer >=1) {
    771.                     if (rpm < engageRPM || carController.clutchInput!=0) changingGear=false;
    772.                 }
    773.             }
    774.         }
    775.     }
    776.  
    777.     void EngageClutch()
    778.     {
    779.         engaging=true;
    780.         int sign=1;
    781.         if (rpm<maxPowerRPM/2) {
    782.             clutchEngageSpeed=Mathf.Clamp(clutchDragImpulse/netTorqueImpulse, Time.fixedDeltaTime*2,1f);
    783.             if (clutchDragImpulse < netTorqueImpulse || netTorque<1) sign=1;
    784.             else sign=-1;
    785.         }
    786.         else{
    787.             clutchEngageSpeed=0.1f;
    788.         }
    789.      
    790.         if (clutchEngageSpeed!=0) clutchPosition += (Time.deltaTime/clutchEngageSpeed)*sign*(throttle>idlethrottle?throttle:1);
    791.         clutchPosition=Mathf.Clamp01(clutchPosition);
    792.         clutch.SetClutchPosition(clutchPosition);
    793.         if (clutchPosition==1) {engaging=false;changingGear=false;}
    794.     }      
    795.  
    796.     void DisengageClutch()
    797.     {      
    798.         // TODO gradually disengage the clutch?
    799.         clutchPosition=0;
    800.         clutch.SetClutchPosition(clutchPosition);
    801.     }
    802.  
    803.     public bool OnGround(){
    804.         bool onGround=false;
    805.         if (poweredWheels!=null){
    806.             foreach(Wheel w in poweredWheels){
    807.                 onGround=w.onGroundDown;
    808.                 if (onGround==true) break;
    809.             }
    810.         }
    811.         return onGround;
    812.     }
    813.  
    814.     public void Shift(int m_gear)
    815.     {
    816.         if (m_gear <= gearRatios.Length - 1 && m_gear >= 0 && changingGear==false){
    817.             if (autoClutch==false && clutch.GetClutchPosition()!=0){
    818.                 // scratching noise?
    819.             }
    820.             else{
    821.                 soundPlayed=false;
    822.                 changingGear=true;
    823.                 nextGear=m_gear;
    824.                 if (nextGear==neutral || (gear==neutral && nextGear==first)  || (gear==neutral && nextGear==firstReverse) || shifter==true) { //from 1st reverse to neutral or from neutral to 1st gear
    825.                     shiftImmediately=true;
    826.                 }          
    827.                 else{
    828.                     shiftImmediately=false;
    829.                 }
    830.             }
    831.         }
    832.     }  
    833. }
    834.  
    835. public class Clutch {
    836.     public float maxTorque;      
    837.     public float speedDiff;
    838.     float clutch_position;
    839.     float impulseLimit;
    840.      
    841.     public float GetDragImpulse(float engine_speed, float drive_speed, float engineInertia, float totalRotationalInertia, float ratio, float wheelImpulse, float engineTorqueImpulse){
    842.      
    843.         float totalRotationalInertiaR=totalRotationalInertia/(ratio*ratio);
    844.         impulseLimit=clutch_position*maxTorque*Time.deltaTime;
    845.         speedDiff=engine_speed - drive_speed;
    846.      
    847.         float a=engineInertia*totalRotationalInertiaR*speedDiff;
    848.         float b=engineInertia*(wheelImpulse/ratio);
    849.         float c=totalRotationalInertiaR*engineTorqueImpulse;
    850.      
    851.         float lambda=(a - b + c)/(engineInertia + totalRotationalInertiaR);
    852.         lambda = Mathf.Clamp(lambda, -impulseLimit, impulseLimit);      
    853.         return lambda;
    854.     }
    855.  
    856.     //set the clutch engagement, where 1.0 is fully engaged
    857.     public void SetClutchPosition(float value){
    858.         clutch_position = value;
    859.     }
    860.  
    861.     public float GetClutchPosition() {
    862.         return clutch_position;
    863.     }  
    864. }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    It's not really a Unity thing... you're probably just dividing by zero.

    Look through your code for divides... I started but that's a PILE of code, so I stopped looking at line 296 where you divide by fuelConsumptionSpeed.

    But almost certainly one of your divisors is zero.

    If that's not it, try some others, and ultimately, well...

    Time to start debugging! Here is how you can begin your exciting new debugging adventures:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the names of the GameObjects or Components involved?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer for iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    If your problem is with OnCollision-type functions, print the name of what is passed in!

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    "When in doubt, print it out!(tm)" - Kurt Dekker (and many others)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    What have you tried so far? Did you check the console for errors?

    This should be straightforward: if the engine is stalled, and user tries to restart the engine, check what the calculation does at this point in time. You can either log all values involved in the calculations or just set a breakpoint (possibly with a condition) and attach the debugger, then step through the code.

    Most likely you're looking at either a division by zero though this should throw an exception unless you've wrapped the affected code in try/catch exception handling. Or the value explodes, it may go out of the allowed range of values, or you may have some other value that's NaN take part in the calculation of RPM.

    Observations skimming over the code:
    differentialSpeed has a NaN check - perhaps differentialSpeed is still NaN at the moment you calculate rpm (order of operations) before it's reset to 0?

    Since differentialSpeed apparently can be NaN I would suggest looking into why it can become NaN in the first place. I bet that totalRotationalInertia can be 0 (line 705) and thus you get a div by zero there if left unchecked:
    differentialSpeed += (wheelImpulse + clutchDragImpulse*ratio)/(totalRotationalInertia);

    Though that seems to indicate you're only catching the NaN after the fact, whereas you should have done:
    if (totalRotationalInertia == 0f) differentialSpeed = 0f;

    This prevents the div by zero exception from occuring in the first place and you can remove the NaN check that follows.

    Just FYI you also set clutchSpeed to 0 when rpm<20 by this calculation just after differentialSpeed is set to 0:
    clutchSpeed=differentialSpeed*ratio;

    Doesn't seem to be related but may not be what you want. At least from my "i just want cars to work" perspective it still seems odd to have an "infinitely fast" clutch speed.
     
    Last edited: Aug 6, 2023
    Kurt-Dekker likes this.