using System; using System.Collections; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.Serialization; using Random = UnityEngine.Random; // ReSharper disable once CheckNamespace namespace BlueWaterProject { public class Cannon : MonoBehaviour { /*********************************************************************** * Variables ***********************************************************************/ #region Variables [Title("초기화 방식")] [SerializeField] private bool autoInit = true; [Title("컴포넌트")] [SerializeField] private PlayerInput playerInput; [SerializeField] private GameObject projectileObj; [SerializeField] private Transform firePos; [SerializeField] private GameObject directionIndicator; [SerializeField] private ProcessBar cannonProcessBar; [Title("대포 변수")] [SerializeField] private float cannonCooldown = 1f; [SerializeField] private float chargingSpeed = 1f; [Range(0f, 0.5f)] [SerializeField] private float fireAngle = 0.2f; [SerializeField] private float launchSpeed = 75f; [SerializeField] private float launchAngle = 30f; [SerializeField] private Vector2 randomCatch = new(1, 4); [SerializeField] private LayerMask waterLayer; [SerializeField] private LayerMask targetLayer; [Title("캐논 발사 카메라 효과")] [SerializeField] private float cameraShakePower = 2f; [SerializeField] private float cameraShakeDuration = 0.3f; [Title("실시간 상태")] [DisableIf("@true")] [SerializeField] private bool isFireMode; [DisableIf("@true")] [SerializeField] private bool chargingCannon; [DisableIf("@true")] [SerializeField] private bool isReloading; [DisableIf("@true")] [SerializeField] private float chargingGauge; [DisableIf("@true")] [SerializeField] private float previousGauge; private float cannonRadius; private Collider[] hitColliders = new Collider[3]; #endregion /*********************************************************************** * Unity Events ***********************************************************************/ #region Unity Events private void Awake() { if (autoInit) { Init(); } } private void Start() { cannonProcessBar = UiManager.Inst.OceanUi.ProcessBar; } private void OnEnable() { playerInput.actions.FindAction("ToggleCannon").started += _ => ToggleCannon(); playerInput.actions.FindAction("FireCannon").started += _ => ChargeCannon(); playerInput.actions.FindAction("FireCannon").canceled += _ => FireCannon(); } private void OnDisable() { playerInput.actions.FindAction("ToggleCannon").started += _ => ToggleCannon(); playerInput.actions.FindAction("FireCannon").started -= _ => ChargeCannon(); playerInput.actions.FindAction("FireCannon").canceled -= _ => FireCannon(); } private void Update() { HandleFireCannon(); } #endregion /*********************************************************************** * Init Methods ***********************************************************************/ #region Init Methods [Button("셋팅 초기화")] private void Init() { playerInput = GetComponentInParent(); projectileObj = Utils.LoadFromFolder("Assets/05.Prefabs/Particles/GrenadeFire", "GrenadeFireOBJ", ".prefab"); firePos = transform.Find("FirePos"); directionIndicator = transform.parent.Find("DirectionIndicator").gameObject; directionIndicator.SetActive(false); cannonRadius = projectileObj.GetComponent()?.radius ?? projectileObj.GetComponent().colliderRadius; waterLayer = LayerMask.GetMask("Water"); targetLayer = LayerMask.GetMask("Boids"); } #endregion /*********************************************************************** * PlayerInput ***********************************************************************/ #region PlayerInput private void ToggleCannon() { isFireMode = !isFireMode; directionIndicator.SetActive(isFireMode); cannonProcessBar.SetActive(isFireMode); if (!isFireMode) { chargingCannon = false; chargingGauge = 0f; previousGauge = chargingGauge; cannonProcessBar.SetFillAmount(0f); cannonProcessBar.SetRotateZ(previousGauge * -360f); cannonProcessBar.SetRotateZ(0f); cannonProcessBar.SetSliderValue(0f); } } private void ChargeCannon() { if (!isFireMode) return; if (isReloading) { StartCoroutine(UiManager.Inst.OceanUi.ProcessBar.ShakeProcessBarCoroutine()); } else { chargingCannon = true; chargingGauge = 0f; } } private void FireCannon() { if (!isFireMode || !chargingCannon) return; chargingCannon = false; // previousGauge = 0f ~ 1f previousGauge = chargingGauge; chargingGauge = 0f; cannonProcessBar.SetFillAmount(0f); cannonProcessBar.SetRotateZ(previousGauge * -360f); Fire(previousGauge * 100); isReloading = true; StartCoroutine(CannonCoolDown(cannonCooldown)); } #endregion /*********************************************************************** * Methods ***********************************************************************/ #region Methods private void HandleFireCannon() { if (!isFireMode) return; var ray = CameraManager.Inst.MainCam.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out var hit, Mathf.Infinity, waterLayer)) { var directionToMouse = hit.point - directionIndicator.transform.position; directionToMouse.y = 0f; var lookRotation = Quaternion.LookRotation(directionToMouse); var indicatorRotationDirection = Quaternion.Euler(0f, lookRotation.eulerAngles.y, 0f); var cannonRotationDirection = Quaternion.Euler(transform.rotation.eulerAngles.x, lookRotation.eulerAngles.y, 0f); directionIndicator.transform.rotation = indicatorRotationDirection; transform.rotation = cannonRotationDirection; } if (!chargingCannon) return; if (chargingGauge < 1f) { chargingGauge += chargingSpeed * Time.deltaTime; chargingGauge = Mathf.Clamp(chargingGauge, 0f, 1f); } else { chargingGauge = 1f; } cannonProcessBar.SetFillAmount(chargingGauge); // if (!isFireMode) return; // // var ray = CameraManager.Inst.MainCam.ScreenPointToRay(Input.mousePosition); // // if (Physics.Raycast(ray, out var hit, Mathf.Infinity, waterLayer)) // { // var directionToMouse = hit.point - directionIndicator.transform.position; // directionToMouse.y = 0f; // // var lookRotation = Quaternion.LookRotation(directionToMouse); // var indicatorRotationDirection = Quaternion.Euler(0f, lookRotation.eulerAngles.y, 0f); // var cannonRotationDirection = Quaternion.Euler(cannon.transform.rotation.eulerAngles.x, lookRotation.eulerAngles.y, 0f); // directionIndicator.transform.rotation = indicatorRotationDirection; // cannon.transform.rotation = cannonRotationDirection; // } // // if (!chargingCannon) return; // // if (chargingGauge < 1f) // { // chargingGauge += chargingSpeed * Time.deltaTime; // chargingGauge = Mathf.Clamp(chargingGauge, 0f, 1f); // } // else // { // chargingGauge = 1f; // } // UiManager.Inst.OceanUi.ProcessBar.SetFillAmount(chargingGauge); } private IEnumerator CannonCoolDown(float waitTime) { var time = 0f; cannonProcessBar.SetSliderValue(0f); cannonProcessBar.SetActiveReloadSlider(true); while (time <= waitTime) { time += Time.deltaTime; var sliderValue = time > 0 ? time / waitTime : 0f; cannonProcessBar.SetSliderValue(sliderValue); yield return null; } isReloading = false; cannonProcessBar.SetActiveReloadSlider(false); } private void Fire(float chargingGauge) { VisualFeedbackManager.Inst.CameraShake(CameraManager.Inst.OceanCamera.BaseShipCam, cameraShakePower, cameraShakeDuration); var addAngle = chargingGauge * fireAngle; var firePosRotation = firePos.rotation.eulerAngles; firePosRotation.x -= addAngle; var projectile = Instantiate(projectileObj, firePos.position, Quaternion.Euler(firePosRotation)); var particleWeapon = projectile.GetComponent(); particleWeapon.onHitAction.AddListener(HandleCannonHit); projectile.GetComponent().velocity = projectile.transform.forward * launchSpeed; } private void HandleCannonHit(RaycastHit hit, float power) { if (hit.collider.gameObject.layer == LayerMask.NameToLayer("Water")) { var maxSize = Physics.OverlapSphereNonAlloc(hit.point, cannonRadius, hitColliders, targetLayer, QueryTriggerInteraction.Collide); for (var i = 0; i < maxSize; i++) { var hitBoids = hitColliders[i].GetComponentInParent(); var catchSize = Random.Range((int)randomCatch.x, (int)randomCatch.y); hitBoids.CatchBoid(hitColliders[i], catchSize); } } else { hit.transform.GetComponent()?.TakeDamage(power); } } #endregion } }