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 = 5f; [Tooltip("화살이 날아가는 속도")] [SerializeField] private float arrowSpeed = 10f; private bool isAttacked; private bool isOffense; private float g = Mathf.Abs(Physics.gravity.y); private float inaccuracy; private float atk; private float shieldPenetrationRate; private Vector3? attackerPos; private Vector3 targetPos; private EAiType attackerAiType; public Coroutine shootCoroutine; private Transform attackerTransform; 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 (isAttacked) return; if (other.gameObject.layer == LayerMask.NameToLayer("Ground") || other.gameObject.layer == LayerMask.NameToLayer("Water")) { isAttacked = true; DestroyObject(); } else if (other.gameObject.layer == LayerMask.NameToLayer("Props")) { if (isOffense) { if (other.gameObject.CompareTag("House")) { var iDamageable = other.GetComponentInParent(); iDamageable.TakeDamage(atk, shieldPenetrationRate); isAttacked = true; DestroyObject(); } } else { isAttacked = true; DestroyObject(); } } else if (other.gameObject.layer == LayerMask.NameToLayer("HitBox")) { switch (attackerAiType) { case EAiType.NONE: print("aiType == NONE Error"); break; case EAiType.PLAYER: case EAiType.PIRATE: if (other.gameObject.CompareTag("Enemy")) { break; } return; case EAiType.ENEMY: if (other.gameObject.CompareTag("Pirate") || other.gameObject.CompareTag("Player")) { break; } return; default: throw new ArgumentOutOfRangeException(); } var iDamageable = other.GetComponentInParent(); if (attackerPos != null) { iDamageable.TakeDamage(atk, shieldPenetrationRate, (Vector3)attackerPos); } isAttacked = true; 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 shootLocationPos, Vector3? attackPos, Vector3 targetPosition, EAiType aiType, float atkStat, float shieldPenetrationRateStat, float inaccuracyStat, bool isOffense) { isAttacked = false; transform.position = shootLocationPos; if (attackPos != null) { attackerPos = (Vector3)attackPos; } targetPos = targetPosition; attackerAiType = aiType; atk = atkStat; shieldPenetrationRate = shieldPenetrationRateStat; inaccuracy = inaccuracyStat; this.isOffense = isOffense; } #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 } }