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
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.
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?
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.
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.