using UnityEngine; // 유니티 엔진 기본 네임스페이스
///
/// 활 쏠 때만 화살을 왼손에 표시하는 컴포넌트
/// Player 오브젝트에 부착하여 사용
///
public class ArrowHolder : MonoBehaviour // MonoBehaviour를 상속받아 컴포넌트로 사용
{
[Header("=== 화살 설정 ===")] // 인스펙터에서 섹션 구분용 헤더
[Tooltip("화살 프리팹을 여기에 드래그")] // 인스펙터 툴팁 설명
[SerializeField] private GameObject arrowPrefab; // 화살 프리팹 참조 (인스펙터에서 할당)
[Tooltip("화살을 붙일 뼈 이름 (왼손 뼈)")] // 인스펙터 툴팁 설명
[SerializeField] private string handBoneName = "RogueHooded_ArmLeft"; // 왼손 뼈 이름 (스크린샷 기준 기본값)
[Header("=== 화살 위치/회전 오프셋 ===")] // 인스펙터에서 섹션 구분용 헤더
[Tooltip("손 기준 화살 위치 오프셋")] // 인스펙터 툴팁 설명
[SerializeField] private Vector3 positionOffset = Vector3.zero; // 손 뼈 기준 화살 위치 보정값
[Tooltip("손 기준 화살 회전 오프셋")] // 인스펙터 툴팁 설명
[SerializeField] private Vector3 rotationOffset = Vector3.zero; // 손 뼈 기준 화살 회전 보정값
private GameObject arrowInstance; // 생성된 화살 인스턴스를 저장할 변수
private Transform handBone; // 왼손 뼈의 Transform 캐싱용 변수
private bool isInitialized; // 초기화 완료 여부 플래그
///
/// 시작 시 손 뼈를 찾고 화살 인스턴스를 미리 생성해둠
///
private void Start() // 게임 시작 시 1회 호출
{
InitializeArrow(); // 화살 초기화 함수 호출
}
///
/// 화살 초기화 — 손 뼈 탐색 + 화살 인스턴스 생성 + 비활성화
///
private void InitializeArrow() // 화살 셋업을 담당하는 함수
{
// --- 1. 프리팹 유효성 검사 ---
if (arrowPrefab == null) // 화살 프리팹이 할당되지 않았으면
{
Debug.LogError("[ArrowHolder] arrowPrefab이 할당되지 않았습니다!"); // 에러 로그 출력
return; // 초기화 중단
}
// --- 2. 손 뼈 탐색 (재귀적으로 자식까지 전부 검색) ---
handBone = FindBoneRecursive(transform, handBoneName); // 이름으로 뼈를 재귀 탐색
if (handBone == null) // 손 뼈를 찾지 못했으면
{
Debug.LogError($"[ArrowHolder] '{handBoneName}' 뼈를 찾을 수 없습니다! 뼈 이름을 확인하세요."); // 에러 로그 출력
return; // 초기화 중단
}
// --- 3. 화살 인스턴스 생성 후 손 뼈에 자식으로 붙이기 ---
arrowInstance = Instantiate(arrowPrefab, handBone); // 프리팹을 손 뼈의 자식으로 인스턴스화
arrowInstance.name = "Arrow_Instance"; // 인스턴스 이름을 보기 쉽게 설정
// --- 4. 오프셋 적용 (인스펙터에서 조절 가능) ---
arrowInstance.transform.localPosition = positionOffset; // 로컬 위치를 오프셋값으로 설정
arrowInstance.transform.localRotation = Quaternion.Euler(rotationOffset); // 로컬 회전을 오프셋값으로 설정
// --- 5. 기본 비활성화 (쏠 때만 보이게) ---
arrowInstance.SetActive(false); // 화살을 비활성화 상태로 시작
isInitialized = true; // 초기화 완료 표시
Debug.Log($"[ArrowHolder] 화살이 '{handBoneName}' 뼈에 성공적으로 부착되었습니다."); // 성공 로그 출력
}
///
/// 화살 표시 — 차징/발사 애니메이션 시작할 때 호출
///
public void ShowArrow() // 외부에서 호출하여 화살을 보이게 하는 함수
{
if (!isInitialized) return; // 초기화 안 됐으면 무시
if (arrowInstance != null) // 화살 인스턴스가 유효하면
{
arrowInstance.SetActive(true); // 화살 활성화 (보이게)
}
}
///
/// 화살 숨기기 — 발사 후 또는 취소 시 호출
///
public void HideArrow() // 외부에서 호출하여 화살을 숨기는 함수
{
if (!isInitialized) return; // 초기화 안 됐으면 무시
if (arrowInstance != null) // 화살 인스턴스가 유효하면
{
arrowInstance.SetActive(false); // 화살 비활성화 (숨기기)
}
}
///
/// 재귀적으로 자식 Transform을 탐색하여 이름이 일치하는 뼈를 찾음
///
/// 탐색 시작할 부모 Transform
/// 찾을 뼈 이름
/// 찾은 뼈의 Transform, 없으면 null
private Transform FindBoneRecursive(Transform parent, string boneName) // 뼈를 재귀적으로 찾는 함수
{
// --- 현재 오브젝트 이름이 일치하는지 확인 ---
if (parent.name == boneName) // 이름이 정확히 일치하면
{
return parent; // 해당 Transform 반환
}
// --- 모든 자식을 순회하며 재귀 탐색 ---
for (int i = 0; i < parent.childCount; i++) // 자식 수만큼 반복
{
Transform found = FindBoneRecursive(parent.GetChild(i), boneName); // 자식에서 재귀적으로 탐색
if (found != null) // 찾았으면
{
return found; // 결과 반환
}
}
return null; // 못 찾았으면 null 반환
}
///
/// 인스펙터에서 오프셋을 실시간으로 조절할 때 반영되게 함
///
private void OnValidate() // 인스펙터 값이 변경될 때 자동 호출
{
if (arrowInstance != null) // 화살 인스턴스가 존재하면
{
arrowInstance.transform.localPosition = positionOffset; // 변경된 위치 오프셋 즉시 반영
arrowInstance.transform.localRotation = Quaternion.Euler(rotationOffset); // 변경된 회전 오프셋 즉시 반영
}
}
}