using UnityEngine; // 유니티 기본 기능을 쓰기 위해 불러올거에요 -> UnityEngine를 /// // 요약 주석을 시작할거에요 -> 클래스 설명을 /// 바닥에 떨어진 화살 아이템 (아이템 전용 — 발사체 기능 제거됨) // 설명할거에요 -> 이 스크립트 역할을 /// 습득 시 PlayerAttack에 파티클 프리팹 + 속성 정보를 전달합니다. // 설명할거에요 -> 습득 시 전달 내용 /// // 요약 주석을 끝낼거에요 -> 클래스 설명을 public class ArrowPickup : MonoBehaviour // 클래스를 선언할거에요 -> 화살 아이템 습득/표시를 담당하는 ArrowPickup을 { // 코드 블록을 시작할거에요 -> ArrowPickup 범위를 [Header("--- 화살 아이템 정보 ---")] // 인스펙터에 제목을 표시할거에요 -> 화살 아이템 정보 섹션을 [SerializeField] private string arrowName = "기본 화살"; // 변수를 선언할거에요 -> 화살 이름을 arrowName에 [SerializeField] private ArrowElementType elementType = ArrowElementType.None; // 변수를 선언할거에요 -> 화살 속성 타입을 elementType에 [Header("--- 스탯 ---")] // 인스펙터에 제목을 표시할거에요 -> 스탯 섹션을 [SerializeField] private float baseDamage = 15f; // 변수를 선언할거에요 -> 기본 데미지를 baseDamage에 [SerializeField] private float elementDamage = 5f; // 변수를 선언할거에요 -> 속성 데미지를 elementDamage에 [SerializeField] private float elementDuration = 3f; // 변수를 선언할거에요 -> 속성 지속시간을 elementDuration에 [Header("--- 발사체 파티클 ---")] // 인스펙터에 제목을 표시할거에요 -> 파티클 섹션을 [Tooltip("이 화살이 장착될 때 발사할 파티클 프리팹")] // 인스펙터에 툴팁을 달거에요 -> projectilePrefab 설명을 [SerializeField] private GameObject projectilePrefab; // 변수를 선언할거에요 -> 장착 시 쓸 파티클 프리팹을 projectilePrefab에 [Header("--- 비주얼 (필드 아이템) ---")] // 인스펙터에 제목을 표시할거에요 -> 비주얼 섹션을 [SerializeField] private GameObject visualModel; // 변수를 선언할거에요 -> 필드에서 보일 모델을 visualModel에 [Header("--- UI 표시 ---")] // 인스펙터에 제목을 표시할거에요 -> UI 표시 섹션을 [SerializeField] private GameObject pickupUI; // 변수를 선언할거에요 -> 습득 UI 오브젝트를 pickupUI에 [SerializeField] private float uiDisplayRange = 3f; // 변수를 선언할거에요 -> UI 표시 거리를 uiDisplayRange에 [Header("--- Collider 설정 (자동 탐지) ---")] // 인스펙터에 제목을 표시할거에요 -> 콜라이더 설정 섹션을 [Tooltip("아이템 습득용 Sphere Collider (Trigger)")] // 인스펙터에 툴팁을 달거에요 -> pickupCollider 설명을 [SerializeField] private Collider pickupCollider; // 변수를 선언할거에요 -> 습득용 트리거 콜라이더를 pickupCollider에 [Tooltip("바닥 충돌용 Box Collider (물리)")] // 인스펙터에 툴팁을 달거에요 -> physicsCollider 설명을 [SerializeField] private Collider physicsCollider; // 변수를 선언할거에요 -> 물리 충돌 콜라이더를 physicsCollider에 private Transform playerTransform; // 변수를 선언할거에요 -> 플레이어 트랜스폼을 저장할 playerTransform을 private Rigidbody rb; // 변수를 선언할거에요 -> 리지드바디를 저장할 rb를 /// // 요약 주석을 시작할거에요 -> ArrowData 패키징 설명을 /// [NEW] ArrowData 구조체로 정보 패키징 // 설명할거에요 -> PlayerAttack에 넘기기 쉽게 묶는 함수 /// // 요약 주석을 끝낼거에요 -> 설명 종료 public ArrowData GetArrowData() // 함수를 선언할거에요 -> 현재 화살 정보를 ArrowData로 반환하는 GetArrowData를 { // 코드 블록을 시작할거에요 -> GetArrowData 범위를 return new ArrowData // 새 ArrowData를 만들어 반환할거에요 -> 화살 정보를 채워서 { // 코드 블록을 시작할거에요 -> 초기화 블록 범위를 arrowName = this.arrowName, // 값을 넣을거에요 -> 화살 이름을 elementType = this.elementType, // 값을 넣을거에요 -> 속성 타입을 baseDamage = this.baseDamage, // 값을 넣을거에요 -> 기본 데미지를 elementDamage = this.elementDamage, // 값을 넣을거에요 -> 속성 데미지를 elementDuration = this.elementDuration, // 값을 넣을거에요 -> 속성 지속시간을 projectilePrefab = this.projectilePrefab // 값을 넣을거에요 -> 파티클 프리팹을 }; // ArrowData 생성을 끝낼거에요 -> 반환 준비 완료 } // 코드 블록을 끝낼거에요 -> GetArrowData를 private void Awake() // 함수를 선언할거에요 -> 오브젝트가 켜질 때 1번 실행되는 Awake를 { // 코드 블록을 시작할거에요 -> Awake 범위를 rb = GetComponent(); // 컴포넌트를 가져올거에요 -> Rigidbody를 찾아 rb에 저장 if (pickupCollider == null || physicsCollider == null) // 조건을 검사할거에요 -> 콜라이더 참조가 비어있는지 { // 코드 블록을 시작할거에요 -> 자동 탐지 처리 AutoDetectColliders(); // 함수를 실행할거에요 -> 콜라이더 자동 탐지 } // 코드 블록을 끝낼거에요 -> 자동 탐지 처리 } // 코드 블록을 끝낼거에요 -> Awake를 /// // 요약 주석을 시작할거에요 -> 콜라이더 자동 탐지 설명을 /// Collider 자동 탐지 (기존 로직 유지) // 설명할거에요 -> Sphere/Box를 찾아 자동 세팅 /// // 요약 주석을 끝낼거에요 -> 설명 종료 private void AutoDetectColliders() // 함수를 선언할거에요 -> 콜라이더를 자동으로 찾는 AutoDetectColliders를 { // 코드 블록을 시작할거에요 -> AutoDetectColliders 범위를 Collider[] allColliders = GetComponents(); // 배열을 가져올거에요 -> 내 오브젝트의 모든 Collider를 foreach (Collider col in allColliders) // 반복할거에요 -> 모든 콜라이더를 하나씩 { // 코드 블록을 시작할거에요 -> 콜라이더 판별 범위 if (col is SphereCollider && pickupCollider == null) // 조건을 검사할거에요 -> SphereCollider이고 pickupCollider가 비어있는지 { // 코드 블록을 시작할거에요 -> 습득용 콜라이더 설정 pickupCollider = col; // 값을 넣을거에요 -> 습득용 콜라이더로 등록 Debug.Log($"습득용 Collider: {col.GetType().Name}"); // 로그를 찍을거에요 -> 어떤 타입이 잡혔는지 } // 코드 블록을 끝낼거에요 -> 습득용 설정 else if (col is BoxCollider && physicsCollider == null) // 조건을 검사할거에요 -> BoxCollider이고 physicsCollider가 비어있는지 { // 코드 블록을 시작할거에요 -> 물리용 콜라이더 설정 physicsCollider = col; // 값을 넣을거에요 -> 물리용 콜라이더로 등록 Debug.Log($"물리용 Collider: {col.GetType().Name}"); // 로그를 찍을거에요 -> 어떤 타입이 잡혔는지 } // 코드 블록을 끝낼거에요 -> 물리용 설정 } // 코드 블록을 끝낼거에요 -> 콜라이더 판별 } // 코드 블록을 끝낼거에요 -> AutoDetectColliders를 private void Start() // 함수를 선언할거에요 -> 시작 시 1회 실행되는 Start를 { // 코드 블록을 시작할거에요 -> Start 범위를 SetupAsItem(); // 함수를 실행할거에요 -> 아이템 모드로 세팅 } // 코드 블록을 끝낼거에요 -> Start를 /// // 요약 주석을 시작할거에요 -> 아이템 모드 세팅 설명을 /// 아이템 모드 설정 (기존 로직 유지 + 단순화) // 설명할거에요 -> UI/콜라이더/물리 설정을 정리 /// // 요약 주석을 끝낼거에요 -> 설명 종료 private void SetupAsItem() // 함수를 선언할거에요 -> 아이템 상태로 세팅하는 SetupAsItem을 { // 코드 블록을 시작할거에요 -> SetupAsItem 범위를 GameObject player = GameObject.FindGameObjectWithTag("Player"); // 오브젝트를 찾을거에요 -> Player 태그 오브젝트를 if (player != null) // 조건을 검사할거에요 -> 플레이어를 찾았는지 { // 코드 블록을 시작할거에요 -> 플레이어 참조 저장 playerTransform = player.transform; // 값을 넣을거에요 -> 플레이어 위치를 저장 } // 코드 블록을 끝낼거에요 -> 플레이어 참조 저장 if (pickupUI != null) pickupUI.SetActive(false); // 조건이 맞으면 실행할거에요 -> 시작 시 UI를 숨기기 // Collider 설정 // 설명을 적을거에요 -> 습득용/물리용 콜라이더 모드 설정 if (pickupCollider != null) // 조건을 검사할거에요 -> 습득용 콜라이더가 있는지 { // 코드 블록을 시작할거에요 -> 습득용 콜라이더 설정 pickupCollider.enabled = true; // 값을 바꿀거에요 -> 습득용 콜라이더 활성화 pickupCollider.isTrigger = true; // 값을 바꿀거에요 -> 습득용은 트리거로 } // 코드 블록을 끝낼거에요 -> 습득용 설정 if (physicsCollider != null) // 조건을 검사할거에요 -> 물리용 콜라이더가 있는지 { // 코드 블록을 시작할거에요 -> 물리용 콜라이더 설정 physicsCollider.enabled = true; // 값을 바꿀거에요 -> 물리 콜라이더 활성화 physicsCollider.isTrigger = false; // 값을 바꿀거에요 -> 물리용은 트리거 아님 } // 코드 블록을 끝낼거에요 -> 물리용 설정 // 물리 설정 // 설명을 적을거에요 -> 리지드바디 세팅 if (rb != null) // 조건을 검사할거에요 -> Rigidbody가 있는지 { // 코드 블록을 시작할거에요 -> Rigidbody 설정 rb.useGravity = true; // 값을 바꿀거에요 -> 중력 적용 rb.isKinematic = false; // 값을 바꿀거에요 -> 물리 시뮬레이션 적용 rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; // 값을 바꿀거에요 -> 빠른 충돌도 놓치지 않게 } // 코드 블록을 끝낼거에요 -> Rigidbody 설정 } // 코드 블록을 끝낼거에요 -> SetupAsItem을 private void Update() // 함수를 선언할거에요 -> 매 프레임 실행되는 Update를 { // 코드 블록을 시작할거에요 -> Update 범위를 if (playerTransform == null || pickupUI == null) return; // 조건이 맞으면 종료할거에요 -> 플레이어/UI가 없으면 처리 불가 float distance = Vector3.Distance(transform.position, playerTransform.position); // 값을 계산할거에요 -> 플레이어와의 거리를 pickupUI.SetActive(distance <= uiDisplayRange); // 값을 적용할거에요 -> 거리 안이면 UI 켜고 밖이면 끄기 } // 코드 블록을 끝낼거에요 -> Update를 /// // 요약 주석을 시작할거에요 -> 습득 함수 설명을 /// [MODIFIED] PlayerInteraction에서 'E' 키를 눌렀을 때 호출 // 설명할거에요 -> 상호작용으로 호출되는 함수 /// 기존: SwapArrow(gameObject) → 변경: SetCurrentArrow(ArrowData) // 설명할거에요 -> 전달 방식 변경점 /// // 요약 주석을 끝낼거에요 -> 설명 종료 public void Pickup(PlayerAttack playerAttack) // 함수를 선언할거에요 -> 플레이어가 화살을 줍는 Pickup을 { // 코드 블록을 시작할거에요 -> Pickup 범위를 if (playerAttack == null) return; // 조건이 맞으면 종료할거에요 -> PlayerAttack이 없으면 진행 불가 ArrowData data = GetArrowData(); // 값을 만들거에요 -> 현재 화살 정보를 ArrowData로 패키징 playerAttack.SetCurrentArrow(data); // 함수를 실행할거에요 -> 플레이어 공격 스크립트에 현재 화살로 세팅 Debug.Log($"[{arrowName}] 화살 습득! 속성: {elementType}"); // 로그를 찍을거에요 -> 습득/속성 확인 Destroy(gameObject); // 오브젝트를 삭제할거에요 -> 습득했으니 아이템 제거 } // 코드 블록을 끝낼거에요 -> Pickup을 /// // 요약 주석을 시작할거에요 -> 드롭 세팅 함수 설명을 /// 몬스터 드롭 시 화살 정보 설정 (기존 함수 유지) // 설명할거에요 -> 예전 호환용 함수 /// // 요약 주석을 끝낼거에요 -> 설명 종료 public void SetArrowData(string name, float dmg, float spd, float rng) // 함수를 선언할거에요 -> 간단 세팅용 SetArrowData를 { // 코드 블록을 시작할거에요 -> SetArrowData 범위를 arrowName = name; // 값을 넣을거에요 -> 이름을 세팅 baseDamage = dmg; // 값을 넣을거에요 -> 기본 데미지를 세팅 } // 코드 블록을 끝낼거에요 -> SetArrowData를 /// // 요약 주석을 시작할거에요 -> 전체 세팅 함수 설명을 /// [NEW] 속성 정보까지 포함한 전체 세팅 함수 // 설명할거에요 -> 속성/파티클까지 세팅하는 버전 /// // 요약 주석을 끝낼거에요 -> 설명 종료 public void SetArrowDataFull( // 함수를 선언할거에요 -> 화살 모든 정보를 세팅하는 SetArrowDataFull을 string name, // 매개변수를 받을거에요 -> 화살 이름을 ArrowElementType element, // 매개변수를 받을거에요 -> 속성 타입을 float baseDmg, // 매개변수를 받을거에요 -> 기본 데미지를 float elemDmg, // 매개변수를 받을거에요 -> 속성 데미지를 float elemDur, // 매개변수를 받을거에요 -> 속성 지속 시간을 GameObject particlePrefab) // 매개변수를 받을거에요 -> 파티클 프리팹을 { // 코드 블록을 시작할거에요 -> SetArrowDataFull 범위를 arrowName = name; // 값을 넣을거에요 -> 이름 세팅 elementType = element; // 값을 넣을거에요 -> 속성 세팅 baseDamage = baseDmg; // 값을 넣을거에요 -> 기본 데미지 세팅 elementDamage = elemDmg; // 값을 넣을거에요 -> 속성 데미지 세팅 elementDuration = elemDur; // 값을 넣을거에요 -> 속성 지속시간 세팅 projectilePrefab = particlePrefab; // 값을 넣을거에요 -> 파티클 프리팹 세팅 } // 코드 블록을 끝낼거에요 -> SetArrowDataFull을 /// // 요약 주석을 시작할거에요 -> 바닥 충돌 감쇠 설명을 /// 바닥 충돌 시 속도 감쇠 (기존 로직 유지) // 설명할거에요 -> 바닥에 튕김을 줄이기 /// // 요약 주석을 끝낼거에요 -> 설명 종료 private void OnCollisionEnter(Collision collision) // 함수를 선언할거에요 -> 충돌이 발생하면 호출되는 OnCollisionEnter를 { // 코드 블록을 시작할거에요 -> OnCollisionEnter 범위를 if (rb != null && collision.gameObject.CompareTag("Ground")) // 조건을 검사할거에요 -> Rigidbody가 있고 땅에 닿았는지 { // 코드 블록을 시작할거에요 -> 감쇠 처리 rb.velocity *= 0.5f; // 값을 줄일거에요 -> 선속도를 절반으로 rb.angularVelocity *= 0.5f; // 값을 줄일거에요 -> 회전속도를 절반으로 } // 코드 블록을 끝낼거에요 -> 감쇠 처리 } // 코드 블록을 끝낼거에요 -> OnCollisionEnter를 #region 디버그 시각화 // 영역을 표시할거에요 -> Gizmos 관련 코드 묶음임을 private void OnDrawGizmosSelected() // 함수를 선언할거에요 -> 선택됐을 때 Gizmo를 그리는 OnDrawGizmosSelected를 { // 코드 블록을 시작할거에요 -> OnDrawGizmosSelected 범위를 if (pickupCollider != null && pickupCollider.enabled) // 조건을 검사할거에요 -> 습득용 콜라이더가 있고 켜져있는지 { // 코드 블록을 시작할거에요 -> 습득 범위 시각화 Gizmos.color = Color.green; // 색을 정할거에요 -> 초록색으로 if (pickupCollider is SphereCollider sphere) // 조건을 검사할거에요 -> 습득용이 SphereCollider인지 { // 코드 블록을 시작할거에요 -> 구체 범위 그리기 Gizmos.DrawWireSphere(transform.position + sphere.center, sphere.radius); // 선 구체를 그릴거에요 -> 습득 범위를 } // 코드 블록을 끝낼거에요 -> 구체 그리기 } // 코드 블록을 끝낼거에요 -> 습득 범위 시각화 if (physicsCollider != null && physicsCollider.enabled) // 조건을 검사할거에요 -> 물리 콜라이더가 있고 켜져있는지 { // 코드 블록을 시작할거에요 -> 물리 범위 시각화 Gizmos.color = Color.blue; // 색을 정할거에요 -> 파란색으로 if (physicsCollider is BoxCollider box) // 조건을 검사할거에요 -> 물리용이 BoxCollider인지 { // 코드 블록을 시작할거에요 -> 박스 범위 그리기 Gizmos.matrix = transform.localToWorldMatrix; // 좌표계를 바꿀거에요 -> 오브젝트 로컬 기준으로 Gizmos.DrawWireCube(box.center, box.size); // 선 박스를 그릴거에요 -> 물리 충돌 범위를 } // 코드 블록을 끝낼거에요 -> 박스 그리기 } // 코드 블록을 끝낼거에요 -> 물리 범위 시각화 // [NEW] 속성 색상 표시 // 설명을 적을거에요 -> 속성 타입에 따라 색을 다르게 switch (elementType) // 분기할거에요 -> elementType에 따라 { // 코드 블록을 시작할거에요 -> switch 범위를 case ArrowElementType.Fire: Gizmos.color = Color.red; break; // 빨강으로 칠할거에요 -> 불 속성이면 case ArrowElementType.Ice: Gizmos.color = Color.cyan; break; // 하늘색으로 칠할거에요 -> 얼음 속성이면 case ArrowElementType.Poison: Gizmos.color = new Color(0.5f, 1f, 0f); break; // 연두로 칠할거에요 -> 독 속성이면 case ArrowElementType.Lightning: Gizmos.color = Color.yellow; break; // 노랑으로 칠할거에요 -> 번개 속성이면 default: Gizmos.color = Color.white; break; // 흰색으로 칠할거에요 -> 기본/없음이면 } // 코드 블록을 끝낼거에요 -> switch를 Gizmos.DrawWireSphere(transform.position, 0.3f); // 선 구체를 그릴거에요 -> 아이템 위치에 속성 표시용으로 } // 코드 블록을 끝낼거에요 -> OnDrawGizmosSelected를 #endregion // 영역을 끝낼거에요 -> Gizmos 묶음을 } // 코드 블록을 끝낼거에요 -> ArrowPickup을