본문 바로가기
AI 게임 제작/AI 코딩

스킬 강화 - 폭발 구현

by Yuriring 2025. 9. 28.

원시 프롬프트

이제 좀더 복잡한 스킬을 구현할거야.
* 기본 투사체가 적에게 충돌해 적을 처치하는데 성공했다면, 해당 적이 폭발해서 주변의 원형 범위로 피해를 입힌다.
* 피해는 몬스터에게만 적용되며 플레이어에게는 적용되지 않는다.
* 입히는 피해 배율은 인스펙터에서 입력할 수 있어야 한다. (투사체 공격력의 N배, 예를 들면 0.3배)
* 주변 범위 원형 지름도 인스펙터에서 입력할 수 있어야 한다.
* 이 피해로 사망한 적은 추가로 폭발하지 않는다. 코덱스 프롬프트를 짜줘

프롬프트

Unity 2D 탑뷰 로그라이크에서 “기본 투사체(Projectile)”가 적을 처치(kill)했을 때
해당 지점에서 원형 범위 피해를 주는 기능을 추가해줘.

[요구사항]
1) 트리거 조건
   - 투사체가 Enemy와 충돌해 대미지를 적용한 “그 타격”으로 적의 HP가 0 이하가 되었을 때만 폭발한다.
   - 단순히 피격만 하고 살아있으면 폭발하지 않는다.
   - 폭발 피해로 사망한 적들은 추가 폭발을 일으키지 않는다(체인 폭발 금지).

2) 피해 적용 대상
   - 오직 Enemy 레이어/태그 대상에게만 범위 피해를 준다.
   - Player, 아군, 오브젝트 등은 절대 피해받지 않는다.
   - (LayerMask로 대상 제어 가능하도록 할 것)

3) 인스펙터에서 조절 가능한 값
   - ExplosionDamageMultiplier (float): 투사체 공격력의 배수. 예) 0.3 → 공격력*0.3
   - ExplosionDiameter (float): 폭발 범위의 “지름” 값. 내부 계산 시 radius = diameter * 0.5f 로 사용.
   - EnemyLayerMask (LayerMask): 범위 판정에 포함할 레이어.
   - Optional: 폭발 VFX 프리팹(없으면 생략 가능), Gizmo 표시 on/off.

4) 대미지 계산
   - 투사체의 기본 대미지(예: PlayerStats.AttackPower 또는 Projectile.damage)를 기준으로,
     폭발 피해 = 기본 대미지 * ExplosionDamageMultiplier.
   - 범위 판정은 Physics2D.OverlapCircleAll 사용.

5) 중복/재폭발 방지
   - 폭발 피해로 사망한 적에게서는 절대로 또다시 폭발이 발생하지 않아야 한다.
   - 구현 방법 예시:
     * Projectile 충돌로 “내가 처치했는지”를 bool 반환값으로 받는다(EnemyHealth.TakeDamage(...) 가 diedByThisHit 를 반환).
     * 폭발 피해 전달 시에는 DamageSource 를 ‘Explosion’ 로 표기하고,
       EnemyHealth 가 ‘Explosion’의 킬에는 폭발 트리거 로직을 실행하지 않도록 한다.
     * 또는 Projectile 에서만 폭발을 생성하고, ExplosionDamage 스크립트는 폭발 생성 기능을 갖지 않는다.

6) 기존 로직과의 호환
   - 투사체 수명(lifetime), 속도, 벽 충돌 시 소멸 등 기존 동작은 그대로 유지.
   - 관통(PIERCE) 등 다른 강화와도 충돌 없이 동작해야 한다.
   - 투사체는 적을 처치해 폭발을 일으킨 뒤에도 기존 규칙(PIERCE 여부, lifetime 등)에 따라 유지/소멸.

[구현 지시]
A. Projectile.cs (수정)
   - [SerializeField] float explosionDamageMultiplier = 0.3f;
   - [SerializeField] float explosionDiameter = 2.0f;
   - [SerializeField] LayerMask enemyLayerMask;
   - OnTriggerEnter2D에서 Enemy 피격 처리 시:
       bool diedByThisHit = enemyHealth.TakeDamage(projectileDamage, DamageSource.Projectile);
       if (diedByThisHit) TriggerExplosionAt(hitPoint);
   - private void TriggerExplosionAt(Vector2 center):
       - float radius = explosionDiameter * 0.5f;
       - Collider2D[] hits = Physics2D.OverlapCircleAll(center, radius, enemyLayerMask);
       - foreach (var h in hits):
           * EnemyHealth eh = h.GetComponent<EnemyHealth>();
           * if (eh != null && !eh.IsDead) eh.TakeDamage(projectileDamage * explosionDamageMultiplier, DamageSource.Explosion);
   - OnDrawGizmosSelected로 폭발 반경 시각화(개발 편의).

B. EnemyHealth.cs (또는 Damageable 인터페이스 구현체, 수정)
   - public bool TakeDamage(float amount, DamageSource source)
     * HP 감소 후, HP<=0이고 아직 사망 처리 전이라면 사망 처리.
     * 사망을 “이번 히트로” 일으켰다면 true 반환, 아니면 false.
     * DamageSource.Explosion 으로 사망했을 때는 폭발 트리거를 만들지 않도록(현재 로직에선 Projectile만 폭발 생성).

   - public bool IsDead { get; private set; }

C. DamageSource 열거형 추가 (선호 시)
   - enum DamageSource { Projectile, Explosion, Other }
   - 향후 확장 대비 원인 구분용.

D. 레이어/태그 상수
   - Enemy 인식은 CompareTag("Enemy") 또는 enemyLayerMask 로 통일.
   - Player/벽 등은 충돌/피해 제외.

E. 옵션: 폭발 이펙트
   - [SerializeField] GameObject explosionVfxPrefab;
   - TriggerExplosionAt에서 Instantiate 후 일정 시간 뒤 Destroy.

[테스트 시나리오]
1) 일반: 투사체로 적 HP 10 남기면 폭발 없음. 0 이하로 죽이면 즉시 폭발, 주변 적들에게 배율 피해.
2) 플레이어/오브젝트는 범위 피해를 받지 않음.
3) 폭발 피해로 죽은 적은 추가 폭발 없음(체인 X).
4) 인스펙터에서 배율/지름 값을 바꿔 범위와 피해량이 즉시 반영되는지 확인.

위 내용을 반영해 필요한 스크립트(Projectile, EnemyHealth 등)를 생성/수정하고,
모든 공개 필드를 인스펙터에서 조절 가능하게 만들어줘. 주석은 한국어로 상세히 달아줘.

 

연쇄적으로 폭발하는 스킬의 아이콘을 그려줘. 만화에서 흔히 폭발을 묘사할때 그리는 뾰족뾰족한 도형이 두세개 연달아 그려져 있으면 좋을 것 같아.

Unity 2D 탑뷰 로그라이크 프로젝트에서 ‘투사체 처치 시 폭발(EXPLOSION)’ 로직을 다음처럼 수정/확장해줘.

[문제/수정]
- 현재 폭발이 엉뚱한 위치에서 재생된다. 폭발은 **투사체에 맞아 죽은 적의 중심 위치**에서만 발생해야 한다.
- 위치 계산은 Projectile 위치가 아니라, **사망한 Enemy의 Collider2D.bounds.center (없으면 transform.position)** 를 사용해라.
- Enemy가 Destroy되기 전에 반드시 해당 위치를 **캐싱**한 뒤, 그 좌표로 범위 피해와 VFX를 재생해라.

[확률 적용(신규)]
- CardItem 테이블에서 플레이어가 카드를 선택할 때,
  - Effect == "EXPLOSION" 이면 VALUE(정수, 백분율: 예 20=20%)를 읽어 **폭발 확률**로 사용한다.
  - 이 값은 0~100 범위, 내부 연산은 0~1 float 로 변환한다. (예: 20 → 0.2f)
- 확률 판정은 **투사체로 죽인 그 순간**에만 수행한다.
  - `if (Random.value < explosionChance01) { 폭발 } else { 비폭발 }`
- EXPLOSION 카드가 없으면 기본 확률은 0으로 간주한다.

[기존 스펙 유지]
- 폭발 피해는 **Enemy 레이어만** 맞는다. Player/오브젝트는 제외.
- 폭발 피해로 죽은 적은 **추가 폭발을 일으키지 않는다**(체인 금지).
- 폭발 피해량 = (투사체 기본 대미지) * ExplosionDamageMultiplier (인스펙터 설정값).
- 폭발 지름(=2*반경)도 인스펙터에서 설정.

[구현 지시]
A. PlayerPassive(or SkillManager) 에 확률 저장
- public float ExplosionChance01 { get; private set; } // 0~1
- public void SetExplosionChancePercent(int percent)
  {
      percent = Mathf.Clamp(percent, 0, 100);
      ExplosionChance01 = percent / 100f;
  }

B. 카드 적용부(CardEffectApplier 등)
- CardItem.Effect == "EXPLOSION" 일 때:
  - int p = CardItem.Value(없으면 0);
  - playerPassive.SetExplosionChancePercent(p);

C. Projectile.cs 수정 (핵심)
- [SerializeField] float explosionDamageMultiplier = 0.3f;
- [SerializeField] float explosionDiameter = 2.0f;
- [SerializeField] LayerMask enemyLayerMask;
- PlayerPassive 참조를 받아 `explosionChance01 = playerPassive.ExplosionChance01` 로 셋업.

- OnTriggerEnter2D(Collider2D other) 에서 Enemy 피격 시:
  1) EnemyHealth eh = other.GetComponent<EnemyHealth>();
  2) bool diedByThisHit = eh.TakeDamage(projectileDamage, DamageSource.Projectile);
  3) if (diedByThisHit)
     {
         // ★ 폭발 위치를 Enemy 기준으로 캐싱
         Vector2 center = other.bounds.center; // Collider2D가 있으면 bounds.center
         // (Collider2D가 없으면 eh.transform.position 사용)
         if (explosionChance01 > 0f && Random.value < explosionChance01)
         {
             TriggerExplosionAt(center);  // 여기서만 폭발
         }
         // 이후 투사체의 소멸/관통 로직은 기존 규칙대로 유지(PIERCE 등)
     }

- private void TriggerExplosionAt(Vector2 center)
  {
      float radius = explosionDiameter * 0.5f;
      // 범위 피해
      var hits = Physics2D.OverlapCircleAll(center, radius, enemyLayerMask);
      float dmg = projectileDamage * explosionDamageMultiplier;
      foreach (var h in hits)
      {
          var eh = h.GetComponent<EnemyHealth>();
          if (eh != null && !eh.IsDead)
          {
              eh.TakeDamage(dmg, DamageSource.Explosion); // 체인 금지용 source
          }
      }

      // VFX (선택)
      if (explosionVfxPrefab != null)
      {
          var vfx = Instantiate(explosionVfxPrefab, center, Quaternion.identity);
          Destroy(vfx, 0.5f);
      }
  }

- 개발 편의: OnDrawGizmosSelected 로 radius 시각화.

D. EnemyHealth.cs (혹은 Damageable)
- public bool TakeDamage(float amount, DamageSource source)
  - HP 감소 → 0 이하가 되고 아직 미사망이면 사망 처리 후 true 반환.
  - IsDead 플래그 보유.
  - DamageSource.Explosion 으로 죽은 경우에는 **추가 폭발 트리거 금지** (Projectile만 폭발 생성).

E. 상수/태그
- Enemy 판정은 enemyLayerMask 또는 "Enemy" 태그. Player는 절대 포함하지 말 것.

[테스트 체크리스트]
- EXPLOSION 카드 미보유: 절대 폭발 안 함.
- EXPLOSION 20%: 10마리 연속 처치 시 대략 2마리 내외에서만 폭발(통계적 오차 허용).
- 폭발/VFX 좌표가 **사망한 Enemy의 중심**에서 정확히 발생.
- 폭발로 죽은 적은 재폭발 없음.
- 관통(PIERCE)와 함께 사용해도 충돌 없음.

잘 된다! 유니티로 이펙트도 입혔음