Problem with simulating input

Hi all
I’m trying to simulate a gear shift input for my CarAIController. The idea is I have a CarController script that handles physics and inside there is ShiftGear(bool ShiftUpInput, bool ShiftDownInput)

public void shiftGear(bool shiftUpInput, bool shiftDownInput)
    {
        //if current gear less than 0 then it's reverse gear
        //set gear ratio to reverse ratio
        //0 is neutral
        if (currentGear < 0)
        {
            currentGearRatio = -reverseRatio;
        }
        //else look up gear ratio in the array
        else
        {
            currentGearRatio = gearRatio[currentGear];
        }
        switch (canShift)
        {
            case false:
                shiftUpInput = false;
                shiftDownInput = false;
                break;
            case true:
                shiftUpInput = shiftUpInput;
                shiftDownInput = shiftDownInput;
                break;
        }
        //shift gear base on transmission type
        switch (myTransmissionType)
        {
            //if semi auto with automatic clutch
            //just press shift up and gear will shift after delay time
            case TransmissionType.SemiAuto:
                if (shiftUpInput && currentGear < gearRatio.Length - 1)
                {
                    //only auto shift within delay time on gear 1 above
                    //as from 0 to gear 1 engine has to slowly drive the wheel and apply clutch
                    if (currentGear > 1)
                    {
                       
                        currentGear++;
                    }
                    else currentGear++;
                    GetComponent<CarAudio>().PlayShiftSound();
                    StartCoroutine(emitBackFire());
                }
                if (shiftDownInput && currentGear >= 0)
                {
                    StartCoroutine(ShiftDownAfterTime(shiftTime));
                    currentGear--;
                    GetComponent<CarAudio>().PlayShiftSound();
                    StartCoroutine(emitBackFire());
                }
                break;
            //if manual with manual clutch
            //has to press clutch in order to shift
            //use clutch input > 0.90f to compensate for deadzone
            case TransmissionType.Manual:
                if ((shiftUpInput && clutchInput > 0.90f) && currentGear < gearRatio.Length - 1)
                {
                    currentGear++;
                    //currentEngineRPM = Mathf.Clamp(currentEngineRPM, minRPM, maxRPM);
                    GetComponent<CarAudio>().PlayShiftSound();
                    StartCoroutine(emitBackFire());
                }
                if ((shiftDownInput&& clutchInput > 0.90f) && currentGear > 0)
                {
                    currentGear--;
                    //currentEngineRPM = Mathf.Clamp(currentEngineRPM, minRPM, maxRPM);
                    GetComponent<CarAudio>().PlayShiftSound();
                    StartCoroutine(emitBackFire());
                }
                break;
        }
       
    }

So this function will receive input from another script CaruserController. This script will actually receive inputs from player and then feed that to the shifting function above in CarController.

bool shiftUp = Input.GetButtonDown("ShiftUp");
        bool shiftDown = Input.GetButtonDown("ShiftDown");
        myCarController.shiftGear(shiftUp, shiftDown);

Now I want to simulate the same input feeding method to carAIControl. But the problem is that when shifting in manual, the clutch has to be pressed, the RPM takes a while to move back. During that time, the check RPM for shifting method still run and the RPM still remain for a short time during the frame. So that the AI just shift furiously to the highest gear. It’s fine for semi auto since it is defined in CarController that the RPM will move back within a certain time, and the AI only need to wait for that certain time. But for manual there is no defined time, so there is no way to check
These are the code I have so far for the AI:

public IEnumerator ShiftUp()
    {
       
        switch (m_CarController.myTransmissionType)
        {
           
            case TransmissionType.Manual:
                //release throttle, press clutch
                throttle = 0f;
                clutch = 1f;
                //m_CarController.Move(steer, 0f, 1f, 0f);
                //"press" shift up
                m_CarController.shiftGear(true, false);
                yield return null;
                m_CarController.shiftGear(false, false);
                //yield return new WaitForSeconds(shiftTimeDifficulty);
                clutch = 0f;
                yield return new WaitForSeconds(1f);
                break;
            case TransmissionType.SemiAuto:
                m_CarController.shiftGear(true, false);
                //yield return new WaitForEndOfFrame();
                //yield return 0;
                m_CarController.shiftGear(false, false);
                //yield return new WaitForEndOfFrame();
                yield return new WaitForSeconds(1f);
                break;
        }
       
       
    }
    //coroutin for shifting down
    public IEnumerator ShiftDown()
    {
       
        //2 cases, manual clutch and semi auto
        switch (m_CarController.myTransmissionType)
        {
            //if manual, press clutch then shift
            //process takes time depends on difficulty level
            case TransmissionType.Manual:
                //release throttle, press clutch
                throttle = 0f;
                clutch = 1f;
                //"press" shift down by setting input to true for 1 frame then false
                m_CarController.shiftGear(false, true);
                yield return null;
                m_CarController.shiftGear(false, false);
                //wait for difficulty time then restore throttle and release clutch
                //yield return new WaitForSeconds(shiftTimeDifficulty);
                clutch = 0f;
                yield return new WaitForSeconds(1f);
                break;
               
                //if semi auto, just press shift gear
                //since clutch and throttle are handled automatically by carcontroller
            case TransmissionType.SemiAuto:
                m_CarController.shiftGear(false, true);
                //yield return new WaitForEndOfFrame();
               // yield return 0;
                m_CarController.shiftGear(false, false);
                //yield return new WaitForEndOfFrame();
                yield return new WaitForSeconds(1f);
                break;
        }
        //yield return new WaitForSeconds(1f);

    }
    //handle gear changing
    public void GearChange()
    {
        int currentGear = m_CarController.currentGear;
        float currentRPM = m_CarController.currentEngineRPM;
        //shift up when reach upper limit of power band
        float shiftUpRPM;
        float shiftDownRPM;
        //if launch from neutral gear then shift up when RMP is at max torque RPM
        if (currentGear == 0 )
        {
            //throttle = Mathf.Clamp((m_CarController.maxTorqueRPM - currentRPM) * AccelSensitivity, 0, 1);
            shiftUpRPM = m_CarController.maxTorqueRPM;
            //shiftDownRPM = m_CarController.maxTorqueRPM;
        }
        else
        {
            shiftUpRPM = m_CarController.powerBand[1];
            shiftDownRPM = m_CarController.powerBand[1] / m_CarController.gearRatio[currentGear] / m_CarController.gearRatio[currentGear - 1];
        }
        //shift down when reach lower limit of power band
        shiftDownRPM = m_CarController.powerBand[0];
        //shiftDownRPM = m_CarController.powerBand[1] / m_CarController.gearRatio[currentGear] / m_CarController.gearRatio[currentGear - 1]; ;
        //if (currentGear > 0)
        //{
        //    shiftDownRPM = m_CarController.powerBand[1] / (m_CarController.gearRatio[currentGear] / m_CarController.gearRatio[currentGear - 1]);
        //}
        //else
        //{
        //    shiftDownRPM = m_CarController.maxTorqueRPM;
        //}
        //only shift when car control is allowed shifting and AI is driving
        if (m_CarController.canShift && Driving)
        {
            if (currentGear < m_CarController.gearRatio.Length - 1 && currentRPM >= shiftUpRPM)
            {
                StartCoroutine(ShiftUp());
            }
            if (currentGear > 1 && currentRPM <= shiftDownRPM)
            {
                StartCoroutine(ShiftDown());
            }
        }
       
    }

Please help. Thank you very much

Have a look at the Command Design Pattern. I am currently using this to encapsulate my actions from the player’s input, so I can easily execute them via code (I use an abstract class instead of an interface, to not duplicate some information needed in all commands, like the actor that should execute the command, but other than that it is really similar to his examples).

As a sidenote: Why not continue in your previous topic on your Car AI?

1 Like

Thank you for your reply and I’m so sorry that I’ve created a new topic without realizing there was already a similar topic.
I had some luck with using coroutines and the input is received correctly by the controller.
But now my problem is that when the AI “press” shift gear, the engine RPM has to take a while to jump back, especially when the car is having wheel spin, it would take more than a few seconds. During that time, the check function ShiftGear() is still checking the condition for shifting up, which is when the RPM is at shift up RPM. Because of that, the AI car would call the coroutine shiftUp() again, so it shifts furiously to the highest gear, then when the RPM moves back and stabilize, it shift down and then up a few times then reaches the appropriate gear.
To me it sounds like the mind of a impatient new driver and kind of logically, as you would try and fail until you get the right value. But this is not what we want. Can you help me structure the function or just guide me to the right logic?
Below is the code for ShiftGear() and 2 coroutines ShiftUp() and ShiftDown(). ShiftGear() is called in Update.
I’m thinking about having a ShiftGear() coroutine instead of a function that gets called in Update() and start the coroutine at start(). That way maybe I can implement some kind of delay between checks.

//coroutine for shifting up
    public IEnumerator ShiftUp()
    {
        switch (m_CarController.myTransmissionType)
        {
            case TransmissionType.Manual:
                //release throttle, press clutch
                throttle = 0f;
                yield return 0;
                clutch = 1f;
                yield return 0;
                //"press" shift up               
                m_CarController.shiftGear(true, false);
                m_CarController.shiftGear(false, false);
                clutch = 0f;
                break;
            case TransmissionType.SemiAuto:
                m_CarController.shiftGear(true, false);
                m_CarController.shiftGear(false, false);
                yield return new WaitForSeconds(m_CarController.shiftTime);
                break;
        }
    }
   

    //coroutin for shifting down
    public IEnumerator ShiftDown()
    {

        //2 cases, manual clutch and semi auto
        switch (m_CarController.myTransmissionType)
        {
            //if manual, press clutch then shift
            //process takes time depends on difficulty level
            case TransmissionType.Manual:
                //release throttle, press clutch
                throttle = 0f;
                //yield return null;
                clutch = 1f;
                yield return null;
                //"press" shift down by setting input to true for 1 frame then false
                m_CarController.shiftGear(false, true);
                m_CarController.shiftGear(false, false);
                clutch = 0f;
            
                break;

            //if semi auto, just press shift gear
            //since clutch and throttle are handled automatically by carcontroller
            case TransmissionType.SemiAuto:
                m_CarController.shiftGear(false, true);
                m_CarController.shiftGear(false, false);
                yield return new WaitForSeconds(m_CarController.shiftTime);
                break;
        }
    }
//handle gear changing
    public void GearChange()
    {
        int currentGear = m_CarController.currentGear;
        float currentRPM = m_CarController.currentEngineRPM;
        float[] gearRatios = m_CarController.gearRatio;
        float finalDrive = m_CarController.finalDriveRear;
        float currentWheelRPM = m_CarController.currentWheelRPM;
        float shiftUpRPM;
        float shiftDownRPM;
        switch (currentGear)
        {
            case 0:
                shiftUpRPM = m_CarController.maxTorqueRPM;
                shiftDownRPM = m_CarController.maxTorqueRPM;
                //StartCoroutine(LaunchFromNeutral());
                break;
            default:
                shiftUpRPM = m_CarController.powerBand[1];
                shiftDownRPM = m_CarController.powerBand[0];
                break;
        }
        //only shift when car control is allowed shifting and AI is driving
        if (m_CarController.canShift && Driving)
        {
            if (currentGear < m_CarController.gearRatio.Length - 1 && currentRPM >= shiftUpRPM)
            {
                //UpShift();
                StartCoroutine(ShiftUp());
               
            }
            if (currentGear > 0 && currentRPM <= shiftDownRPM)
            {
                //DownShift();
                StartCoroutine(ShiftDown());
            }
        }
       
    }

Bump. Any idea?

Bump again