Another Quaternion.lerp Question

Hey, thanks for checking out my question. I am trying to do gun recoil with my camera so I am using quaternion.lerp, (not slerp because I want it to be linear), and when the player shoots I have the camera lerp to a quaternion that is the cameras local rotation being multiplied by a recoil value. This seems to work fine but I want the camera to come back to the rotation in which the player started shooting. So I also store the cameras local rotation when the player starts shooting, and then when they are done shooting I call a function to lerp the camera back to its initial rotation. I am having a problem where there seems to be an offset in which the camera is stopping 0.4 units before the initial rotation. This offset seems to be inconsistent as well but its around 0.4 units every time. Not sure why it would not be the same value every time or why it stops short in the first place. Ive been messing with it for a week now and still have not found what I am doing wrong. I will provide my code down below and thank you for the help in advance!

void Update () {
        if (Input.GetKeyUp(KeyCode.Mouse0) && timeShooting > 0 || bulletsInMag <= 0 && Input.GetKeyUp(KeyCode.Mouse0)) {
            StartCoroutine(RecoverRecoil());
        }
        if (Input.GetKeyDown(KeyCode.Mouse0)) {
            initialShotRot = cam.transform.localRotation;
            recoilFinishRot = initialShotRot * Quaternion.Euler(maxRecoil, 0, 0);
        }
	}

//Technically does not need to be in a Ienumerator currently 
IEnumerator recoiling () {
        timeShooting += 1;
        float recoil = 1f;
        while (recoil > 0) {
            cam.transform.localRotation = Quaternion.Lerp(cam.transform.localRotation, recoilFinishRot, 0.1f);
            recoil -= 1f;
            if (Input.GetKeyUp(KeyCode.Mouse0) || bulletsInMag <= 0) {
               yield break;
            }
            yield return null;
        }
    }
    
    IEnumerator RecoverRecoil() {
        while (timeShooting > 0f) {
            cam.transform.localRotation = Quaternion.Lerp(cam.transform.localRotation, initialShotRot , 1f); //its 1 for testing purposes
            timeShooting -= 0.25f;
            yield return null;
        }
        timeShooting = 0;
    }

Hi,

where exactly is recoiling() called? In the code you shared it is not shown. I ask this, because I think what probably causes the bug is recoiling() and RecoverRecoil() executing in the same frame, but not always in the same order. Another possible source of the bug is that the global rotation of the camera (camera.transform.rotation) is modified somewhere else, e.g. in a mouse look script.

If you don’t mind some advice, some other things that could cause bugs:

  • as rightdroid commented, in case the player shoots again before RecoverRecoil() finishes, you will start another Lerp to an angle between. To avoid this, you can store the result of StartCoroutine() in a variable type Coroutine and before starting RecoverRecoil(), if it is not null, call StopCoroutine().

  • you check mouse button down and up separately, and store rotation to recover to on button down. What if the player holds the button and turns 180 degrees, then releases? Instead, I would store the rotation to recover to just before stating RecoverRecoil() coroutine

  • you don’t have to store initialShotRot as a variable in the class, you can just pass it to the RecoverRecoil() coroutine as parameter (less variables in class, less possibility of reusing an outdated value)

Finally, I’m not sure it is a good idea from gameplay point of view to take away control from the player and automatically compensate for recoil. In most games the player has to manually recover from recoil and it adds to immersion (you feel the weight of the shot more). Of course, it could be an important element of your game, in which case just ignore this comment :slight_smile: