게임 개발

Unity 내적으로 적의 시야각 판정하기

거리와 내적을 결합한 수평 시야 판정 코드입니다. 같은 위치, 높이 차이, 시야 경계, 벽 뒤의 대상까지 구분해 검증합니다.

CodingJoa 수정 2026.09.22 2분 정도 걸립니다
검은 배경의 3D 외적 시각화. 바닥 격자 위에 길이 4인 파란 벡터 a와 길이 3인 초록 벡터 b가 67도 각도로 벌어져 갈색 평행사변형을 이루고, 그 평면에 수직으로 주황색 a × b 화살표가 솟아 있다. 오른쪽에는 |a × b| = |a||b| sin θ = 4 × 3 × sin 67° = 11.05 계산이 적혀 있고 아래에는 '오른손을 a에서 b로 감으면 엄지가 a × b 방향'이라는 설명이 있다

적이 플레이어를 발견하는 기능을 만들 때 “앞에 있다”와 “보인다”를 한 조건으로 처리하기 쉽습니다. 하지만 방향이 맞아도 너무 멀 수 있고, 벽 뒤에 숨어 있을 수도 있습니다. 이 글에서는 거리와 각도까지만 판정하고, 장애물 검사를 어디에 붙일지 분리합니다.

Unity 6, Y축이 위인 3D 게임을 기준으로 합니다. 바닥 위를 이동하는 적이어서 시야각은 XZ 평면에서 계산하고, 허용 높이는 별도 조건으로 둡니다.

내적 값이 코사인이 되는 조건

두 벡터를 정규화하면 내적은 사이각의 코사인입니다. 전체 시야각이 90도라면 정면에서 좌우 45도까지 허용하므로 임계값은 cos(45°), 약 0.7071입니다. 정규화하지 않은 방향을 이 임계값과 비교하면 거리가 각도 판정에 섞입니다.

앞뒤만 필요하다면 정규화 없이 내적의 부호를 볼 수 있습니다. 다만 어느 한 벡터가 0이면 방향 자체가 없으므로, 내적이 0이라는 이유만으로 두 방향이 수직이라고 판단하면 안 됩니다.

수평 시야 판정 함수

PlanarVision.cs로 저장할 수 있는 정적 헬퍼입니다. 모든 위치와 방향은 월드 공간 입력입니다.

using UnityEngine;

public static class PlanarVision
{
    public static bool Contains(
        Vector3 origin, Vector3 forward, Vector3 target,
        float range, float fullAngleDegrees, float maxHeightDifference)
    {
        if (range < 0f || maxHeightDifference < 0f)
            return false;

        Vector3 offset = target - origin;
        if (Mathf.Abs(offset.y) > maxHeightDifference)
            return false;

        offset.y = 0f;
        forward.y = 0f;

        if (offset.sqrMagnitude > range * range)
            return false;
        if (offset.sqrMagnitude < 0.000001f)
            return true;
        if (forward.sqrMagnitude < 0.000001f)
            return false;

        float halfAngle = Mathf.Clamp(fullAngleDegrees, 0f, 360f) * 0.5f;
        float threshold = Mathf.Cos(halfAngle * Mathf.Deg2Rad);
        return Vector3.Dot(forward.normalized, offset.normalized) >= threshold;
    }
}

대상과 같은 위치라면 방향 판정이 불가능하므로 위 구현은 감지하는 것으로 정했습니다. 게임 규칙에 따라 false로 바꿔도 됩니다. 중요한 것은 이 결정을 정규화 함수의 우연한 결과에 맡기지 않는 것입니다.

기대값으로 오류를 좁히기

원점 (0,0,0), 정면 +Z, 거리 5, 전체 각도 90도, 허용 높이 1로 호출합니다.

목표기대값확인하는 조건
(0,0,4)true정면
(4,0,0)false옆쪽
(0,0,-1)false뒤쪽
(0,2,2)false높이 제한
(0,0,6)false거리 제한
(0,0,0)true동일 위치 정책

경계는 (3,0,3) 근처를 조금씩 움직여 확인합니다. 딱 경계에서 깜박이는 현상은 부동소수점 오차뿐 아니라 대상의 실제 이동 때문에도 생깁니다. 감지 시작 각도와 해제 각도를 조금 다르게 두는 방식으로 흔들림을 줄일 수 있습니다.

이 함수가 true여도 벽은 검사하지 않았습니다. 그다음 눈 위치에서 목표 지점까지 Raycast 또는 Linecast를 수행하고, 장애물 레이어와 자기 Collider 제외 조건을 정해야 합니다. 거리·각도·가림 중 어느 단계에서 실패했는지 로그를 나누면 “적이 멍하니 서 있다”는 증상을 훨씬 구체적으로 조사할 수 있습니다.

관련 글과 API

이 글 공유하기 X Facebook 네이버

이 글은 기술 설명과 참고 자료, 글쓴이의 판단을 담고 있습니다. 예제와 실제 사용 사례의 적용 조건은 본문을 참고해주세요. 도구의 버전·가격·동작은 시간이 지나면 달라질 수 있으니, 따라 하실 때는 공식 문서를 함께 확인해주세요. 자세한 내용은 이용약관에 정리해두었습니다.