first commit
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
public class Gun : MonoBehaviour
|
||||
{
|
||||
[Header("Stats")]
|
||||
public string weaponName = "Pistol";
|
||||
public bool automatic;
|
||||
public float damage = 20f;
|
||||
public float range = 90f;
|
||||
public float fireRate = 3f;
|
||||
public float spread = 0.008f;
|
||||
public int projectilesPerShot = 1;
|
||||
public int magazineSize = 12;
|
||||
public int reserveAmmo = 48;
|
||||
public float reloadTime = 1.1f;
|
||||
public float recoilPitch = 1.6f;
|
||||
public float recoilYaw = 0.45f;
|
||||
public Color tracerColor = new Color(1f, 0.85f, 0.3f);
|
||||
|
||||
[Header("References")]
|
||||
public Transform muzzlePoint;
|
||||
public ParticleSystem muzzleFlash;
|
||||
public AudioSource shootAudioSource;
|
||||
public AudioSource reloadAudioSource;
|
||||
public AudioClip shootClip;
|
||||
public AudioClip reloadClip;
|
||||
|
||||
[Header("Audio")]
|
||||
public Vector2 shootPitchRange = new Vector2(0.98f, 1.02f);
|
||||
public Vector2 reloadPitchRange = new Vector2(0.98f, 1.02f);
|
||||
public float shootVolume = 1f;
|
||||
public float reloadVolume = 1f;
|
||||
|
||||
public int AmmoInMagazine { get; private set; }
|
||||
public int ReserveAmmo => reserveAmmo;
|
||||
public bool IsReloading { get; private set; }
|
||||
|
||||
private static Material _sharedTracerMaterial;
|
||||
|
||||
private Camera _firingCamera;
|
||||
private FPSController _controller;
|
||||
private HUDController _hud;
|
||||
private WeaponSwitcher _weaponSwitcher;
|
||||
private float _nextFireTime;
|
||||
private Coroutine _reloadRoutine;
|
||||
private bool _initialized;
|
||||
|
||||
public void Initialize(Camera firingCamera, FPSController controller, HUDController hud, WeaponSwitcher weaponSwitcher)
|
||||
{
|
||||
_firingCamera = firingCamera;
|
||||
_controller = controller;
|
||||
_hud = hud;
|
||||
_weaponSwitcher = weaponSwitcher;
|
||||
AmmoInMagazine = magazineSize;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
public void BindOwnership(HUDController hud, WeaponSwitcher weaponSwitcher)
|
||||
{
|
||||
_hud = hud;
|
||||
_weaponSwitcher = weaponSwitcher;
|
||||
UpdateHud();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
EnsureInitialized();
|
||||
UpdateHud();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
CancelReload();
|
||||
muzzleFlash?.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (_firingCamera == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
TryReload();
|
||||
}
|
||||
|
||||
bool firePressed = automatic ? Input.GetButton("Fire1") : Input.GetButtonDown("Fire1");
|
||||
if (firePressed)
|
||||
{
|
||||
TryFire();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEquipCooldown(float cooldown)
|
||||
{
|
||||
_nextFireTime = Mathf.Max(_nextFireTime, Time.time + cooldown);
|
||||
}
|
||||
|
||||
public bool AddReserveAmmo(int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
reserveAmmo += amount;
|
||||
UpdateHud();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CancelReload()
|
||||
{
|
||||
if (_reloadRoutine != null)
|
||||
{
|
||||
StopCoroutine(_reloadRoutine);
|
||||
_reloadRoutine = null;
|
||||
}
|
||||
|
||||
IsReloading = false;
|
||||
if (reloadAudioSource != null)
|
||||
{
|
||||
reloadAudioSource.Stop();
|
||||
}
|
||||
UpdateHud();
|
||||
}
|
||||
|
||||
private void TryFire()
|
||||
{
|
||||
if (IsReloading || Time.time < _nextFireTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AmmoInMagazine <= 0)
|
||||
{
|
||||
TryReload();
|
||||
return;
|
||||
}
|
||||
|
||||
AmmoInMagazine--;
|
||||
_nextFireTime = Time.time + (1f / fireRate);
|
||||
_controller?.ApplyRecoil(recoilPitch, recoilYaw);
|
||||
PlayShootEffects();
|
||||
|
||||
Vector3 tracerStart = muzzlePoint != null ? muzzlePoint.position : _firingCamera.transform.position;
|
||||
Vector3 lastEndPoint = tracerStart + _firingCamera.transform.forward * range;
|
||||
|
||||
for (int i = 0; i < Mathf.Max(1, projectilesPerShot); i++)
|
||||
{
|
||||
Vector3 viewportPoint = new Vector3(
|
||||
0.5f + Random.Range(-spread, spread),
|
||||
0.5f + Random.Range(-spread, spread),
|
||||
0f);
|
||||
|
||||
Ray ray = _firingCamera.ViewportPointToRay(viewportPoint);
|
||||
Vector3 endPoint = ray.origin + ray.direction * range;
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, range, ~0, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
endPoint = hit.point;
|
||||
IDamageable damageable = hit.collider.GetComponentInParent<IDamageable>();
|
||||
damageable?.TakeDamage(damage, hit.point, hit.normal);
|
||||
}
|
||||
|
||||
lastEndPoint = endPoint;
|
||||
|
||||
if (projectilesPerShot == 1 || i < 2 || i == projectilesPerShot - 1)
|
||||
{
|
||||
ShowTracer(tracerStart, endPoint, tracerColor);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectilesPerShot > 1)
|
||||
{
|
||||
ShowTracer(tracerStart, lastEndPoint, tracerColor);
|
||||
}
|
||||
|
||||
UpdateHud();
|
||||
}
|
||||
|
||||
private void TryReload()
|
||||
{
|
||||
if (IsReloading || AmmoInMagazine >= magazineSize || reserveAmmo <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_reloadRoutine = StartCoroutine(ReloadRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator ReloadRoutine()
|
||||
{
|
||||
IsReloading = true;
|
||||
UpdateHud();
|
||||
PlayReloadSound();
|
||||
|
||||
yield return new WaitForSeconds(reloadTime);
|
||||
|
||||
int ammoNeeded = magazineSize - AmmoInMagazine;
|
||||
int ammoToLoad = Mathf.Min(ammoNeeded, reserveAmmo);
|
||||
AmmoInMagazine += ammoToLoad;
|
||||
reserveAmmo -= ammoToLoad;
|
||||
|
||||
IsReloading = false;
|
||||
_reloadRoutine = null;
|
||||
UpdateHud();
|
||||
}
|
||||
|
||||
private void UpdateHud()
|
||||
{
|
||||
if (_hud == null || _weaponSwitcher == null || _weaponSwitcher.CurrentGun != this)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hud.SetWeapon(weaponName);
|
||||
_hud.SetAmmo(AmmoInMagazine, reserveAmmo);
|
||||
_hud.SetCenterMessage(IsReloading ? "Reloading..." : string.Empty);
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (_firingCamera == null)
|
||||
{
|
||||
_firingCamera = GetComponentInParent<Camera>();
|
||||
if (_firingCamera == null)
|
||||
{
|
||||
_firingCamera = Camera.main;
|
||||
}
|
||||
}
|
||||
|
||||
if (_controller == null)
|
||||
{
|
||||
_controller = GetComponentInParent<FPSController>();
|
||||
}
|
||||
|
||||
if (_weaponSwitcher == null)
|
||||
{
|
||||
_weaponSwitcher = GetComponentInParent<WeaponSwitcher>();
|
||||
}
|
||||
|
||||
if (_hud == null)
|
||||
{
|
||||
_hud = FindObjectOfType<HUDController>();
|
||||
}
|
||||
|
||||
if (muzzlePoint == null)
|
||||
{
|
||||
Transform foundMuzzle = transform.Find("Muzzle");
|
||||
if (foundMuzzle != null)
|
||||
{
|
||||
muzzlePoint = foundMuzzle;
|
||||
}
|
||||
}
|
||||
|
||||
if (muzzleFlash == null)
|
||||
{
|
||||
Transform flashTransform = muzzlePoint != null ? muzzlePoint.Find("MuzzleFlash") : transform.Find("Muzzle/MuzzleFlash");
|
||||
if (flashTransform != null)
|
||||
{
|
||||
muzzleFlash = flashTransform.GetComponent<ParticleSystem>();
|
||||
}
|
||||
else
|
||||
{
|
||||
muzzleFlash = GetComponentInChildren<ParticleSystem>(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (shootAudioSource == null)
|
||||
{
|
||||
Transform shootTransform = transform.Find("ShootAudio");
|
||||
if (shootTransform != null)
|
||||
{
|
||||
shootAudioSource = shootTransform.GetComponent<AudioSource>();
|
||||
}
|
||||
}
|
||||
|
||||
if (reloadAudioSource == null)
|
||||
{
|
||||
Transform reloadTransform = transform.Find("ReloadAudio");
|
||||
if (reloadTransform != null)
|
||||
{
|
||||
reloadAudioSource = reloadTransform.GetComponent<AudioSource>();
|
||||
}
|
||||
}
|
||||
|
||||
if (shootClip == null)
|
||||
{
|
||||
shootClip = Resources.Load<AudioClip>($"Audio/{weaponName.ToLowerInvariant()}_fire");
|
||||
}
|
||||
|
||||
if (reloadClip == null)
|
||||
{
|
||||
reloadClip = Resources.Load<AudioClip>($"Audio/{weaponName.ToLowerInvariant()}_reload");
|
||||
}
|
||||
|
||||
if (AmmoInMagazine <= 0)
|
||||
{
|
||||
AmmoInMagazine = magazineSize;
|
||||
}
|
||||
|
||||
_initialized = _firingCamera != null;
|
||||
}
|
||||
|
||||
private void PlayShootEffects()
|
||||
{
|
||||
if (muzzleFlash != null)
|
||||
{
|
||||
muzzleFlash.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
||||
muzzleFlash.Play();
|
||||
}
|
||||
|
||||
PlayClip(shootAudioSource, shootClip, shootPitchRange, shootVolume, false);
|
||||
}
|
||||
|
||||
private void PlayReloadSound()
|
||||
{
|
||||
PlayClip(reloadAudioSource, reloadClip, reloadPitchRange, reloadVolume, true);
|
||||
}
|
||||
|
||||
private static void PlayClip(AudioSource source, AudioClip clip, Vector2 pitchRange, float volume, bool replaceClip)
|
||||
{
|
||||
if (source == null || clip == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
source.pitch = Random.Range(pitchRange.x, pitchRange.y);
|
||||
source.volume = volume;
|
||||
|
||||
if (replaceClip)
|
||||
{
|
||||
source.Stop();
|
||||
source.clip = clip;
|
||||
source.Play();
|
||||
return;
|
||||
}
|
||||
|
||||
source.PlayOneShot(clip, volume);
|
||||
}
|
||||
|
||||
private void ShowTracer(Vector3 start, Vector3 end, Color color)
|
||||
{
|
||||
GameObject tracer = new GameObject($"{weaponName}_Tracer");
|
||||
LineRenderer renderer = tracer.AddComponent<LineRenderer>();
|
||||
renderer.material = GetTracerMaterial();
|
||||
renderer.positionCount = 2;
|
||||
renderer.SetPosition(0, start);
|
||||
renderer.SetPosition(1, end);
|
||||
renderer.startWidth = 0.032f;
|
||||
renderer.endWidth = 0.012f;
|
||||
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
renderer.receiveShadows = false;
|
||||
renderer.startColor = color;
|
||||
renderer.endColor = new Color(color.r, color.g, color.b, 0.1f);
|
||||
|
||||
Destroy(tracer, 0.06f);
|
||||
}
|
||||
|
||||
private static Material GetTracerMaterial()
|
||||
{
|
||||
if (_sharedTracerMaterial == null)
|
||||
{
|
||||
_sharedTracerMaterial = new Material(Shader.Find("Sprites/Default"));
|
||||
}
|
||||
|
||||
return _sharedTracerMaterial;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user