2023-08-09 07:44:09 +00:00
|
|
|
using System;
|
2023-08-03 08:00:14 +00:00
|
|
|
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)]
|
2023-08-21 18:08:11 +00:00
|
|
|
[SerializeField] private float autoDestroyTime = 5f;
|
2023-08-03 08:00:14 +00:00
|
|
|
|
2023-08-09 07:44:09 +00:00
|
|
|
[Tooltip("화살이 날아가는 속도")]
|
2023-08-21 18:08:11 +00:00
|
|
|
[SerializeField] private float arrowSpeed = 10f;
|
2023-08-09 07:44:09 +00:00
|
|
|
|
2023-08-21 18:08:11 +00:00
|
|
|
private bool isAttacked;
|
2023-08-09 07:44:09 +00:00
|
|
|
private float g = Mathf.Abs(Physics.gravity.y);
|
2023-08-10 18:03:23 +00:00
|
|
|
private float inaccuracy;
|
2023-08-17 07:57:46 +00:00
|
|
|
private Vector3? attackerPos;
|
2023-08-03 08:00:14 +00:00
|
|
|
private Vector3 targetPos;
|
2023-08-09 07:44:09 +00:00
|
|
|
private AttackerType attackerType;
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
public Coroutine shootCoroutine;
|
2023-08-09 07:44:09 +00:00
|
|
|
private Transform attackerTransform;
|
2023-08-03 08:00:14 +00:00
|
|
|
private AiStat attackerStat;
|
|
|
|
private Rigidbody arrowRigidbody;
|
2023-08-09 07:44:09 +00:00
|
|
|
private IObjectPool<Arrow> managedArrowPool;
|
2023-08-03 08:00:14 +00:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Unity built-in function
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
{
|
|
|
|
arrowRigidbody = Utils.GetComponentAndAssert<Rigidbody>(transform);
|
|
|
|
}
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
private void OnDisable()
|
|
|
|
{
|
|
|
|
if (shootCoroutine == null) return;
|
|
|
|
|
|
|
|
StopCoroutine(shootCoroutine);
|
|
|
|
shootCoroutine = null;
|
|
|
|
}
|
|
|
|
|
2023-08-03 08:00:14 +00:00
|
|
|
private void OnTriggerEnter(Collider other)
|
|
|
|
{
|
2023-08-17 07:57:46 +00:00
|
|
|
if (isAttacked) return;
|
|
|
|
|
2023-08-09 07:44:09 +00:00
|
|
|
if (other.gameObject.layer == LayerMask.NameToLayer("Ground") ||
|
|
|
|
other.gameObject.layer == LayerMask.NameToLayer("Water"))
|
|
|
|
{
|
2023-08-17 07:57:46 +00:00
|
|
|
isAttacked = true;
|
2023-08-09 07:44:09 +00:00
|
|
|
DestroyObject();
|
|
|
|
}
|
|
|
|
else if (other.gameObject.layer == LayerMask.NameToLayer("HitBox"))
|
|
|
|
{
|
|
|
|
switch (attackerType)
|
|
|
|
{
|
|
|
|
case AttackerType.NONE:
|
|
|
|
break;
|
2023-08-21 18:08:11 +00:00
|
|
|
case AttackerType.OFFENSE:
|
|
|
|
if (!other.gameObject.CompareTag("Enemy") && !other.gameObject.CompareTag("House"))
|
2023-08-09 07:44:09 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
2023-08-21 18:08:11 +00:00
|
|
|
case AttackerType.DEFENSE:
|
2023-08-17 07:57:46 +00:00
|
|
|
if (!other.gameObject.CompareTag("Player") && !other.gameObject.CompareTag("Pirate"))
|
2023-08-09 07:44:09 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
}
|
2023-08-15 20:36:04 +00:00
|
|
|
var iDamageable = other.GetComponentInParent<IDamageable>();
|
2023-08-17 07:57:46 +00:00
|
|
|
|
|
|
|
if (attackerPos != null)
|
|
|
|
{
|
|
|
|
iDamageable.TakeDamage(attackerStat, iDamageable.AiStat, (Vector3)attackerPos);
|
|
|
|
}
|
|
|
|
isAttacked = true;
|
2023-08-09 07:44:09 +00:00
|
|
|
DestroyObject();
|
|
|
|
}
|
2023-08-03 08:00:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Custom function
|
|
|
|
|
|
|
|
public IEnumerator Shoot()
|
|
|
|
{
|
|
|
|
var time = 0f;
|
2023-08-10 18:03:23 +00:00
|
|
|
// 화살의 목표 지점에 대한 불확실성 위치 값
|
|
|
|
var inaccuracyOffset = new Vector3
|
|
|
|
(
|
2023-08-09 07:44:09 +00:00
|
|
|
UnityEngine.Random.Range(-inaccuracy, inaccuracy),
|
|
|
|
UnityEngine.Random.Range(-inaccuracy, inaccuracy),
|
|
|
|
UnityEngine.Random.Range(-inaccuracy, inaccuracy)
|
|
|
|
);
|
2023-08-10 18:03:23 +00:00
|
|
|
// 화살의 최종 목표 지점
|
2023-08-09 07:44:09 +00:00
|
|
|
var inaccurateTargetPos = targetPos + inaccuracyOffset;
|
2023-08-15 20:36:04 +00:00
|
|
|
// 화살 자신의 위치 값
|
2023-08-10 18:03:23 +00:00
|
|
|
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;
|
2023-08-09 07:44:09 +00:00
|
|
|
|
2023-08-10 18:03:23 +00:00
|
|
|
arrowRigidbody.velocity = launchVelocity;
|
2023-08-15 20:36:04 +00:00
|
|
|
transform.rotation = Quaternion.LookRotation(arrowRigidbody.velocity) * Quaternion.Euler(0f, 90f, 0f);
|
2023-08-09 07:44:09 +00:00
|
|
|
|
2023-08-03 08:00:14 +00:00
|
|
|
while (time < autoDestroyTime)
|
|
|
|
{
|
2023-08-10 18:03:23 +00:00
|
|
|
time += Time.deltaTime;
|
2023-08-03 08:00:14 +00:00
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
if(arrowRigidbody.velocity != Vector3.zero)
|
|
|
|
{
|
|
|
|
transform.rotation = Quaternion.LookRotation(arrowRigidbody.velocity) * Quaternion.Euler(0f, 90f, 0f);
|
|
|
|
}
|
2023-08-03 08:00:14 +00:00
|
|
|
yield return null;
|
|
|
|
}
|
|
|
|
|
2023-08-09 07:44:09 +00:00
|
|
|
if (gameObject.activeSelf)
|
2023-08-03 08:00:14 +00:00
|
|
|
{
|
2023-08-09 07:44:09 +00:00
|
|
|
DestroyObject();
|
2023-08-03 08:00:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-15 20:36:04 +00:00
|
|
|
public void ShootArrowCoroutine()
|
|
|
|
{
|
|
|
|
if (shootCoroutine != null)
|
|
|
|
{
|
|
|
|
StopCoroutine(shootCoroutine);
|
|
|
|
shootCoroutine = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
shootCoroutine = StartCoroutine(Shoot());
|
|
|
|
}
|
|
|
|
|
2023-08-10 18:03:23 +00:00
|
|
|
/// <summary>
|
|
|
|
/// 화살이 발사 되기 직전에 필요한 데이터들을 입력받는 함수
|
|
|
|
/// </summary>
|
2023-08-17 07:57:46 +00:00
|
|
|
public void SetShootingArrow(Vector3 shootLocationPos, Vector3? attackPos, Vector3 targetPosition, AiStat attackerAiStat, AttackerType type, float inaccuracyValue)
|
2023-08-03 08:00:14 +00:00
|
|
|
{
|
2023-08-17 07:57:46 +00:00
|
|
|
isAttacked = false;
|
|
|
|
transform.position = shootLocationPos;
|
|
|
|
if (attackPos != null)
|
|
|
|
{
|
|
|
|
attackerPos = (Vector3)attackPos;
|
|
|
|
}
|
2023-08-03 08:00:14 +00:00
|
|
|
targetPos = targetPosition;
|
2023-08-09 07:44:09 +00:00
|
|
|
attackerStat = attackerAiStat;
|
|
|
|
attackerType = type;
|
|
|
|
inaccuracy = inaccuracyValue;
|
2023-08-03 08:00:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region ObjectPool function
|
|
|
|
|
|
|
|
private void DestroyObject() => managedArrowPool.Release(this);
|
|
|
|
|
|
|
|
public void SetManagedPool(IObjectPool<Arrow> pool) => managedArrowPool = pool;
|
|
|
|
|
|
|
|
#endregion
|
2023-08-09 07:44:09 +00:00
|
|
|
|
|
|
|
#region Custom function
|
|
|
|
|
2023-08-10 18:03:23 +00:00
|
|
|
/// <summary>
|
|
|
|
/// objectPool 시스템에 의해서 Release되는 경우 값을 초기화 시키는 함수
|
|
|
|
/// </summary>
|
2023-08-09 07:44:09 +00:00
|
|
|
public void ReleaseArrowSetting()
|
|
|
|
{
|
2023-08-15 20:36:04 +00:00
|
|
|
if (shootCoroutine != null)
|
|
|
|
{
|
|
|
|
StopCoroutine(shootCoroutine);
|
|
|
|
shootCoroutine = null;
|
|
|
|
}
|
|
|
|
|
2023-08-09 07:44:09 +00:00
|
|
|
arrowRigidbody.velocity = Vector3.zero;
|
|
|
|
arrowRigidbody.angularVelocity = Vector3.zero;
|
2023-08-15 20:36:04 +00:00
|
|
|
gameObject.SetActive(false);
|
2023-08-09 07:44:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
2023-08-03 08:00:14 +00:00
|
|
|
}
|
|
|
|
}
|