using System; using System.Collections; using UnityEngine; using UnityEngine.Pool; // ReSharper disable once CheckNamespace namespace BlueWaterProject { public class Arrow : MonoBehaviour { #region Property and variable [Tooltip("발사 이후 자동으로 사라지는데 까지 걸리는 시간")] [Range(0f, 10f)] [SerializeField] private float autoDestroyTime; [Tooltip("화살이 날아가는 속도")] [SerializeField] private float arrowSpeed = 15f; [SerializeField] private bool isAttacked; private float g = Mathf.Abs(Physics.gravity.y); private float inaccuracy; private Vector3 targetPos; private AttackerType attackerType; public Coroutine shootCoroutine; private Transform attackerTransform; private AiStat attackerStat; private Rigidbody arrowRigidbody; private IObjectPool managedArrowPool; #endregion #region Unity built-in function private void Awake() { arrowRigidbody = Utils.GetComponentAndAssert(transform); } private void OnDisable() { if (shootCoroutine == null) return; StopCoroutine(shootCoroutine); shootCoroutine = null; } private void OnTriggerEnter(Collider other) { if (other.gameObject.layer == LayerMask.NameToLayer("Ground") || other.gameObject.layer == LayerMask.NameToLayer("Water")) { DestroyObject(); } else if (other.gameObject.layer == LayerMask.NameToLayer("HitBox")) { switch (attackerType) { case AttackerType.NONE: break; case AttackerType.PLAYER: if (!other.gameObject.CompareTag("Enemy")) { return; } break; case AttackerType.PIRATE: if (!other.gameObject.CompareTag("Enemy")) { return; } break; case AttackerType.ENEMY: if (!other.gameObject.CompareTag("Player") || !other.gameObject.CompareTag("Pirate")) { return; } break; default: throw new ArgumentOutOfRangeException(); } var iDamageable = other.GetComponentInParent(); iDamageable.TakeDamage(attackerStat, iDamageable.AiStat); DestroyObject(); } } #endregion #region Custom function public IEnumerator Shoot() { var time = 0f; // 화살의 목표 지점에 대한 불확실성 위치 값 var inaccuracyOffset = new Vector3 ( UnityEngine.Random.Range(-inaccuracy, inaccuracy), UnityEngine.Random.Range(-inaccuracy, inaccuracy), UnityEngine.Random.Range(-inaccuracy, inaccuracy) ); // 화살의 최종 목표 지점 var inaccurateTargetPos = targetPos + inaccuracyOffset; // 화살 자신의 위치 값 var myPos = transform.position; // 화살이 날아가야 하는 초기 수평 방향 var toTargetFlat = new Vector3(inaccurateTargetPos.x - myPos.x, 0, inaccurateTargetPos.z - myPos.z); // 발사될 때의 화살과 목표 지점 간의 수평 거리 var horizontalDistance = toTargetFlat.magnitude; // 발사될 때의 화살과 목표 지점 간의 수직 거리 var yOffset = inaccurateTargetPos.y - myPos.y; // 화살이 목표 지점에 도착하는 데 예상되는 시간 var timeToTarget = horizontalDistance / arrowSpeed; // 화살의 초기 수직 속도 var initialVerticalSpeed = (yOffset + (0.5f * g * timeToTarget * timeToTarget)) / timeToTarget; // 화살의 발사 속도 var launchVelocity = toTargetFlat.normalized * arrowSpeed + Vector3.up * initialVerticalSpeed; arrowRigidbody.velocity = launchVelocity; transform.rotation = Quaternion.LookRotation(arrowRigidbody.velocity) * Quaternion.Euler(0f, 90f, 0f); while (time < autoDestroyTime) { time += Time.deltaTime; if(arrowRigidbody.velocity != Vector3.zero) { transform.rotation = Quaternion.LookRotation(arrowRigidbody.velocity) * Quaternion.Euler(0f, 90f, 0f); } yield return null; } if (gameObject.activeSelf) { DestroyObject(); } } public void ShootArrowCoroutine() { if (shootCoroutine != null) { StopCoroutine(shootCoroutine); shootCoroutine = null; } shootCoroutine = StartCoroutine(Shoot()); } /// /// 화살이 발사 되기 직전에 필요한 데이터들을 입력받는 함수 /// public void SetShootingArrow(Vector3 shootPos, Vector3 targetPosition, AiStat attackerAiStat, AttackerType type, float inaccuracyValue) { transform.position = shootPos; targetPos = targetPosition; attackerStat = attackerAiStat; attackerType = type; inaccuracy = inaccuracyValue; } #endregion #region ObjectPool function private void DestroyObject() => managedArrowPool.Release(this); public void SetManagedPool(IObjectPool pool) => managedArrowPool = pool; #endregion #region Custom function /// /// objectPool 시스템에 의해서 Release되는 경우 값을 초기화 시키는 함수 /// public void ReleaseArrowSetting() { if (shootCoroutine != null) { StopCoroutine(shootCoroutine); shootCoroutine = null; } arrowRigidbody.velocity = Vector3.zero; arrowRigidbody.angularVelocity = Vector3.zero; gameObject.SetActive(false); } #endregion } }