Projext/Assets/Scripts/Player/Controller/ArrowPickup.cs

229 lines
19 KiB
C#
Raw Normal View History

2026-02-12 15:23:25 +00:00
using UnityEngine; // 유니티 기본 기능을 쓰기 위해 불러올거에요 -> UnityEngine를
/// <summary> // 요약 주석을 시작할거에요 -> 클래스 설명을
/// 바닥에 떨어진 화살 아이템 (아이템 전용 — 발사체 기능 제거됨) // 설명할거에요 -> 이 스크립트 역할을
/// 습득 시 PlayerAttack에 파티클 프리팹 + 속성 정보를 전달합니다. // 설명할거에요 -> 습득 시 전달 내용
/// </summary> // 요약 주석을 끝낼거에요 -> 클래스 설명을
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를
/// <summary> // 요약 주석을 시작할거에요 -> ArrowData 패키징 설명을
/// [NEW] ArrowData 구조체로 정보 패키징 // 설명할거에요 -> PlayerAttack에 넘기기 쉽게 묶는 함수
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
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>(); // 컴포넌트를 가져올거에요 -> Rigidbody를 찾아 rb에 저장
if (pickupCollider == null || physicsCollider == null) // 조건을 검사할거에요 -> 콜라이더 참조가 비어있는지
{ // 코드 블록을 시작할거에요 -> 자동 탐지 처리
AutoDetectColliders(); // 함수를 실행할거에요 -> 콜라이더 자동 탐지
} // 코드 블록을 끝낼거에요 -> 자동 탐지 처리
} // 코드 블록을 끝낼거에요 -> Awake를
/// <summary> // 요약 주석을 시작할거에요 -> 콜라이더 자동 탐지 설명을
/// Collider 자동 탐지 (기존 로직 유지) // 설명할거에요 -> Sphere/Box를 찾아 자동 세팅
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
private void AutoDetectColliders() // 함수를 선언할거에요 -> 콜라이더를 자동으로 찾는 AutoDetectColliders를
{ // 코드 블록을 시작할거에요 -> AutoDetectColliders 범위를
Collider[] allColliders = GetComponents<Collider>(); // 배열을 가져올거에요 -> 내 오브젝트의 모든 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를
/// <summary> // 요약 주석을 시작할거에요 -> 아이템 모드 세팅 설명을
/// 아이템 모드 설정 (기존 로직 유지 + 단순화) // 설명할거에요 -> UI/콜라이더/물리 설정을 정리
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
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를
/// <summary> // 요약 주석을 시작할거에요 -> 습득 함수 설명을
/// [MODIFIED] PlayerInteraction에서 'E' 키를 눌렀을 때 호출 // 설명할거에요 -> 상호작용으로 호출되는 함수
/// 기존: SwapArrow(gameObject) → 변경: SetCurrentArrow(ArrowData) // 설명할거에요 -> 전달 방식 변경점
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
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을
/// <summary> // 요약 주석을 시작할거에요 -> 드롭 세팅 함수 설명을
/// 몬스터 드롭 시 화살 정보 설정 (기존 함수 유지) // 설명할거에요 -> 예전 호환용 함수
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
public void SetArrowData(string name, float dmg, float spd, float rng) // 함수를 선언할거에요 -> 간단 세팅용 SetArrowData를
{ // 코드 블록을 시작할거에요 -> SetArrowData 범위를
arrowName = name; // 값을 넣을거에요 -> 이름을 세팅
baseDamage = dmg; // 값을 넣을거에요 -> 기본 데미지를 세팅
} // 코드 블록을 끝낼거에요 -> SetArrowData를
/// <summary> // 요약 주석을 시작할거에요 -> 전체 세팅 함수 설명을
/// [NEW] 속성 정보까지 포함한 전체 세팅 함수 // 설명할거에요 -> 속성/파티클까지 세팅하는 버전
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
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을
/// <summary> // 요약 주석을 시작할거에요 -> 바닥 충돌 감쇠 설명을
/// 바닥 충돌 시 속도 감쇠 (기존 로직 유지) // 설명할거에요 -> 바닥에 튕김을 줄이기
/// </summary> // 요약 주석을 끝낼거에요 -> 설명 종료
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을