[2025_01_23] 반사 스킬 무한 루프 방지 & 관통 투사체 최적화
2026. 1. 23. 21:22ㆍTIL
문제 1: 반사 데미지 무한 루프
상황
성기사 A가 반사 실드를 가진 상태에서 성기사 B에게 공격당하면?
B가 A 공격 → A가 데미지 반사 → B가 데미지 반사 → A가 데미지 반사 → ...
무한 루프 발생 → 스택 오버플로우
처음 시도한 방법들
1. bool 플래그 추가
csharp
public void TakeDamage(int damage, UnitBase attacker, bool canReflect = true)
{
if (canReflect && reflectShield != null)
{
attacker.TakeDamage(reflectedDamage, this, false); // 반사 비활성화
}
}
- 문제: 기존 TakeDamage 호출부를 전부 수정해야 함
- 다른 시스템(DOT, 환경 데미지 등)과의 호환성 문제
2. 별도 ReflectController 클래스
csharp
public class ReflectController
{
private HashSet<(UnitBase, UnitBase)> reflectingPairs;
// 현재 반사 중인 쌍을 추적...
}
- 문제: 과도한 복잡성, 전역 상태 관리 필요
최종 해결: attacker를 null로 전달
csharp
public void TakeDamage(int damage, UnitBase attacker)
{
// 실제 데미지 처리
currentHp -= damage;
// 반사 처리: attacker가 null이면 반사하지 않음
if (attacker != null && reflectShield != null && reflectShield.charges > 0)
{
int reflectedDamage = Mathf.RoundToInt(damage * reflectShield.reflectRatio);
// 핵심: attacker를 null로 전달하여 재반사 방지
attacker.TakeDamage(reflectedDamage, null);
reflectShield.charges--;
}
}
왜 이게 좋은가?
기준결과
| 기존 코드 수정 | 없음 (null은 이미 유효한 값) |
| 추가 파라미터 | 없음 |
| 의미적 명확성 | "공격자 없음 = 반사 불가" |
| 확장성 | DOT, 환경 데미지도 자연스럽게 처리 |
교훈: 복잡한 문제에 복잡한 해결책이 필요한 건 아니다. 복작하게 해결할려고 하다가 오히려 오래 걸렷다...
문제 2: 관통 투사체 중복 타격
상황
스나이퍼의 관통 화살이 직선으로 날아가며 모든 적을 타격해야 하는데:
- 같은 적을 프레임마다 중복 타격
- 많은 적과 충돌 체크 시 성능 저하
해결: HashSet + Interval 기반 체크
csharp
public class PiercingProjectile : MonoBehaviour
{
private HashSet<UnitBase> hitTargets = new HashSet<UnitBase>();
[SerializeField] private float checkInterval = 0.05f;
private float lastCheckTime;
private void Update()
{
// 매 프레임이 아닌 일정 간격으로 체크
if (Time.time - lastCheckTime < checkInterval) return;
lastCheckTime = Time.time;
CheckCollisions();
}
private void CheckCollisions()
{
Collider2D[] hits = Physics2D.OverlapCircleAll(
transform.position,
hitRadius,
targetLayer
);
foreach (var hit in hits)
{
if (hit.TryGetComponent<UnitBase>(out var unit))
{
// HashSet으로 O(1) 중복 체크
if (hitTargets.Add(unit))
{
unit.TakeDamage(damage, owner);
Debug.Log($"[Piercing] Hit: {unit.name}, " +
$"Total hits: {hitTargets.Count}");
}
}
}
}
}
성능 비교
방식중복 체크시간 복잡도프레임당 체크
| List + Contains | 순회 검색 | O(n) | 매 프레임 |
| HashSet + Interval | 해시 조회 | O(1) | 0.05초 간격 |
50개 적 기준, HashSet + Interval이 약 5배 효율적
디버그 로깅
관통 투사체처럼 검증이 어려운 시스템은 로깅이 필수:
csharp
Debug.Log($"[Piercing] Spawned at {transform.position}, " +
$"Direction: {direction}, Owner: {owner.name}");
Debug.Log($"[Piercing] Hit: {unit.name}, " +
$"Position: {unit.transform.position}, " +
$"Total hits: {hitTargets.Count}");
Debug.Log($"[Piercing] Destroyed - Final hit count: {hitTargets.Count}");
정리
반사 스킬
- 문제: 양측 반사 시 무한 루프
- 해결: 반사 데미지의 attacker를 null로 전달
- 원칙: 기존 시스템을 수정하지 않는 가장 단순한 방법 선택
관통 투사체
- 문제: 중복 타격 + 성능
- 해결: HashSet(O(1) 조회) + Interval 체크(프레임 부하 분산)
- 원칙: 자료구조 선택이 성능을 결정한다
공통 교훈
"가장 단순한 해결책이 종종 최선이다"
복잡한 컨트롤러나 플래그 시스템 대신, 기존 구조 안에서 자연스럽게 동작하는 방법을 먼저 찾자.
'TIL' 카테고리의 다른 글
| [2026_01_22] 임원 모의 면접 (0) | 2026.01.22 |
|---|---|
| [2026_01_21] 지휘관 스킬 전역 이벤트 시스템 (0) | 2026.01.21 |
| [2026_01_09] 사운드 고르기... (0) | 2026.01.09 |
| [2026_01_08] 버프/디버프 적용 시 실제 적용값(appliedValue) 저장 (0) | 2026.01.08 |
| [2026_01_07] Unity UI Mask를 활용한 역마스크(Cutout Mask) (0) | 2026.01.07 |