반응형

발전기를 방어하는 게임에서는 좀비가 플레이어만 쫓아다니면 방어 목표의 의미가 약해집니다. 반대로 좀비가 발전기만 공격하면 플레이어와의 전투가 단순해지므로, OUTPOST에서는 두 목표를 상황에 따라 선택하게 했습니다.

1. 목표를 Transform과 피해 대상으로 나누기

좀비는 단순히 목표 위치로 이동하기만 하는 것이 아니라 그 대상에게 공격도 해야 합니다. 그래서 ZombieAI에는 목표 위치를 나타내는 _activeTarget과 IDamageable을 나타내는 _activeDamageable을 함께 보관합니다.

목표는 TargetMode.Player와 TargetMode.Generator로 구분하고, 웨이브 데이터에서 기본 목표를 지정할 수 있습니다.

public enum TargetMode { Player, Generator }

public void ConfigureTarget(TargetMode mode)
{
    _targetMode = mode;
    _prefersObjective = mode == TargetMode.Generator;
    ResolveTargets();
}

2. 플레이어가 근처에 있으면 위협으로 판단하기

발전기를 우선하는 좀비라도 플레이어가 바로 앞에 있다면 공격 대상을 바꾸도록 했습니다. 핵심은 SelectActiveTarget()의 다음 조건입니다.

float playerDistance = Vector3.Distance(
    transform.position, _playerTarget.transform.position);
bool playerIsImmediateThreat = playerDistance <= 7f ||
    _targetMode == TargetMode.Player || !_prefersObjective;

_activeTarget = playerIsImmediateThreat
    ? _playerTarget.transform
    : _objectiveTarget.transform;
_activeDamageable = playerIsImmediateThreat
    ? (IDamageable)_playerTarget : _objectiveTarget;

기본 목표가 Generator라도 플레이어가 7m 이내라면 플레이어를 선택합니다. 즉 목표가 하나로 고정된 것이 아니라 현재 거리와 웨이브 성격에 따라 달라집니다. 플레이어가 죽었거나 발전기가 파괴된 경우에도 사용 가능한 다른 목표를 확인합니다.

3. 발전기 공격 범위는 중심 좌표가 기준이 아닙니다

발전기처럼 크기가 있는 오브젝트를 공격할 때는 중심점까지 거리를 재면 실제 표면 바로 앞에 있어도 공격하지 못하는 문제가 생길 수 있습니다.

그래서 발전기를 대상으로 할 때는 콜라이더에서 가장 가까운 지점을 사용합니다.

float attackDistance = targetingObjective && _objectiveCollider != null
    ? Vector3.Distance(transform.position,
        _objectiveCollider.ClosestPoint(transform.position))
    : distance;

float effectiveAttackRange = targetingObjective
    ? attackRange + objectiveAttackRangePadding
    : attackRange;

ClosestPoint()는 콜라이더 표면을 기준으로 가까운 지점을 찾습니다. 따라서 발전기의 피벗 위치나 실제 크기가 달라도 공격 거리를 더 자연스럽게 검사할 수 있습니다.

4. 이동 중에는 목적지를 주기적으로 갱신

목표가 움직일 수 있기 때문에 좀비는 일정 시간 간격으로 NavMeshAgent.SetDestination()을 다시 호출합니다. 근접 거리에서는 멈춰서 대상을 바라본 후 IDamageable.TakeDamage()로 피해를 적용합니다. 피격 경직 중에는 움직임을 일시 정지하도록 분리했습니다.


정리

웨이브의 기본 목표 → 현재 살아 있는 대상 확인 → 플레이어 근접 여부 확인 → 활성 목표 선택 → 이동 또는 공격의 흐름입니다. 플레이어와 발전기 모두 IDamageable을 사용해 피해 처리 인터페이스는 통일하면서, 표적 선택 기준은 따로 두었습니다.


참고한 구현 코드

반응형