Unity) 입수 데미지 추가
캐릭터가 물속에 입수하면 일정 시간 후 데미지를 적용하도록 만들어보려 한다.
물에 BoxCollider
적용
우선 물에 BoxCollider를 적용한다.
물에 1만큼 입수하면 Collider와 접촉 할 수 있도록 적용했다.
이후 캐릭터와 Collider가 만나면 트리거를 작동하는 방식으로 구현할 예정이다.
기존 CampFire
코드 활용
기존에 작성한 CampFire
스크립트가 있다.
public class CampFire : MonoBehaviour
{
public int damage;
public float damageRate;
List<IDamagable> things = new List<IDamagable>();
void Start()
{
InvokeRepeating("DealDamage", 0, damageRate);
}
void DealDamage()
{
for (int i = 0; i < things.Count; i++)
{
things[i].TakePhysicalDamage(damage);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent(out IDamagable damagable))
{
things.Add(damagable);
}
}
private void OnTriggerExit(Collider other)
{
if (other.TryGetComponent(out IDamagable damagable))
{
things.Remove(damagable);
}
}
}
이 코드는 트리거를 통해 즉시 데미지를 적용하는 방식으로 설계되었다.
하지만 물에서는 즉시 데미지를 적용하는 것이 아니라 일정 시간 후에 데미지를 적용해야 한다.
따라서 코드를 변형할 필요가 있다.
코드 변형을 위한 변경 사항 정리
- 딜레이 타임 추가: 즉시 데미지를 입히는 것이 아니라 일정 시간이 지난 후에 데미지를 적용해야 한다.
- 코루틴 적용:
WaitForSeconds
를 이용해 일정 시간이 지나야만 데미지를 받도록 변경한다. -
범위 이탈 처리: 대기 시간 중 캐릭터가 물을 벗어나면 데미지를 받지 않도록 설정한다.
- 데이터 구조 변경: 중복 처리를 방지하기 위해
List
대신HashSet
을 사용한다.
변경된 CampFire
코드
public class CampFire : MonoBehaviour
{
public int damage;
public float damageRate;
public float delayTime = 0;
bool enter = false;
HashSet<IDamagable> things = new HashSet<IDamagable>(); // 중복 방지
void Start()
{
InvokeRepeating("DealDamage", 0, damageRate);
}
void DealDamage()
{
foreach (var thing in things)
{
thing.TakePhysicalDamage(damage);
}
}
IEnumerator AddDelay(IDamagable damagable)
{
yield return new WaitForSeconds(delayTime);
if (!things.Contains(damagable) && enter)
{
things.Add(damagable);
}
}
private void OnTriggerEnter(Collider other)
{
enter = true;
if (other.TryGetComponent(out IDamagable damagable))
{
StartCoroutine(AddDelay(damagable));
}
}
private void OnTriggerExit(Collider other)
{
enter = false;
if (other.TryGetComponent(out IDamagable damagable))
{
things.Remove(damagable);
}
}
}
코드 변경 이유
기존의 CampFire
코드는 트리거에 진입하면 바로 데미지를 입도록 되어 있었다. 하지만 물에서는 일정 시간 후 데미지가 들어가야 하므로 AddDelay
코루틴을 추가했다.
delayTime
변수를 추가하여 지연 시간을 조절할 수 있도록 했다.OnTriggerEnter
에서 바로 데미지를 적용하는 대신,StartCoroutine(AddDelay(damagable))
을 호출하여 일정 시간이 지나야 데미지를 입도록 변경했다.- 데이터 구조 변경:
List
대신HashSet
을 사용하여 중복 처리를 방지했다. OnTriggerExit
에서things.Remove(damagable)
을 호출하여, 캐릭터가 물을 벗어나면 데미지가 중단되도록 했다.- 코루틴 실행 중 범위를 벗어난 경우를 고려하여
bool enter
추가:AddDelay
가 실행되는 동안 캐릭터가 물을 벗어날 경우에도 데미지가 추가되지 않도록 수정했다.
트러블 슈팅
개발 과정에서 몇 가지 문제를 해결해야 했다.
코루틴을 중지하는 기능이 없었음
- 원래는
OnTriggerEnter
에서StartCoroutine
으로 실행했지만,OnTriggerExit
에서 해당 코루틴을 멈추는 코드가 없었다. - 해결 방법:
Dictionary<IDamagable, Coroutine>
을 사용해 실행 중인 코루틴을 추적하는 방식으로 수정하려 했지만, 최종적으로는HashSet
을 사용하여AddDelay
내에서 중복을 방지하는 형태로 간결하게 해결했다.
코루틴이 실행된 후 범위를 벗어나도 데미지가 적용되는 문제
IEnumerator AddDelay(IDamagable damagable)
{
yield return new WaitForSeconds(delayTime);
if (!things.Contains(damagable))
{
things.Add(damagable);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent(out IDamagable damagable))
{
StartCoroutine(AddDelay(damagable));
}
}
private void OnTriggerExit(Collider other)
{
if (other.TryGetComponent(out IDamagable damagable))
{
things.Remove(damagable);
}
}
- 바로 위 기존 코드에서는
AddDelay
가 실행된 후things.Contains(damagable)
만 확인하여 데미지를 추가했다. - 하지만 캐릭터가 물에 있다가 대기 시간 중 나가버리면, 여전히 데미지가 추가되는 문제가 발생했다.
- 해결 방법:
bool enter
변수를 도입하여OnTriggerEnter
에서true
,OnTriggerExit
에서false
로 설정했다. AddDelay
에서enter
값을 확인하여, 캐릭터가 여전히 물에 있는 경우에만 데미지를 적용하도록 수정했다.
중복 등록 문제
List
를 사용하면 동일한IDamagable
객체가 여러 번 추가될 수 있었다.- 해결 방법:
HashSet<IDamagable>
을 사용해 중복을 방지했다.
결론
이렇게 코드 변형을 통해 CampFire
가 불이 아닌 물에서도 사용될 수 있도록 했다.
- 기존
CampFire
에서는delayTime = 0
으로 설정하여 기존 기능을 유지할 수 있다. - 물에서는
delayTime
을 조정하여 일정 시간 후에 데미지를 적용하도록 설정했다. HashSet
을 사용하여 중복 처리를 방지하여 성능을 최적화했다.- 범위를 벗어나면 데미지가 적용되지 않도록
enter
변수로 추가 확인하여 버그를 수정했다.
이를 통해 같은 코드 구조를 유지하면서도 상황에 맞는 유연한 데미지 적용이 가능하도록 만들었다.
댓글남기기