135 lines
7.2 KiB
C#
135 lines
7.2 KiB
C#
using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
|
|
using UnityEngine.Events; // 유니티 이벤트 기능을 사용할거에요 -> UnityEngine.Events를
|
|
using System; // C# 기본 이벤트(Action)를 사용할거에요 -> System을
|
|
using System.Collections; // 코루틴을 사용할거에요 -> System.Collections를
|
|
|
|
/// <summary>
|
|
/// 플레이어의 체력, 피격, 무적, 사망을 관리합니다.
|
|
/// </summary>
|
|
public class PlayerHealth : MonoBehaviour, IDamageable // 클래스를 선언할거에요 -> PlayerHealth를
|
|
{
|
|
[Header("=== 참조 ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 참조 === 를
|
|
[SerializeField] private Stats playerStats; // 변수를 선언할거에요 -> 스탯 스크립트를 playerStats에
|
|
|
|
[Header("=== 설정 ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 설정 === 을
|
|
[SerializeField] private float invincibleDuration = 1.0f; // 변수를 선언할거에요 -> 무적 시간을 invincibleDuration에
|
|
[SerializeField] private string hitAnimationName = "Player_Hit"; // 변수를 선언할거에요 -> 피격 애니메이션 이름을 hitAnimationName에
|
|
|
|
[Header("=== 유니티 이벤트 (인스펙터 연결용) ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 유니티 이벤트 === 를
|
|
public UnityEvent OnDieEvent; // 이벤트를 선언할거에요 -> 사망 시 발생할 UnityEvent인 OnDieEvent를
|
|
public UnityEvent<float> OnHealthChanged; // 이벤트를 선언할거에요 -> 체력 변경 시 발생할 OnHealthChanged를
|
|
|
|
// ⭐ [이벤트] += 연산자 오류 방지를 위해 event Action 사용
|
|
public event Action OnHitEvent;
|
|
public event Action OnHit;
|
|
public event Action OnDie;
|
|
public event Action OnDead;
|
|
|
|
// ⭐ [핵심 수정 1] 외부에서 수정 가능하도록 set 권한 개방
|
|
public float CurrentHP { get; private set; } // (체력은 함부로 바꾸면 안되니 private set 유지)
|
|
|
|
// "set 접근자에 액세스할 수 없습니다" 오류 해결 -> private 제거
|
|
public bool isInvincible { get; set; } = false;
|
|
|
|
public bool IsDead { get; private set; } = false; // 사망 여부는 내부에서만 관리
|
|
|
|
private Animator animator; // 변수를 선언할거에요 -> 애니메이터 컴포넌트를 담을 animator를
|
|
|
|
// ⭐ [핵심 수정 2] "보호 수준 때문에 액세스 불가" 오류 해결 -> public으로 변경
|
|
public bool isHit = false;
|
|
|
|
private void Awake() // 함수를 실행할거에요 -> 초기화 Awake를
|
|
{
|
|
animator = GetComponentInChildren<Animator>(); // 컴포넌트를 가져올거에요 -> 자식의 애니메이터를
|
|
if (playerStats == null) playerStats = GetComponent<Stats>(); // 조건이 맞으면 가져올거에요 -> 스탯 컴포넌트가 비어있다면 내 몸에서
|
|
}
|
|
|
|
private void Start() // 함수를 실행할거에요 -> 시작 Start를
|
|
{
|
|
if (playerStats != null) // 조건이 맞으면 실행할거에요 -> 스탯이 있다면
|
|
{
|
|
CurrentHP = playerStats.MaxHealth; // 값을 초기화할거에요 -> 현재 체력을 최대 체력으로
|
|
}
|
|
else // 조건이 틀리면 실행할거에요 -> 스탯이 없다면
|
|
{
|
|
CurrentHP = 100f; // 값을 넣을거에요 -> 기본 체력 100으로
|
|
Debug.LogWarning("⚠️ Stats 컴포넌트가 없습니다! 기본 체력 100 적용."); // 경고를 출력할거에요
|
|
}
|
|
|
|
RefreshHealthUI(); // 함수를 실행할거에요 -> 초기 체력 UI 갱신을
|
|
}
|
|
|
|
public void TakeDamage(float damage) // 함수를 선언할거에요 -> 외부에서 호출할 피격 함수 TakeDamage를
|
|
{
|
|
if (IsDead || isInvincible) return; // 조건이 맞으면 중단할거에요 -> 죽었거나 무적이라면
|
|
|
|
// 실제 데미지 적용
|
|
CurrentHP -= damage; // 값을 뺄거에요 -> 체력에서 데미지를
|
|
if (CurrentHP < 0) CurrentHP = 0; // 조건이 맞으면 보정할거에요 -> 체력이 음수가 안 되게 0으로
|
|
|
|
Debug.Log($"💥 피격! 데미지: {damage}, 남은 체력: {CurrentHP}"); // 로그를 출력할거에요 -> 피격 정보를
|
|
|
|
RefreshHealthUI(); // 함수를 실행할거에요 -> 체력바 갱신을
|
|
|
|
if (CurrentHP <= 0) // 조건이 맞으면 실행할거에요 -> 체력이 바닥났다면
|
|
{
|
|
Die(); // 함수를 실행할거에요 -> 사망 처리 Die를
|
|
}
|
|
else // 조건이 틀리면 실행할거에요 -> 아직 살았다면
|
|
{
|
|
StartCoroutine(HitRoutine()); // 코루틴을 실행할거에요 -> 피격 무적/경직 처리를
|
|
|
|
// 모든 이벤트 호출
|
|
OnHit?.Invoke();
|
|
OnHitEvent?.Invoke();
|
|
}
|
|
}
|
|
|
|
private void Die() // 함수를 선언할거에요 -> 사망 처리 Die를
|
|
{
|
|
if (IsDead) return; // 조건이 맞으면 중단할거에요 -> 이미 죽었다면
|
|
|
|
IsDead = true; // 상태를 바꿀거에요 -> 사망 상태를 참으로
|
|
|
|
OnDieEvent?.Invoke(); // 유니티 이벤트
|
|
OnDie?.Invoke(); // C# 이벤트
|
|
OnDead?.Invoke(); // C# 이벤트 (호환용)
|
|
|
|
Debug.Log("💀 플레이어 사망!"); // 로그를 출력할거에요 -> 사망 메시지를
|
|
}
|
|
|
|
public void RefreshHealthUI() // 함수를 선언할거에요 -> UI를 갱신하는 RefreshHealthUI를
|
|
{
|
|
float ratio = (playerStats != null && playerStats.MaxHealth > 0) ? CurrentHP / playerStats.MaxHealth : 0; // 비율을 계산할거에요 -> 현재 체력 / 최대 체력으로
|
|
OnHealthChanged?.Invoke(ratio); // 이벤트를 실행할거에요 -> UI 슬라이더 값을 업데이트하도록
|
|
}
|
|
|
|
private IEnumerator HitRoutine() // 코루틴 함수를 선언할거에요 -> 피격 경직 및 무적 처리 HitRoutine을
|
|
{
|
|
isHit = true; // 상태를 바꿀거에요 -> 경직 상태를 참으로
|
|
isInvincible = true; // 상태를 바꿀거에요 -> 무적 상태를 참으로
|
|
|
|
if (animator != null)
|
|
{
|
|
animator.Play(hitAnimationName, 0, 0f); // 재생할거에요 -> 설정된 피격 애니메이션을
|
|
}
|
|
|
|
yield return new WaitForSeconds(0.3f); // 기다릴거에요 -> 경직 시간(0.3초)만큼
|
|
isHit = false; // 상태를 바꿀거에요 -> 경직 해제
|
|
|
|
yield return new WaitForSeconds(invincibleDuration - 0.3f); // 기다릴거에요 -> 남은 무적 시간만큼
|
|
isInvincible = false; // 상태를 바꿀거에요 -> 무적 해제
|
|
}
|
|
|
|
public void Heal(float amount) // 함수를 선언할거에요 -> 체력을 회복하는 Heal을
|
|
{
|
|
if (IsDead) return; // 조건이 맞으면 중단할거에요 -> 죽은 상태라면
|
|
|
|
CurrentHP += amount; // 값을 더할거에요 -> 체력에 회복량을
|
|
if (playerStats != null && CurrentHP > playerStats.MaxHealth) // 조건이 맞으면 실행할거에요 -> 최대 체력을 넘었다면
|
|
CurrentHP = playerStats.MaxHealth; // 값을 제한할거에요 -> 최대 체력으로
|
|
|
|
RefreshHealthUI(); // 함수를 실행할거에요 -> UI 갱신을
|
|
Debug.Log($"💚 체력 회복: {amount}, 현재: {CurrentHP}"); // 로그를 출력할거에요 -> 회복 정보를
|
|
}
|
|
} |