using System.Collections; using UnityEngine; using UnityEngine.Events; [DisallowMultipleComponent] public sealed class CannonView : MonoBehaviour { [SerializeField] private NetworkCannonController controller; [SerializeField] private Transform recoilVisual; [SerializeField] private Vector3 localRecoilOffset = new(0f, 0f, -0.75f); [SerializeField, Min(0.01f)] private float recoilOutDuration = 0.08f; [SerializeField, Min(0.01f)] private float recoilReturnDuration = 0.45f; [SerializeField] private UnityEvent shotFired; [SerializeField] private UnityEvent missileFired; private Vector3 baseLocalPosition; private Coroutine recoilRoutine; private void Awake() { if (recoilVisual != null) baseLocalPosition = recoilVisual.localPosition; } private void OnEnable() { if (controller != null) controller.ShotFired += HandleShotFired; } private void OnDisable() { if (controller != null) controller.ShotFired -= HandleShotFired; if (recoilRoutine != null) StopCoroutine(recoilRoutine); if (recoilVisual != null) recoilVisual.localPosition = baseLocalPosition; } private void HandleShotFired(CannonShotState shot) { if (recoilRoutine != null) StopCoroutine(recoilRoutine); recoilRoutine = StartCoroutine(PlayRecoil()); shotFired?.Invoke(); missileFired?.Invoke(shot.MissileType); } private IEnumerator PlayRecoil() { if (recoilVisual == null) yield break; Vector3 recoilPosition = baseLocalPosition + localRecoilOffset; yield return Move(recoilVisual.localPosition, recoilPosition, recoilOutDuration); yield return Move(recoilVisual.localPosition, baseLocalPosition, recoilReturnDuration); recoilRoutine = null; } private IEnumerator Move(Vector3 from, Vector3 to, float duration) { float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); recoilVisual.localPosition = Vector3.LerpUnclamped( from, to, 1f - Mathf.Pow(1f - t, 3f)); yield return null; } recoilVisual.localPosition = to; } [ContextMenu("Preview/Recoil")] private void PreviewRecoil() { if (Application.isPlaying) HandleShotFired(new CannonShotState(0, MissileType.Standard)); else if (recoilVisual != null) recoilVisual.localPosition = baseLocalPosition + localRecoilOffset; } [ContextMenu("Preview/Reset")] private void ResetPreview() { if (recoilVisual != null) recoilVisual.localPosition = baseLocalPosition; } }