help with weapon recoil when ads

i have this problem with my weapon recoil. when you ads, the weapon lerps its global position to the global position of the ads point (this is so it always matches up if the parents have different pos) but the problem is, when you recoil ,you move the gun location and this doesnt work because, as stated before, it is always getting lerped to the ads point

this is the code moving the gun during ads

if (Input.GetKey(aimKey) & !completelyAiming)
        {

            transform.position = Vector3.MoveTowards(transform.position, adsPoint.position, adsSpeed * Time.deltaTime);
            //Debug.Log(adsPoint.name);
        }
        else if(!Input.GetKey(aimKey))
        {
            transform.localPosition = Vector3.MoveTowards(transform.localPosition, startPos, adsSpeed * Time.deltaTime);
        }

        if (Vector3.Distance(transform.position, adsPoint.position) <= 0.005f)
        {
            completelyAiming = true;
        }
        else
        {
            completelyAiming = false;
        }

and here is the weapon recoil code

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

public class GunRecoil : MonoBehaviour
{
    Vector3 originalPos;
    bool isAiming;
    Gun currentWeapon;

    public bool recoiling;
    public WeaponSwitching weaponSwitch;


    // Start is called before the first frame update
    void Start()
    {
        currentWeapon = weaponSwitch.currentSelectedWeapon.GetComponent<Gun>();

        originalPos = transform.localPosition;
    }

    // Update is called once per frame
    void Update()
    {
        transform.localPosition = Vector3.Lerp(transform.localPosition, originalPos, currentWeapon.weaponReturnSpeed * Time.deltaTime);

        if(Vector3.Distance(transform.localPosition, originalPos) <= 0.05f)
        {
            recoiling = false;
        }

        isAiming = PlayerMovement.instance.isAiming;
    }

    public void Recoil()
    {
        if (isAiming)
        {
            Vector3 recoilAmount = new Vector3(Random.Range(-currentWeapon.wepAdsRecoilX, currentWeapon.wepAdsRecoilX), currentWeapon.wepAdsRecoilY, -currentWeapon.wepAdsRecoilZ);
            transform.localPosition += recoilAmount;
            recoiling = true;
        }
        else
        {
            Vector3 recoilAmount = new Vector3(Random.Range(-currentWeapon.wepRecoilX, currentWeapon.wepRecoilX), currentWeapon.wepRecoilY, -currentWeapon.wepRecoilZ);
            transform.localPosition += recoilAmount;
            recoiling = true;
        }
    }
}

how can i make it so it doesnt lerp to that specific location when the gun is doing the recoil but still keep ads?

bump

bump, someone please respond

From my reading of your description you need to untangle what you have going for ADS and apply the steps in clean layers that don’t interfere with each other.

Recoil itself is ultra trivial to implement, once you have broken things out so you have a single viewpoint recoiling and key everything else off that. See attached recoil package.

Keep in mind that recoil while ADS and normal recoil may necessarily need to be completely different just to give a good feeling, but this would be entirely up to your design.

7534313–930308–Recoil.unitypackage (211 KB)

do u have any suggestions on how to redo my ads system?

You mean beyond my suggestion of putting two recoil systems on, one for normal and one for ADS?

Recoil is hyper-simple, as seen in the project above. ADS I don’t know about, but it sounds like yours is more on the complex side. Why not just slap two different recoils on, or else hack up the recoil to drive all the transforms you need it to?

wow wtf i completely fixed the problem by doing this

if (Input.GetKey(aimKey) & !completelyAiming)
        {

            transform.position = Vector3.MoveTowards(transform.position, adsPoint.position, adsSpeed * Time.deltaTime);
            //Debug.Log(adsPoint.name);
        }
        else if(!Input.GetKey(aimKey))
        {
            completelyAiming = false;
            transform.localPosition = Vector3.MoveTowards(transform.localPosition, startPos, adsSpeed * Time.deltaTime);
        }

        if (Vector3.Distance(transform.position, adsPoint.position) <= 0.005f)
        {
            completelyAiming = true;
        }
1 Like

also theres this other problem with my ads (wow such a bad ads system, who designed this?) when you reload, i want it to stop the ads. it works in the sense that you zoom out but its not moving the gun back to the start position…how do i fix this

using System.Collections;
using UnityEngine;
using TMPro;

public enum GunType
{
    Pistol, Rifle, Shotgun
}

public class Gun : MonoBehaviour
{
    public GameObject weaponHolder;

    [Header("Shooting")]
    public Camera cam;
    public Camera gunCam;

    public GunType type = GunType.Pistol;

    public int damage = 50;
    public float range = 100f;

    public float cooldown;
    private float currentCooldown;

    [Header("Ammo and Reloading")]
    public int maxAmmo = 100, maxReloadAmmo = 250;
    private int currentAmmo, currentReloadAmmo;
    public float reloadTime = 0.75f;
    private bool isReloading = false;

    public Animator anim;

    [Header("Bloom and Bullet Variation")]
    public float currentBloom;
    public float regularBloom;
    public float aimBloom;

    public float currentSpread;
    public float shotgunSpread;
    public float aimingShotgunSpread;

    [Header("ADS")]
    public float startFOV, aimingFOV, zoomSpeed;
    private float targetFOV;
    bool completelyAiming;
   

    public Transform adsPoint;
    private Vector3 startPos;
    public float adsSpeed;

    [Header("Keybinds")]
    public KeyCode shootKey = KeyCode.Mouse0;
    public KeyCode aimKey = KeyCode.Mouse1;

    [Header("UI Elements")]
    public TMP_Text ammoText, reloadAmmoText;

    [Header("Effects")]
    public ParticleSystem muzzleFlash;
    public Transform firePoint;

    public GameObject bulletHole;
    public LayerMask canBeShot;

    public float timeUntilBulletHoleDestroy = 2f;

    [Header("Recoil")]
    private CameraRecoil camRecoilScript;
    private GunRecoil gunRecoilScript;

    [Header("Cam Recoil")]
    //Hipfire recoil
    public float camRecoilX, camRecoilY, camRecoilZ;

    //Ads recoil
    public float camAdsRecoilX, camAdsRecoilY, camAdsRecoilZ;

    [Header("Weapon Recoil")]
    //Hipfire recoil
    public float wepRecoilX, wepRecoilY, wepRecoilZ;

    //Ads recoil
    public float wepAdsRecoilX, wepAdsRecoilY, wepAdsRecoilZ;


    [Space(20)]
    //Settings
    public float snappiness;
    public float returnSpeed, weaponReturnSpeed;


    private void Awake()
    {
        weaponHolder = GameObject.FindGameObjectWithTag("WeaponHolder");

        cam = Camera.main;
        gunCam = GameObject.FindGameObjectWithTag("GunCam").GetComponent<Camera>();

        anim = weaponHolder.GetComponent<Animator>();

        ammoText = GameObject.FindGameObjectWithTag("AmmoText").GetComponent<TMP_Text>();
        reloadAmmoText = GameObject.FindGameObjectWithTag("ReloadAmmoText").GetComponent<TMP_Text>();

        switch (type)
        {
            case GunType.Pistol:
                {
                    adsPoint = GameObject.FindGameObjectWithTag("PistolADS").GetComponent<Transform>();
                }
                break;
            case GunType.Rifle:
                {
                    adsPoint = GameObject.FindGameObjectWithTag("RifleADS").GetComponent<Transform>();
                }
                break;
            case GunType.Shotgun:
                {
                    adsPoint = GameObject.FindGameObjectWithTag("ShotgunADS").GetComponent<Transform>();
                }
                break;
        }

        camRecoilScript = FindObjectOfType<CameraRecoil>().GetComponent<CameraRecoil>();
        gunRecoilScript = FindObjectOfType<GunRecoil>().GetComponent<GunRecoil>();
    }

    void Start()
    {
        currentCooldown = cooldown;
        currentAmmo = maxAmmo;
        currentReloadAmmo = maxReloadAmmo;

        ammoText.text = currentAmmo.ToString();
        reloadAmmoText.text = currentReloadAmmo.ToString();

        targetFOV = startFOV;

        startPos = transform.localPosition;

        currentBloom = regularBloom;

        currentSpread = shotgunSpread;
    }

    private void OnEnable()
    {
        isReloading = false;
        anim.SetBool("Reloading", false);
    }

    // Update is called once per frame
    void Update()
    {
        if (isReloading)
            return;

        ReloadInput();
        ShootInput();
        AimInput();



        cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFOV, zoomSpeed * Time.deltaTime);
        gunCam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFOV, zoomSpeed * Time.deltaTime);

        ammoText.text = currentAmmo.ToString();
        reloadAmmoText.text = currentReloadAmmo.ToString();
    }

    void Shoot(GunType gun)
    {
        PlayerMovement.instance.isShooting = true;
        if (gun == GunType.Pistol || gun == GunType.Rifle)
        {
            currentAmmo--;

            Vector3 bloom = cam.transform.position + cam.transform.forward * 1000f;
            bloom += Random.Range(-currentBloom, currentBloom) * cam.transform.up;
            bloom += Random.Range(-currentBloom, currentBloom) * cam.transform.right;
            bloom -= cam.transform.position;
            bloom.Normalize();

            RaycastHit hit;
            if (Physics.Raycast(cam.transform.position, bloom, out hit, range, canBeShot))
            {
                //Debug.Log(hit.transform.name);
                GameObject _bulletHole = Instantiate(bulletHole, hit.point + hit.normal * 0.001f, Quaternion.identity);
                _bulletHole.transform.LookAt(hit.point + hit.normal);
                _bulletHole.transform.parent = hit.transform;
                Destroy(_bulletHole, timeUntilBulletHoleDestroy);

                //Deal Damage
                if (hit.transform.gameObject.GetComponent<EnemyHealth>() != null)
                {
                    hit.transform.gameObject.GetComponent<EnemyHealth>().TakeDamage(damage);
                }

            }
            currentCooldown = cooldown;
            Transform effect = Instantiate(muzzleFlash, firePoint.position, firePoint.rotation).transform;

            effect.parent = transform;
            camRecoilScript.RecoilFire();
            gunRecoilScript.Recoil();
        }
    }

    void ShotgunShoot()
    {
        PlayerMovement.instance.isShooting = true;

        currentAmmo--;

        #region "Bullets"

        ShotgunBullet();
        ShotgunBullet();
        ShotgunBullet();
        ShotgunBullet();
        ShotgunBullet();
        ShotgunBullet();

        #endregion "Bullets"

        currentCooldown = cooldown;
        Transform effect = Instantiate(muzzleFlash, firePoint.position, firePoint.rotation).transform;

        effect.parent = transform;

        camRecoilScript.RecoilFire();
        gunRecoilScript.Recoil();
    }

    IEnumerator Reload()
    {
        isReloading = true;
        PlayerMovement.instance.isReloading = true;
        anim.SetBool("Reloading", true);

        //AimOut();

        //Debug.Log("Reloading");
        yield return new WaitForSeconds(reloadTime - .25f);
        if (currentReloadAmmo >= maxAmmo - currentAmmo) //if your reload ammo is more than the amount needed to reload
        {
            currentReloadAmmo -= maxAmmo - currentAmmo; //take away the amount needed to reload from the reload ammo
            //Debug.Log((maxAmmo - currentAmmo).ToString());
            currentAmmo = maxAmmo; //and reload
        }
        else //if you have less than the requested ammo
        {
            currentAmmo += currentReloadAmmo; //just add the rest of the ammo
            currentReloadAmmo = 0; //get rid of the reload ammo
        }
        yield return new WaitForSeconds(.25f);

        anim.SetBool("Reloading", false);
        isReloading = false;
        PlayerMovement.instance.isReloading = false;
    }

    void ShootInput()
    {
        if (currentCooldown > 0f)
        {
            currentCooldown -= Time.deltaTime;
        }
        else
        {
            switch (type)
            {
                case GunType.Pistol:
                    {
                        if (Input.GetKeyDown(shootKey))
                        {
                            if (currentAmmo == 0 && currentReloadAmmo > 0)
                                StartCoroutine(Reload());
                            else if (currentAmmo > 0)
                                Shoot(type);
                        }
                        else
                        {
                            PlayerMovement.instance.isShooting = false;
                        }
                    }
                    break;
                case GunType.Rifle:
                    {
                        if (Input.GetKey(shootKey))
                        {
                            if (currentAmmo == 0 && currentReloadAmmo > 0)
                                StartCoroutine(Reload());

                            else if (currentAmmo > 0)
                                Shoot(type);
                        }
                        else
                        {
                            PlayerMovement.instance.isShooting = false;
                        }
                    }
                    break;
                case GunType.Shotgun:
                    {
                        if(Input.GetKeyDown(shootKey))
                        {
                            if (currentAmmo == 0 && currentReloadAmmo > 0)
                                StartCoroutine(Reload());

                            else if (currentAmmo > 0)
                                ShotgunShoot();
                        }
                        else
                        {
                            PlayerMovement.instance.isShooting = false;
                        }
                    }
                    break;
            }
        }
    }

    void ReloadInput()
    {
        if (Input.GetKeyDown(KeyCode.R) && currentAmmo < maxAmmo && currentReloadAmmo != 0)
        {
            StartCoroutine(Reload());
            AimOut();
        }
    }

    void AimInput()
    {
        if (isReloading)
        {
            AimOut();
        }
        if (Input.GetKey(aimKey) && !completelyAiming && !isReloading)
        {
            // Only move the gun when you aren't completely aiming, this is to prevent it constantly going back when the gun is recoiling.
            // Also, if you are reloading, automatically stop moving the gun and move the gun back to the original position.
            AimIn();

            transform.position = Vector3.MoveTowards(transform.position, adsPoint.position, adsSpeed * Time.deltaTime);
            //Debug.Log(adsPoint.name);
        }
        else if (!Input.GetKey(aimKey))
        {
            AimOut();
            completelyAiming = false;
            transform.localPosition = Vector3.MoveTowards(transform.localPosition, startPos, adsSpeed * Time.deltaTime);
        }

        if (Vector3.Distance(transform.position, adsPoint.position) <= 0.005f)
        {
            completelyAiming = true;
        }

    }

    public void AimIn()
    {
        targetFOV = aimingFOV;
        currentBloom = aimBloom;
        currentSpread = aimingShotgunSpread;


        PlayerMovement.instance.isAiming = true;
        PlayerMovement.instance.AimIn();
    }

    public void AimOut()
    {
        targetFOV = startFOV;
        currentBloom = regularBloom;
        currentSpread = shotgunSpread;


        PlayerMovement.instance.isAiming = false;
        PlayerMovement.instance.AimOut();


    }

    void ShotgunBullet()
    {
        RaycastHit hit;
        if (Physics.Raycast(cam.transform.position, cam.transform.forward + new Vector3(Random.Range(-currentSpread, currentSpread), Random.Range(-currentSpread, currentSpread), 0), out hit, range, canBeShot))
        {
            //Bullet Holes
            GameObject _bulletHole = Instantiate(bulletHole, hit.point + hit.normal * 0.001f, Quaternion.identity);
            _bulletHole.transform.LookAt(hit.point + hit.normal);
            _bulletHole.transform.parent = hit.transform;
            Destroy(_bulletHole, timeUntilBulletHoleDestroy);

            //Deal Damage
            if(hit.transform.gameObject.GetComponent<EnemyHealth>() != null)
            {
                hit.transform.gameObject.GetComponent<EnemyHealth>().TakeDamage(damage);
            }
        }
    }
}

I would constantly tween the values between ADS and normal based on a floating point vlaue, get that working flawlessly.

Then anybody can say ADS or not (by setting desiredQuantity; see below) and the ADS system just handles what needs to happen: eg does nothing if the state is already right.

You probably also want a cascade of truths:

if ADS commanded, then check reload:

  • if reload in progress, no ADS
  • else allow ADS

Smoothing movement between any two particular values:

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: SmoothMovement.cs Ā· GitHub

You would then use currentQuantity to control the ADS-ness of your setup.

i tried doing that first part before. however, when i move to ads, i change the world position but when i move back, only change the localposition.

how would i get aroudn this problem if the gun is constantly moving towards a ā€œtarget positionā€?

Usually this is handled by sub-transforms (child transforms). No matter where you drive your car, you still lift the coffee drink up from the drink holder to your mouth, even when parked or moving… your arm is a sub-transform of you, which are a sub-transform of the car.

i feel stupid asking this but whats a sub-transform? i dont rly understnad what u mean and how i could implement this

→

yea but wouldnt that be ignored if im using world position instead of local?

also do u have a discord? that way it will be easier to communicate