Files
PIM_Laba3/Assets/Scripts/Gun.cs
T
2026-05-25 23:10:02 +03:00

374 lines
10 KiB
C#

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;
}
}