Shooting System

Hello,
I am currently working on my game and I am working on the gun mechanics now. I have started to implement recoil and it works fine but the only issue is that when ever I press the shoot button the recoil will pull my gun upwards which is what I want but when I start to look down it goes jittery and the camera also starts to jitter a bit depending on how far down I look.

So I wanted to know how I could fix this because what I am trying to achieve is that when ever you look down it will start to pull up the gun but I does not do this it only does this when I shoot and I do not try and look down. If I look up it works but when I look down it will start to jitter things which I do not want.

I have put my code below to show what I am currently working with

Recoil:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Recoil : MonoBehaviour
{
    private Vector3 currentRotation;
    private Vector3 targetRotation;

    [Header("HipFire Recoil")]
    [SerializeField] private float minRecoilX;
    [SerializeField] private float maxRecoilX;
    [SerializeField] private float recoilY;
    [SerializeField] private float recoilZ;

    [Header("Recoil Settings")]
    [SerializeField] private float snappiness;
    [SerializeField] private float returnSpeed;

    private void Update()
    {
        targetRotation = Vector3.Lerp(targetRotation, Vector3.zero, returnSpeed * Time.deltaTime);
        currentRotation = Vector3.Slerp(currentRotation, targetRotation, snappiness * Time.fixedDeltaTime);

        transform.localRotation = Quaternion.Euler(currentRotation);
    }

    public void RecoilFire()
    {
        targetRotation += new Vector3(transform.localRotation.x + maxRecoilX, Random.Range(-recoilY, recoilY), Random.Range(-recoilZ, recoilZ));
    }
}

Gun Shooting Script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.InputSystem;

public enum FireModes { Single, Auto}
public class GunShoot : MonoBehaviour
{
    InputMaster controls;

    [Header("Refrences")]
    [SerializeField] private Animator animator;
    [SerializeField] private Recoil recoil;
    [SerializeField] private Camera fpsCamera;

    [Header("Shooting Settings")]
    [SerializeField] private int currentAmmo;
    [SerializeField] private int maxAmmo;
    [SerializeField] private int magCapacity;
    [Space]
    [SerializeField] private float nextTimeToFire;
    [SerializeField] private float fireRate;

    [Header("Aiming")]
    [SerializeField] private float aimingTime;
    [SerializeField] private Vector3 normalPosition;
    [SerializeField] private Vector3 aimingPosition;

    [Header("Reloading Settings")]
    [SerializeField] private float reloadTime;
    [SerializeField] private string reloadAnimation;

    [Header("Firing Mode")]
    [SerializeField] private FireModes _fireModes;

    [Header("Gun Bools")]
    [SerializeField] private bool canShoot;
    [SerializeField] private bool autoShooting;
    public bool aiming;
    [Space]
    [SerializeField] private bool canReload;
    [SerializeField] private bool reloading;
    [SerializeField] private bool canRecoilX;


    private void Awake()
    {
        controls = new InputMaster();

        #region Input Checking

        controls.Gun.AutoFire.performed += _ => autoShooting = true;
        controls.Gun.AutoFire.canceled += _ => autoShooting = false;

        controls.Gun.Aiming.performed += _ => aiming = true;
        controls.Gun.Aiming.canceled += _ => aiming = false;
        #endregion
    }

    private void Update()
    {
        InputCheck();
        ReloadInput();
        SettingBools();
        CycleFireModes();
        Aiming();
    }

    #region Cycling Fire Modes
    private void CycleFireModes()
    {
        if (controls.Gun.CycleFireModes.triggered)
        {
            _fireModes = ((int)_fireModes < 1) ? _fireModes + 1 : 0;
        }
    }
    #endregion

    #region Setting Bools
    private void SettingBools()
    {
        if (currentAmmo > 0 && !reloading) { canShoot = true; }
        else if (currentAmmo <= 0 || reloading) { canShoot = false; }


        if (currentAmmo < maxAmmo && magCapacity > 0 && !reloading) { canReload = true; }
        else if (currentAmmo >= maxAmmo || magCapacity <= 0 || reloading) { canReload = false; }
    }

    #endregion

    #region Input Check
    private void InputCheck()
    {
        //Single Firing
        if(controls.Gun.SingleFire.triggered && _fireModes == FireModes.Single && canShoot)
        {
            Shoot();
        }

        //Auto Firing
        if(autoShooting && _fireModes == FireModes.Auto && Time.time >= nextTimeToFire && canShoot)
        {
            nextTimeToFire = Time.time + 1f / fireRate;
            Shoot();
        }
    }
    #endregion

    #region Shooting
    private void Shoot()
    {
        currentAmmo--;

        recoil.RecoilFire();
    }
    #endregion

    #region Reloading
    private void ReloadInput()
    {
        if(controls.Gun.Reload.triggered && canReload)
        {
            StartCoroutine(Reloading());
        }
    }

    IEnumerator Reloading()
    {
        Debug.Log("Reloading!");
        reloading = true;
        animator.SetBool(reloadAnimation, true);

        yield return new WaitForSeconds(reloadTime);

        int reloadAmount = maxAmmo - currentAmmo;
        if(magCapacity > maxAmmo)
        {
            currentAmmo += reloadAmount;
            magCapacity -= reloadAmount;
        }else if(magCapacity <= maxAmmo)
        {
            currentAmmo = magCapacity;
            magCapacity = 0;
        }

        reloading = false;
        animator.SetBool(reloadAnimation, false);
        Debug.Log("Done Reloading!");
    }

    #endregion

    #region Aiming
    private void Aiming()
    {
        if (aiming)
        {
            transform.localPosition = Vector3.Lerp(transform.localPosition, aimingPosition, Time.deltaTime * aimingTime);
        }
        else
        {
            transform.localPosition = Vector3.Lerp(transform.localPosition, normalPosition, Time.deltaTime * aimingTime);
        }
    }

    #endregion

    #region Enabling and Disabling
    private void OnEnable()
    {
        controls.Enable();
    }

    private void OnDisable()
    {
        controls.Disable();
    }
    #endregion
}

I think the problem is within the recoil script. You basically have Update constantly reading/changing, and you want void Recoil() to change positions and rotations as well. You need to setup a better system that recoil script knows the difference to when to set the new recoil, and when to smooth transition back to(I guess origin?). Enums are quite efficient at doing such things(I mean you could use a bunch of bools, but talk about the headache), lol… But I’m thinking along the lines of:

public enum GunStates
{
     Resting,
     Shooting,
     Recoiling,
     Returning
}
public GunStates gunStates;

if (gunStates == GunStates.Recoiling)
{ Recoil(); } // recoil would then need to change state to return when position reached

if (gunStates == GunState.Returning)
{ ReturnToNormalPosition(); } // move back to origin, when reached state = resting

if (gunStates == GunState.Resting)
{ } // do nothing in update, unless shot makes state shooting

^ Very roughly written, but hopefully you get the idea :slight_smile: