266 lines
9.4 KiB
C#
266 lines
9.4 KiB
C#
|
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<Cannon>();
|
||
|
playerInput = GetComponentInParent<PlayerInput>();
|
||
|
}
|
||
|
|
||
|
#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<Boids>();
|
||
|
var catchSize = Random.Range((int)randomCatch.x, (int)randomCatch.y + 1);
|
||
|
hitBoids.CatchBoid(hitColliders[i], catchSize);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hit.transform.GetComponent<IDamageable>()?.TakeDamage(power);
|
||
|
}
|
||
|
|
||
|
if (marker)
|
||
|
{
|
||
|
Destroy(marker);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|