OldBlueWater/BlueWater/Assets/02.Scripts/Weapon/Arrow.cs

229 lines
7.8 KiB
C#
Raw Normal View History

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;
2023-09-04 07:31:04 +00:00
private bool isOffense;
private float g = Mathf.Abs(Physics.gravity.y);
private float inaccuracy;
2023-09-18 00:21:55 +00:00
private float atk;
private float shieldPenetrationRate;
private Vector3? attackerPos;
private Vector3 targetPos;
2023-09-04 07:31:04 +00:00
private EAiType attackerAiType;
public Coroutine shootCoroutine;
private Transform attackerTransform;
private Rigidbody arrowRigidbody;
private IObjectPool<Arrow> managedArrowPool;
#endregion
#region Unity built-in function
private void Awake()
{
arrowRigidbody = Utils.GetComponentAndAssert<Rigidbody>(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();
}
2023-09-04 07:31:04 +00:00
else if (other.gameObject.layer == LayerMask.NameToLayer("Props"))
{
2023-09-18 00:21:55 +00:00
if (isOffense)
{
if (other.gameObject.CompareTag("House") || other.gameObject.CompareTag("Tower"))
2023-09-18 00:21:55 +00:00
{
var iDamageable = other.GetComponentInParent<IDamageable>();
iDamageable.TakeDamage(atk, shieldPenetrationRate);
isAttacked = true;
DestroyObject();
}
}
else
{
isAttacked = true;
DestroyObject();
}
2023-09-04 07:31:04 +00:00
}
else if (other.gameObject.layer == LayerMask.NameToLayer("HitBox"))
{
2023-09-04 07:31:04 +00:00
switch (attackerAiType)
{
2023-09-04 07:31:04 +00:00
case EAiType.NONE:
print("aiType == NONE Error");
break;
2023-09-04 07:31:04 +00:00
case EAiType.PLAYER:
case EAiType.PIRATE:
if (other.gameObject.CompareTag("Enemy"))
{
2023-09-04 07:31:04 +00:00
break;
}
2023-09-04 07:31:04 +00:00
return;
case EAiType.ENEMY:
if (other.gameObject.CompareTag("Pirate") || other.gameObject.CompareTag("Player"))
{
2023-09-04 07:31:04 +00:00
break;
}
2023-09-04 07:31:04 +00:00
return;
default:
throw new ArgumentOutOfRangeException();
}
var iDamageable = other.GetComponentInParent<IDamageable>();
if (attackerPos != null)
{
2023-09-18 00:21:55 +00:00
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());
}
/// <summary>
/// 화살이 발사 되기 직전에 필요한 데이터들을 입력받는 함수
/// </summary>
2023-09-18 00:21:55 +00:00
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;
2023-09-04 07:31:04 +00:00
attackerAiType = aiType;
2023-09-18 00:21:55 +00:00
atk = atkStat;
shieldPenetrationRate = shieldPenetrationRateStat;
inaccuracy = inaccuracyStat;
2023-09-04 07:31:04 +00:00
this.isOffense = isOffense;
}
#endregion
#region ObjectPool function
private void DestroyObject() => managedArrowPool.Release(this);
public void SetManagedPool(IObjectPool<Arrow> pool) => managedArrowPool = pool;
#endregion
#region Custom function
/// <summary>
/// objectPool 시스템에 의해서 Release되는 경우 값을 초기화 시키는 함수
/// </summary>
public void ReleaseArrowSetting()
{
if (shootCoroutine != null)
{
StopCoroutine(shootCoroutine);
shootCoroutine = null;
}
arrowRigidbody.velocity = Vector3.zero;
arrowRigidbody.angularVelocity = Vector3.zero;
gameObject.SetActive(false);
}
2023-09-20 03:30:58 +00:00
public float GetArrowSpeed() => arrowSpeed;
#endregion
}
}