CapersProject/Assets/02.Scripts/ProjectileController.cs

186 lines
6.7 KiB
C#
Raw Normal View History

2024-06-03 18:26:03 +00:00
using System;
using BlueWater.Audios;
2024-06-03 18:26:03 +00:00
using BlueWater.Interfaces;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
2024-06-03 18:26:03 +00:00
namespace BlueWater
{
public class ProjectileController : MonoBehaviour
2024-06-03 18:26:03 +00:00
{
[Title("컴포넌트")]
[SerializeField, Required]
private Rigidbody _rigidbody;
[SerializeField]
private SphereCollider _sphereCollider;
2024-06-03 18:26:03 +00:00
[field: Title("파티클 설정")]
[FormerlySerializedAs("_projectileParticle")]
2024-06-03 18:26:03 +00:00
[SerializeField]
private GameObject _projectilePrefab;
2024-06-03 18:26:03 +00:00
[SerializeField]
private GameObject _muzzleParticle;
[field: SerializeField]
public GameObject ImpactParticle { get; private set; }
2024-06-03 18:26:03 +00:00
[Title("충돌체 설정")]
[SerializeField, Tooltip("Sphere Collider가 없는 경우, 기본 충돌 크기(반지름)"), ShowIf("@!_sphereCollider")]
2024-06-03 18:26:03 +00:00
private float _colliderRadius = 1f;
// [SerializeField, Range(0f, 1f), Tooltip("타겟보다 해당 값만큼 떨어진 위치에서 충돌")]
// private float _collideOffset;
2024-06-03 18:26:03 +00:00
[SerializeField]
private int _attackDamage;
[SerializeField]
private LayerMask _targetLayer;
[SerializeField]
private bool _useAutoDestroy = true;
[SerializeField, ShowIf("@_useAutoDestroy")]
private float _autoDestroyTime = 10f;
[SerializeField]
private string _awakeSfxName;
public float SphereRadius { get; private set; }
2024-06-03 18:26:03 +00:00
private float _detectionDistance;
private Collider[] _hitColliders;
private bool _isHitCheckRealtime;
2024-06-03 18:26:03 +00:00
public Action OnHitAction;
private void OnDrawGizmosSelected()
{
if (SphereRadius == 0f) return;
2024-06-03 18:26:03 +00:00
var direction = _rigidbody ? _rigidbody.linearVelocity.normalized : transform.forward;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, SphereRadius);
2024-06-03 18:26:03 +00:00
Gizmos.DrawLine(transform.position, transform.position + direction * _detectionDistance);
}
private void Start()
{
if (!string.IsNullOrEmpty(_awakeSfxName))
{
AudioManager.Instance.PlaySfx(_awakeSfxName);
}
SphereRadius = _sphereCollider ? _sphereCollider.radius : _colliderRadius;
_hitColliders = new Collider[1];
2024-06-03 18:26:03 +00:00
if (_useAutoDestroy)
{
Destroy(gameObject, _autoDestroyTime);
}
if (_projectilePrefab)
{
_projectilePrefab = Instantiate(_projectilePrefab, transform.position, transform.rotation, transform);
}
2024-06-03 18:26:03 +00:00
if (_muzzleParticle)
{
_muzzleParticle = Instantiate(_muzzleParticle, transform.position, transform.rotation, transform);
Destroy(_muzzleParticle, 1.5f);
}
}
private void FixedUpdate()
{
if (_rigidbody.linearVelocity.magnitude != 0)
{
transform.rotation = Quaternion.LookRotation(_rigidbody.linearVelocity);
}
//var direction = _rigidbody.linearVelocity.normalized;
2024-06-03 18:26:03 +00:00
_detectionDistance = _rigidbody.linearVelocity.magnitude * Time.deltaTime;
if (!_isHitCheckRealtime) return;
var hitCount = Physics.OverlapSphereNonAlloc(transform.position, SphereRadius, _hitColliders, _targetLayer, QueryTriggerInteraction.Collide);
if (hitCount == 0) return;
2024-06-03 18:26:03 +00:00
// transform.position = raycastHit.point + raycastHit.normal * _collideOffset;
var hitCollider = _hitColliders[0];
2024-06-03 18:26:03 +00:00
var trailParticles = GetComponentsInChildren<ParticleSystem>();
foreach (var element in trailParticles)
{
if (!element.gameObject.name.Contains("Trail")) continue;
element.transform.SetParent(null);
Destroy(element.gameObject, 2f);
}
var impactParticle = Instantiate(ImpactParticle, transform.position, Quaternion.identity);
2024-06-03 18:26:03 +00:00
// TODO : HitBox가 레이어로 설정되어있으도, 부모 객체 Player를 계속 가져오는 버그가 있음
var iDamageable = hitCollider.GetComponentInParent<IDamageable>();
2024-06-03 18:26:03 +00:00
if (iDamageable != null && iDamageable.CanDamage())
{
iDamageable.TakeDamage(_attackDamage);
OnHitAction?.Invoke();
}
Destroy(_projectilePrefab, 3f);
2024-06-03 18:26:03 +00:00
if (impactParticle)
{
Destroy(impactParticle, 3.5f);
}
Destroy(gameObject);
}
public void DoAttack()
{
_hitColliders = new Collider[8];
var hitCount = Physics.OverlapSphereNonAlloc(transform.position, SphereRadius, _hitColliders, _targetLayer, QueryTriggerInteraction.Collide);
// transform.position = raycastHit.point + raycastHit.normal * _collideOffset;
var trailParticles = GetComponentsInChildren<ParticleSystem>();
foreach (var element in trailParticles)
{
if (!element.gameObject.name.Contains("Trail")) continue;
element.transform.SetParent(null);
Destroy(element.gameObject, 2f);
}
for (var i = 0; i < hitCount; i++)
{
// TODO : HitBox가 레이어로 설정되어있으도, 부모 객체 Player를 계속 가져오는 버그가 있음
var iDamageable = _hitColliders[i].GetComponentInParent<IDamageable>();
if (iDamageable == null || !iDamageable.CanDamage()) continue;
iDamageable.TakeDamage(_attackDamage);
OnHitAction?.Invoke();
}
var impactParticle = Instantiate(ImpactParticle, transform.position, Quaternion.identity);
Destroy(_projectilePrefab, 3f);
if (impactParticle)
{
Destroy(impactParticle, 3.5f);
}
Destroy(gameObject);
}
public void Initialize(int attackDamage, LayerMask targetLayer, bool isHitCheckRealtime = true, Action onHitAction = null)
2024-06-03 18:26:03 +00:00
{
_attackDamage = attackDamage;
_targetLayer = targetLayer;
_isHitCheckRealtime = isHitCheckRealtime;
2024-06-03 18:26:03 +00:00
OnHitAction = onHitAction;
}
public void AddForce(Vector3 force, ForceMode forceMode) => _rigidbody.AddForce(force, forceMode);
}
}