using System; using System.Collections; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.InputSystem; using Random = UnityEngine.Random; // ReSharper disable once CheckNamespace namespace BlueWaterProject { public class CannonController : MonoBehaviour { /*********************************************************************** * Variables ***********************************************************************/ #region Variables // 컴포넌트 [TitleGroup("컴포넌트"), BoxGroup("컴포넌트/컴포넌트", ShowLabel = false)] [Required, SerializeField] private Cannon cannon; [BoxGroup("컴포넌트/컴포넌트", ShowLabel = false)] [Required, SerializeField] private PlayerInput playerInput; [BoxGroup("컴포넌트/컴포넌트", ShowLabel = false)] [SerializeField] private ProcessBar launchProcessBar; // 대포 기본 설정 [TitleGroup("대포 기본 설정")] // 게이지 [BoxGroup("대포 기본 설정/게이지")] [Range(0.1f, 5f), Tooltip("게이지가 모두 차는데 걸리는 시간\n게이지는 0 ~ 1의 값을 가짐")] [SerializeField] private float gaugeChargingTime = 1f; // 기타 [BoxGroup("대포 기본 설정/기타")] [Tooltip("랜덤으로 잡힐 물고기 마릿수 x, y를 포함하는 사이의 값")] [SerializeField] private Vector2 randomCatch = new(1, 4); [BoxGroup("대포 기본 설정/기타")] [SerializeField] private float mouseRayDistance = 500f; [BoxGroup("대포 기본 설정/기타")] [SerializeField] private LayerMask waterLayer; [BoxGroup("대포 기본 설정/기타")] [SerializeField] private LayerMask boidsLayer; // 카메라 효과 옵션 [TitleGroup("카메라"), BoxGroup("카메라/카메라 흔들림 효과", ShowLabel = false)] [SerializeField] private float cameraShakePower = 2f; [BoxGroup("카메라/카메라 흔들림 효과", ShowLabel = false)] [SerializeField] private float cameraShakeDuration = 0.3f; // 실시간 상태 [TitleGroup("실시간 상태")] [DisableIf("@true")] [SerializeField] private bool isLaunchMode; [DisableIf("@true")] [SerializeField] private bool isCharging; [DisableIf("@true")] [SerializeField] private float chargingGauge; [DisableIf("@true")] [SerializeField] private float previousGauge; private Collider[] hitColliders; private const int MAX_HIT_SIZE = 8; #endregion /*********************************************************************** * Unity Events ***********************************************************************/ #region Unity Events private void Start() { launchProcessBar = UiManager.Inst.OceanUi.ProcessBar; hitColliders = new Collider[MAX_HIT_SIZE]; } private void OnEnable() { playerInput.actions.FindAction("ToggleLaunchMode").started += _ => ToggleLaunchMode(); playerInput.actions.FindAction("LaunchCannon").started += _ => ChargeCannon(); playerInput.actions.FindAction("LaunchCannon").canceled += _ => LaunchCannon(); } private void OnDisable() { playerInput.actions.FindAction("ToggleLaunchMode").started -= _ => ToggleLaunchMode(); playerInput.actions.FindAction("LaunchCannon").started -= _ => ChargeCannon(); playerInput.actions.FindAction("LaunchCannon").canceled -= _ => LaunchCannon(); } private void Update() { HandleLaunchCannon(); } #endregion /*********************************************************************** * Init Methods ***********************************************************************/ #region Init Methods [Button("셋팅 초기화")] private void Init() { cannon = GetComponent(); playerInput = GetComponentInParent(); } #endregion /*********************************************************************** * PlayerInput ***********************************************************************/ #region PlayerInput private void ToggleLaunchMode() { isLaunchMode = !isLaunchMode; launchProcessBar.SetActive(isLaunchMode); if (!isLaunchMode) { isCharging = false; chargingGauge = 0f; previousGauge = chargingGauge; launchProcessBar.SetFillAmount(0f); launchProcessBar.SetRotateZ(previousGauge * -360f); launchProcessBar.SetRotateZ(0f); launchProcessBar.SetSliderValue(0f); cannon.ExitLaunchMode(); } } private void ChargeCannon() { if (!isLaunchMode) return; if (cannon.IsReloading) { StartCoroutine(UiManager.Inst.OceanUi.ProcessBar.ShakeProcessBarCoroutine()); } else { isCharging = true; chargingGauge = 0f; cannon.StartChargeCannon(); } } private void LaunchCannon() { if (!isLaunchMode || !isCharging) return; isCharging = false; previousGauge = chargingGauge; chargingGauge = 0f; launchProcessBar.SetFillAmount(0f); launchProcessBar.SetRotateZ(previousGauge * -360f); cannon.SetActivePredictLine(false); cannon.Launch(); StartCoroutine(LaunchCoolDown(cannon.LaunchCooldown)); VisualFeedbackManager.Inst.CameraShake(CameraManager.Inst.OceanCamera.BaseShipCam, cameraShakePower, cameraShakeDuration); } #endregion /*********************************************************************** * Methods ***********************************************************************/ #region Methods private void HandleLaunchCannon() { if (!isLaunchMode) return; var ray = CameraManager.Inst.MainCam.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out var hit, mouseRayDistance, waterLayer, QueryTriggerInteraction.Collide)) { var directionToMouse = (hit.point - transform.position).normalized; directionToMouse.y = 0f; var lookRotation = Quaternion.LookRotation(directionToMouse); var cannonRotationDirection = Quaternion.Euler(0f, lookRotation.eulerAngles.y, 0f); transform.rotation = cannonRotationDirection; } if (!isCharging) return; if (chargingGauge < 1f) { if (gaugeChargingTime == 0f) { gaugeChargingTime = 1f; } chargingGauge += 1 / gaugeChargingTime * Time.deltaTime; chargingGauge = Mathf.Clamp(chargingGauge, 0f, 1f); } else { chargingGauge = 1f; } launchProcessBar.SetFillAmount(chargingGauge); cannon.CalculateLaunchTrajectory(cannon.CalculateEndPosition(chargingGauge), true); } private IEnumerator LaunchCoolDown(float waitTime) { var time = 0f; launchProcessBar.SetSliderValue(0f); launchProcessBar.SetActiveReloadSlider(true); while (time <= waitTime) { time += Time.deltaTime; var sliderValue = time > 0 ? time / waitTime : 0f; launchProcessBar.SetSliderValue(sliderValue); yield return null; } cannon.IsReloading = false; launchProcessBar.SetActiveReloadSlider(false); } public void HitAction(RaycastHit hit, float power, GameObject marker = null) { if (hit.collider.gameObject.layer == LayerMask.NameToLayer("Water")) { var maxSize = Physics.OverlapSphereNonAlloc(hit.point, cannon.CannonRadius, hitColliders, boidsLayer, QueryTriggerInteraction.Collide); for (var i = 0; i < maxSize; i++) { var hitBoids = hitColliders[i].GetComponentInParent(); var catchSize = Random.Range((int)randomCatch.x, (int)randomCatch.y + 1); hitBoids.CatchBoid(hitColliders[i], catchSize); } } else { hit.transform.GetComponent()?.TakeDamage(power); } if (marker) { Destroy(marker); } } #endregion } }