Search Unity

Resolved How i make car engine rev limit

Discussion in 'Physics' started by driftnumata, Jun 2, 2023.

  1. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    Hello, I'm making car controller.
    But,the engine rpm continues to rotate indefinitely. So, how can you limit the rpm of the engine so that it does not spin any more?

    ↓↓This is my project's script.

    Code (CSharp):
    1. engineRPM = Mathf.Lerp(engineRPM, Mathf.Max(idleRPM + wheelsRPM * gearRatios[currentGear] * differentialRatio), Time.deltaTime * 3f);
    2.  
    3.         if (0 < KPH)
    4.         {
    5.             currentTorque = (engineTorqueCurve.Evaluate(engineRPM / revLimitRPM) * maxEngineTorque) * gearRatios[currentGear] * differentialRatio * 0.7f / WheelColliders[3].radius * throttle;
    6.         }
    7.         else
    8.         {
    9.             if (!isBraking)
    10.             {
    11.                 currentTorque = engineTorqueCurve.Evaluate(engineRPM / revLimitRPM) * -reversGearRatio * differentialRatio * throttle * maxEngineTorque;
    12.             }
    13.         }
     
  2. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    700
    I think your if statement does not do what you think it does.

    The braking branch only ever evaluates when the speed is 0.

    Also, the engineRPM stays at least at idleRPM or above
     
  3. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    Fast, I tried this ↓
    Code (CSharp):
    1. If(revLimitRPM <  engineRPM)
    2. {
    3. engineRPM = engineRPM * 0.8f;
    4. }
    Second, I tried this ↓
    Code (CSharp):
    1. if(revLimitRPM < engnieRPM)
    2. {
    3. throttle = 0.0f;
    4. }
    But, These were not worked.
    Maybe, I think I need to wait a second when the throttle is limited. But I don't know how to try this....
     
  4. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    700
    Your limiter won't work because the Physics or WheelRPM will likely update the RPM again. You need to build a mental model of your program flow and know which values drive which.

    Since you seem to go with a very physical/engineer-y approach, remember that FixedUpdate is a single integration step of your Physics simulation, and Time.deltaTime is your "dt" in the integral you're integrating. (side note - in Physics, and always use FixedUpdate, not Update, or your results will be ever so unpredictable. Time.deltaTime and Time.fixedDeltaTime are identical inside a FixedUpdate method)

    Everything inside that FixedUpdate physics step should be treated as a small, iterative change, not an absolute setting of a value.

    In actuality, you seem to drive and the value in every (Fixed?)Update correctly at first, using dt as a hyperbolic factor (this has framerate issues, your "3f" factor will compound differently at higher framerates - per frame it is roughly correct, but the integral - change over a longer time - is going to deviate).

    Then you leaving no breathing room for the physics simulation, resolving your integral to just a very stiff, unrealistic linear function that isn't even differentiable with the limiter:

    Code (CSharp):
    1. engineRPM = Mathf.Lerp(engineRPM, Mathf.Max(idleRPM + wheelsRPM * gearRatios[currentGear] * differentialRatio), Time.deltaTime * 3f);
    2. //Later: you immediately multiply it by 0.8, instantly hard-cutting it 20%, even if it's only 0.0001% above rev-limit
    3.  
    (you could resolve this by using k = Mathf.pow(3f/referenceFrameRate, Time.deltaTime * referenceFramerate) in the LERP, but you can't fix the limiter this way.)



    Instead, I suggest using a pair of smooth limiters for the combined term. Perhaps something like this (untested):


    Code (CSharp):
    1. //Put these in the MonoBehaviour class as fields - adjust them up or down for faster or slower responses.
    2. const wheelsRPMresponsiveness = 0.5f;
    3. const throttleRPMresponsiveness = 1.0f;
    4.  
    5. private float _wheelRPMChange;
    6. private float _throttleRPMChange;
    7.  
    8.  
    9. private void FixedUpdate()
    10. {
    11.     //Let's split the interpolation and calculation for clarity. We interpolate using SmoothDamp further down.
    12.  
    13.     //<your input / throttle code goes here, just get a hard goal value, nothing damped or smoothed>
    14.      var engine_throttle_rpm_goal = ...;
    15.  
    16.      //get the target RPM from the ground.
    17.     var wheels_goal_rpm = Mathf.Max(idleRPM + wheelsRPM * gearRatios[currentGear] * differentialRatio);
    18.  
    19.     //Treating revLimit as a hard limit, i.e. the engine can't physically torque higher. You should also limit the goal RPM from the throttle. (just clamp the input value)
    20.     wheels_goal_rpm = Mathf.Clamp(wheels_goal_rpm, -revLimitRPM, revLimitRPM);
    21.  
    22.  
    23.     //Finally, apply smooth damping, combining both factors into the actual RPM.
    24.  
    25.     //Apply Wheel RPM. We do it first so the wheels support the throttle.
    26.     engineRPM = Mathf.SmoothDamp(engineRPM, engine_wheels_rpm_goal, ref _wheelRPMChange, wheelsRPMresponsiveness);
    27.  
    28.     //Now apply Throttle RPM on top.
    29.     engineRPM = Mathf.SmoothDamp(engineRPM, engine_throttle_rpm_goal, ref _throttleRPMChange, throttleRPMresponsiveness);
    30.  
    31.   //Calculate and apply torque as needed from engineRPM, brakes, etc. now. Maybe look it up from a curve depending on current RPM.
    32.   // .....
    33.   // .....
    34.   // .....
    35. }
     
    Last edited: Jun 8, 2023
    driftnumata likes this.
  5. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    Thank you for your great answer! I would like to try it soon.
     
  6. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    Thanks to this, we were able to create a rev limit on the engine, but the car continued to accelerate indefinitely. Is this an air resistance problem?
     
  7. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    700
    You should set an appropriate Linear and Angular Drag value on the car's main rigid body. (usually higher if you are seeing indefinite acceleration)

    It could also be that your torque calculation doesn't take into account that the engine will only place a fraction of that torque on the wheels, because they already have a lot of intertial and rotational moment themselves.
     
  8. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    I'm gonna check out the engine problem. Thank you.
     
  9. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,433
    Usually, you create a power curve (like, use an AnimationCurve to describe RPM vs acceleration). That curve should drop gradually to zero as the RPM gets higher, but drop off entirely at the redline. Evaluate the curve with the RPM and get the amount of additional force you want to apply to the vehicle. If the RPM goes above redline, you can do whatever special effects you want for that, but you'll get no more oomph to accelerate. Combined with drag and slip forces, you'll find it challenging to even keep that top speed at the redline, nevermind go faster.
     
    Unifikation likes this.
  10. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    Yes, I made engineTorqueCurve. I wanna make simply, realistic engine.. But, I still have problems.
     
  11. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    I have a problem. Even if gear is 1st or 5th, the engine does work for very high speed. Is that my program miss? If that is my mistake, maybe I was mistaken here " engine_throttle_rpm_goal" I don't know what I need in there? I need an example. Can you give me an example?
     
  12. driftnumata

    driftnumata

    Joined:
    May 31, 2023
    Posts:
    29
    After that, I tried other methods and it worked. Thank you very much for explaining various things in an easy-to-understand manner.

    Final script↓↓
    Code (CSharp):
    1. private void Engine()
    2.     {
    3.         WheelsRPM();
    4.         LerpEngine();
    5.  
    6.         throttle = vertical > 0 ? vertical : wheelsRPM <= 1 ? vertical : 0;
    7.  
    8.         if (engineRPM >= revLimitRPM)
    9.         {
    10.             engineLerp(revLimitRPM - 1000);
    11.         }
    12.         if (!isEngineLimit)
    13.         {
    14.             engineRPM = Mathf.Lerp(engineRPM, Mathf.Max(idleRPM + wheelsRPM * gearRatios[currentGear] * differentialRatio), Time.deltaTime * 3f);
    15.             currentTorque = (engineTorqueCurve.Evaluate(engineRPM / revLimitRPM) * maxEngineTorque) * gearRatios[currentGear] * differentialRatio * 0.7f / WheelColliders[3].radius * throttle;
    16.         }
    17.  
    18.         engineLoad = Mathf.Lerp(engineLoad, vertical - ((engineRPM - 1000) / maxEngineRPM), Time.deltaTime * 3f);
    19.     }
    20.  
    21.     private void engineLerp(float value)
    22.     {
    23.         isEngineLimit = true;
    24.         engineLerpValue = value;
    25.     }
    26.  
    27.     public void LerpEngine()
    28.     {
    29.         if (isEngineLimit)
    30.         {
    31.             currentTorque = 0;
    32.             engineRPM = Mathf.Lerp(engineRPM, engineLerpValue, 20 * Time.deltaTime);
    33.             isEngineLimit = engineRPM <= engineLerpValue + 100 ? false : true;
    34.         }
    35.     }