본문 바로가기
Unity 궁둥이 쪼물쪼물

009. 적 체력(HP) 시스템

by daco2020 2026. 4. 13.

한 발에 죽던 적에게 체력을 부여해서 n발 맞아야 죽도록 변경하고,
죽을 때 이펙트와 함께 사라지는 연출을 추가한 과정을 기록한다.

 

 

 

1. 변경 전 vs 변경 후

변경 전

총알 맞음 → OnHit() → 무조건 Die() = 그냥 멈춰있음

변경 후

총알 맞음 → OnHit() → HP 감소
  → HP > 0: 넉백 + 0.5초 후 행동 재개
  → HP <= 0: Die() → 0.5초 후 이펙트 + 사라짐

 

 

 

2. HP 시스템 구현

추가된 변수

[SerializeField]
[Tooltip("최대 체력")]
private int maxHp = 3;

[SerializeField]
[Tooltip("넉백 후 다시 행동을 재개하기까지의 시간 (초)")]
private float stunDuration = 0.5f;

private int _hp;
  • maxHp: Inspector에서 조절 가능 (기본 3 = 3발 맞으면 사망)
  • stunDuration: 넉백 후 멍한 시간
  • _hp: 현재 남은 체력

 

Start()에서 초기화

_hp = maxHp;

 

 

 

3. OnHit() 확장

public void OnHit(Vector3 knockbackForce, int damage = 1)
{
    if (state == State.Die) return;

    _hp -= damage;

    // 넉백 적용 (NavMeshAgent 끄기 → Rigidbody 활성화 → 힘 가하기)
    // ... (이전과 동일)

    if (_hp <= 0)
    {
        nextState = State.Die;      // 죽음
    }
    else
    {
        StartCoroutine(RecoverFromHit());  // 회복
    }
}

 

핵심: damage = 1 (기본값 파라미터)

enemy.OnHit(힘);        // damage = 1 (기본값)
enemy.OnHit(힘, 5);     // damage = 5 (강한 무기)

 

Bullet.cs를 수정하지 않아도 기본 1 데미지가 적용된다.
나중에 강한 무기를 만들면 damage 값만 바꾸면 된다.

 

 

 

4. 코루틴으로 넉백 회복

private IEnumerator RecoverFromHit()
{
    yield return new WaitForSeconds(stunDuration);  // 0.5초 대기

    // Rigidbody 초기화 (물리 비활성화)
    Rigidbody rb = GetComponent<Rigidbody>();
    if (rb != null)
    {
        rb.linearVelocity = Vector3.zero;
        rb.isKinematic = true;
        rb.constraints = RigidbodyConstraints.None;
    }

    // NavMeshAgent 다시 켜기 → 행동 재개
    _agent.enabled = true;
    nextState = State.Idle;
}

 

코루틴(Coroutine)이란?

일반 함수는 한 프레임 안에서 전부 실행된다. 코루틴은 중간에 멈추고 나중에 이어할 수 있는 특수한 함수다.

일반 함수:     실행 → → → → → 끝 (한 프레임에 전부)
코루틴:        실행 → → 멈춤(0.5초 대기) → → → 끝 (여러 프레임에 걸쳐)

 

yield return new WaitForSeconds(0.5f) = "0.5초 기다려, 그 다음 줄부터 다시 실행해"

 

회복 흐름

맞음 → 넉백으로 밀림 → 0.5초 멍 → Rigidbody 정지 → NavMeshAgent 재시작 → Idle 상태
  → 다시 순찰/추적 시작!

 

 

 

5. 죽음 연출: 이펙트 + 사라짐

private IEnumerator DieAndDisappear()
{
    yield return new WaitForSeconds(0.5f);  // 죽는 애니메이션 잠깐 보여주기

    if (splashFx != null)
        Instantiate(splashFx, transform.position, Quaternion.identity);

    gameObject.SetActive(false);  // 사라짐
}

 

흐름은 다음과 같다.

Die 상태 → stun 애니메이션 재생 → 0.5초 대기 → 이펙트(OrangeHit) 터짐 → 오브젝트 비활성화

 

 

 

6. 넉백 방향 수정

문제: 적이 앞으로 당겨짐

처음엔 enemy.position - bullet.position으로 방향을 계산했는데, 충돌 시점에 총알이 이미 적을 통과한 뒤라서 방향이 반대가 될 수 있었다.

해결: 총알의 실제 이동 방향(velocity) 사용

// 변경 전: 위치 기반 (부정확)
Vector3 dir = enemy.transform.position - transform.position;

// 변경 후: 속도 기반 (정확)
Vector3 dir = bulletRb.linearVelocity.normalized;

 

총알이 날아가던 방향 그대로 적을 미니까 항상 정확하다.

 

 

 

7. 이번 작업에서 배운 것 요약

  1. HP 시스템 = _hp 변수 + OnHit()에서 감소 + 0 체크
  2. 기본값 파라미터 (damage = 1) = 호출하는 쪽을 수정 안 해도 됨
  3. 코루틴 = yield return으로 시간을 두고 실행하는 함수
  4. 넉백 회복 = Rigidbody 정지 → isKinematic 복원 → Agent 재시작
  5. 죽음 연출 = 코루틴으로 딜레이 후 이펙트 + SetActive(false)
  6. 넉백 방향은 위치 뺄셈보다 velocity가 정확함