using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을 /// /// 만능 투사체 (화살, 마법, 돌맹이 공용) /// public class Projectile : MonoBehaviour // 클래스를 선언할거에요 -> MonoBehaviour를 상속받는 Projectile을 { [Header("기본 설정")] // 인스펙터 창에 제목을 표시할거에요 -> 기본 설정 을 [SerializeField] private float lifetime = 5f; // 변수를 선언할거에요 -> 수명(5.0초)을 lifetime에 [SerializeField] private GameObject hitEffectPrefab; // 변수를 선언할거에요 -> 충돌 이펙트 프리팹을 hitEffectPrefab에 [SerializeField] private bool destroyOnHit = true; // 변수를 선언할거에요 -> 충돌 시 삭제 여부를 destroyOnHit에 // 내부 변수 private Vector3 _direction; // 변수를 선언할거에요 -> 날아갈 방향을 _direction에 private float _speed; // 변수를 선언할거에요 -> 이동 속도를 _speed에 private float _damage; // 변수를 선언할거에요 -> 공격 데미지를 _damage에 private bool _isInitialized = false; // 변수를 초기화할거에요 -> 초기화 여부를 거짓(false)으로 private Rigidbody _rigidbody; // 변수를 선언할거에요 -> 물리 컴포넌트 _rigidbody를 private void Awake() // 함수를 실행할거에요 -> 스크립트 시작 시 호출되는 Awake를 { _rigidbody = GetComponent(); // 컴포넌트를 가져올거에요 -> 리지드바디를 } /// /// 몬스터가 발사할 때 호출하는 초기화 함수 /// /// 날아갈 방향 /// 속도 (0이면 물리력으로 날아감) /// 줄 데미지 public void Initialize(Vector3 direction, float speed, float damage) // 함수를 선언할거에요 -> 투사체 정보를 설정하는 Initialize를 { _direction = direction.normalized; // 값을 저장할거에요 -> 방향 벡터를 정규화해서 _speed = speed; // 값을 저장할거에요 -> 속도를 _damage = damage; // 값을 저장할거에요 -> 데미지를 _isInitialized = true; // 상태를 바꿀거에요 -> 초기화 완료 상태를 참으로 // 속도가 있다? -> 화살/마법 (직선 운동) if (_rigidbody != null && speed > 0) // 조건이 맞으면 실행할거에요 -> 리지드바디가 있고 속도가 있다면 { _rigidbody.velocity = _direction * _speed; // 값을 넣을거에요 -> 속도 벡터를 리지드바디 속도에 _rigidbody.useGravity = false; // 설정을 바꿀거에요 -> 중력 영향을 끄기로 (직선 비행) } // 속도가 0이다? -> 돌맹이 (외부에서 물리력 가함) else if (_rigidbody != null && speed == 0) // 조건이 맞으면 실행할거에요 -> 리지드바디가 있고 속도가 0이라면 { _rigidbody.useGravity = true; // 설정을 바꿀거에요 -> 중력 영향을 켜기로 (포물선 비행) } // 수명 지나면 자동 삭제 Destroy(gameObject, lifetime); // 파괴할거에요 -> 이 오브젝트를 수명 시간이 지나면 } private void FixedUpdate() // 함수를 실행할거에요 -> 물리 연산 주기로 호출되는 FixedUpdate를 { if (!_isInitialized) return; // 조건이 맞으면 중단할거에요 -> 초기화되지 않았다면 // 리지드바디가 없거나, Kinematic인 경우 (강제 이동 보정) if (_rigidbody == null || _rigidbody.isKinematic) // 조건이 맞으면 실행할거에요 -> 물리 이동을 안 쓰는 상태라면 { if (_speed > 0) // 조건이 맞으면 실행할거에요 -> 속도가 설정되어 있다면 { transform.position += _direction * _speed * Time.fixedDeltaTime; // 이동시킬거에요 -> 방향과 속도에 맞춰 직접 위치를 } } } private void OnTriggerEnter(Collider other) // 함수를 실행할거에요 -> 충돌이 발생했을 때 OnTriggerEnter를 { // 1. 적(몬스터)끼리 맞으면 무시 if (other.CompareTag("Enemy") || other.gameObject == gameObject) return; // 조건이 맞으면 중단할거에요 -> 적끼리 충돌했거나 자기 자신이라면 // 2. 투사체끼리 충돌 무시 if (other.GetComponent()) return; // 조건이 맞으면 중단할거에요 -> 다른 투사체와 충돌했다면 // 3. 플레이어 피격 처리 if (other.CompareTag("Player")) // 조건이 맞으면 실행할거에요 -> 충돌 대상이 플레이어라면 { // PlayerHealth 혹은 IDamageable 스크립트 찾기 var playerHealth = other.GetComponent(); // 컴포넌트를 가져올거에요 -> 플레이어 체력 스크립트를 if (playerHealth != null) // 조건이 맞으면 실행할거에요 -> 체력 스크립트가 있다면 { // 무적 상태 체크 (있다면) if (!playerHealth.isInvincible) // 조건이 맞으면 실행할거에요 -> 플레이어가 무적 상태가 아니라면 { playerHealth.TakeDamage(_damage); // 함수를 실행할거에요 -> 데미지를 입히는 TakeDamage를 Debug.Log($"🎯 [적중] 플레이어에게 {_damage} 데미지!"); // 로그를 출력할거에요 -> 적중 메시지를 } } } // 4. 벽이나 땅에 닿았을 때도 이펙트 if (other.CompareTag("Ground") || other.CompareTag("Wall") || other.CompareTag("Player")) // 조건이 맞으면 실행할거에요 -> 땅, 벽, 플레이어에 닿았다면 { if (hitEffectPrefab != null) // 조건이 맞으면 실행할거에요 -> 충돌 이펙트 프리팹이 있다면 { Instantiate(hitEffectPrefab, transform.position, Quaternion.identity); // 생성할거에요 -> 이펙트를 충돌 위치에 } if (destroyOnHit) // 조건이 맞으면 실행할거에요 -> 충돌 시 삭제 옵션이 켜져 있다면 { Destroy(gameObject); // 파괴할거에요 -> 이 투사체 오브젝트를 } } } }