unity6-5AI 작성

이지성·2024년 12월 2일

AI를 어떻게 작성해야 하는가?

일단 팀원들과 AI에 대해 토론해 미뤄놨던 AI구성에 대해 열띤 토론을 하기 시작했다.
일단 나는 어떻게 해야할지 정하기 위해 대표적으로 성공적인 에일리언:아이솔레이션의 에일리언 AI를 분석했다.

분석

일단 음... 공포게임 관련 저널이 거의 없기 때문에(그나마 구한 자료는 돈주고 사야 하는데, 한국에서는 구매가 불가능한 자료였다.)
커뮤니티에 올라온 글로 단순하게 만들어 봐야한다.

자, 그럼 내용을 분석해 보자.

AI 시스템은 크게 두 가지 레벨의 AI로 나뉩니다: 거시적(Macroscopic) AI와 미시적(Microscopic) AI입니다. 각각의 역할과 구현 방법을 알아보겠습니다.

  1. 거시적 AI (Macroscopic AI)
    거시적 AI는 플레이어와 직접 상호작용하지 않고, 게임의 긴장감과 난이도를 조율하는 "감독" 역할을 합니다. 이 시스템은 다음과 같은 일을 합니다:

역할
플레이어 상태 분석:

플레이어의 심리적 상태(긴장, 여유, 공포)를 게임 플레이 데이터를 통해 추정.
플레이어가 너무 긴장했으면 AI를 멀리 보내 휴식을 주고, 너무 여유로우면 긴장을 조성.
에일리언 위치 제어:

플레이어 근처에 항상 에일리언을 배치하지만, 직접적인 위협을 느끼지 않도록 조정.
에일리언이 적당히 플레이어 근처에 있으면서도 즉각적으로 위협을 가하지 않게 설정.
구현 방법
플레이어 상태 추적:

인벤토리 사용량: 아이템을 많이 사용하는가, 적게 사용하는가.
이동 패턴: 천천히 숨어서 움직이는가, 달리고 있는가.
최근 만남 기록: 마지막으로 에일리언을 본 후 얼마나 시간이 흘렀는가.
전투 및 회피 행동: 플레이어가 자주 싸우는지, 도망가는지.
동적 긴장 조절:

플레이어가 자신감이 높을 때:
에일리언을 가까운 섹터로 이동시키거나, 갑작스럽게 나타나게 설정.
플레이어가 긴장 상태일 때:
에일리언을 멀리 보내고, 대신 발소리나 환경 소음으로 긴장을 유지.
섹터 관리:

게임 맵을 "섹터"로 나누고, 거시적 AI가 에일리언을 적절한 섹터로 이동.
섹터는 플레이어의 위치와 상태에 따라 동적으로 결정.
2. 미시적 AI (Microscopic AI)
미시적 AI는 에일리언의 행동과 센서를 기반으로, 플레이어와 직접 상호작용합니다. 이 AI는 에일리언의 지능적인 행동과 추적 능력을 담당합니다.

역할
센서를 통해 데이터 수집:

시야(Vision Cone):
에일리언의 전방에 넓은 시야와 후방에 좁은 시야를 설정해 플레이어를 발견.
소리(Sound Detection):
소리가 나는 방향과 강도에 따라 반응.
냄새(Scent, 선택적):
플레이어의 흔적을 감지하고 시간이 지나면 사라지도록 설정.
행동 트리(Behavior Tree):

에일리언이 다양한 자극(시야, 소리, 냄새)에 반응하여 행동을 결정.
무작위성을 추가해 예측할 수 없는 행동을 생성.
적응형 행동:

플레이어가 반복적으로 사용하는 전략(예: 소음 발생기 남용)을 학습하고 이에 대한 대처 행동 추가.
플레이어가 자주 숨는 장소(락커, 책상 아래)를 점검.

이렇게 된다.

간단히 코드로 작성해 보면...

using UnityEngine;

public class AlienAI : MonoBehaviour
{
    public Transform player;
    public LayerMask obstacles;

    private Transform currentSector;
    private float tensionLevel = 0; // 플레이어 상태 추적 (0 = 여유, 1 = 긴장)

    // 센서 및 행동 변수
    private float visionRange = 15f;
    private float soundDetectionRadius = 10f;
    private float randomCheckProbability = 0.1f;

    void Update()
    {
        AdjustAlienProximity();
        HandleAlienBehavior();
    }

    void AdjustAlienProximity()
    {
        if (tensionLevel < 0.3f)
        {
            MoveAlienToSector(FarSector());
        }
        else if (tensionLevel > 0.7f)
        {
            MoveAlienToSector(NearbySector());
        }
    }

    void HandleAlienBehavior()
    {
        if (CanSeePlayer())
        {
            ChasePlayer();
        }
        else if (HeardSound())
        {
            InvestigateSound();
        }
        else
        {
            Patrol();
        }

        if (Random.value < randomCheckProbability)
        {
            RandomCheck();
        }
    }

    void MoveAlienToSector(Transform sector)
    {
        currentSector = sector;
        transform.position = sector.position;
    }

    Transform FarSector()
    {
        // 멀리 떨어진 섹터 계산
        return null;
    }

    Transform NearbySector()
    {
        // 가까운 섹터 계산
        return null;
    }

    bool CanSeePlayer()
    {
        Vector3 directionToPlayer = (player.position - transform.position).normalized;
        float angle = Vector3.Angle(transform.forward, directionToPlayer);

        if (angle < 60f && Vector3.Distance(transform.position, player.position) < visionRange)
        {
            if (!Physics.Linecast(transform.position, player.position, obstacles))
            {
                return true;
            }
        }
        return false;
    }

    bool HeardSound()
    {
        return Vector3.Distance(transform.position, player.position) < soundDetectionRadius;
    }

    void ChasePlayer() { Debug.Log("추격 중"); }
    void InvestigateSound() { Debug.Log("소리 조사 중"); }
    void Patrol() { Debug.Log("순찰 중"); }
    void RandomCheck() { Debug.Log("무작위 점검 중"); }
}

이렇게 된다.
이걸 다이어 그램화 하면

이런 모양이 나온다.

이걸 토대로 우리 게임의 AI를 적용시켜 봐야겠다.

여기서 대부분을 쳐내고 간단한 다이어 그램으로 바꿧다(이건 BT는 아니다.)

결론(BT 아님!)


우리는 이렇게 AI를 작성하기로 합의 했다.

profile
이지성입니다!

0개의 댓글