언리얼 엔진 본캠프 10주차-3 언리얼 엔진 C++ : AI

정재훈·2025년 2월 19일
0

unreal engine

목록 보기
29/45

AI 이동의 기초

  • 경로 탐색(Pathfinding)
    • 목표 지점까지 이동 경로를 계산하는 알고리즘 혹은 기법
    • 주로 AI가 장애물을 피해 효율적인 경로를 찾을 때 사용
  • NavMesh
    • 게임 월드에서 이동할 수 있는 영역을 메쉬 형태로 표현한 것
    • AI가 이 NavMesh 위에서 경로 탐색을 수행
  • AIController
    • 게임 속 AI를 제어하는 주체로, Pawn이나 Character과 상호 작용하여 행동을 결정
    • AI의 의사결정 로직을 담고, Behavior Tree나 Perception등을 관리

내비게이션 시스템 : 맵 위의 지형 정보를 스캔하여 이동 가능한 구역과 장애물 계산을 자동으로 하고, 이 정보를 가지고 최적의 이동 경로를 탐색하는 시스템


1. 순찰

  • 먼저 AI 캐릭터는 NavMesh 위에서만 이동할 수 있기 때문에 NavMesh를 먼저 깔아주고 약간의 장애물을 추가
    • NavMesh를 깔고 'p'를 누르면 사진처럼 이동할 수 있는 공간을 초록색으로 표시해준다
  • AI 캐릭터가 이동할 목적지인 Target Point도 추가
    • 빨간 원 안에 있는 것
  • 그 다음 AI 캐릭터Character 클래스를, AIControllerAIController 클래스를 상속받아서 생성하면 된다
    • AI 캐릭터는 자신이 이동할 Target Point를 가지고 있어야 해당 지점들을 순찰할 수 있음
      • TArray<AActor*> PetrolPoints;
      • UPROPERTY()를 했기 때문에 AI 캐릭터마다 에디터에서 수정하면 됨
    • AIContollerClass = AEnemyAIController::StaticClass();
      • 사용할 AIController를 설정 => 다른 동작을 하는 여러 AIController를 만들면 게임 상황에 맞게 AI 캐릭터의 동작을 조절할 수 있을 듯
    • AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
      • 언제 AIController가 빙의될 지 설정
  • AIController는 이 AI 캐릭터에 빙의를 하고, 움직이도록 한다
    • OnPossess(APawn* InPawn)은 AIController가 AI 캐릭터에 빙의되는 순간 호출되는 함수
      • Behavior Tree/Blackboard 초기화나 빙의되는 AI 캐릭터의 초기값을 설정할 수도 있을 듯
    • AI Controller는 AI 캐릭터를 Target Point까지 이동시켜야 하는데, 이때 언리얼 엔진에 이미 구현되어 있는 MoveToActor() 함수를 사용
    • 그리고 MoveToActor()가 수행되었을 때 호출되는 OnMoveCompleted() 함수를 통해 계속해서 다음 Target Point로 이동할 수 있도록 함

MoveToActor()

  • 함수 시그니처
    • AcceptanceRadius : AI 캐릭터와 목표 지점사이의 오차 범위 정도로 생각하면 된다
      • 이 값이 0.0f이면 목표 지점과 AI 캐릭터가 완전히 겹쳐야 하고 default인 5.0f이면 5cm 차이가 나도 도착했다고 간주
    • FilterClass : 특정 네비게이션 필터 적용 (예: 특정 지형 피하기)
      • 특정 지역을 지나갈 수 있는지 없는지를 추가로 지정

Behavior Tree를 활용한 AI 기초

  • Behavior Tree
    • AI 의사 결정을 트리 형태로 구성하여 단계적으로 처리하는 구조
    • 노드들의 조건과 태스크를 순서대로 평가하여 행동을 결정
  • Blackboard
    • AI 뇌 속에 있는 메모장
    • Behavior Tree가 사용할 변수들을 저장해놓는 곳
  • AI Perception
    • AI가 주변 환경을 인지하는 시스템
    • 시야, 청각 등 다양한 센서를 통해 목표나 이벤트를 감지

2. 순찰/추격

  • c++ 코드 대신 Behavior Tree를 사용
  • Target Point를 관리하기 위한 Actor 클래스를 생성
    • 이에 따라 AI 캐릭터와 AIController에서 Target Point와 이동과 관련된 부분은 주석처리
  • 이렇게 만든 PatrolPath 클래스를 상속받는 BP 클래스를 만들고 에디터에서 Target Point를 BP_PatrolPath의 Waypoints에 할당
  • 에디터에서 Behavior Tree와 Blackboard를 생성하고 Behavior Tree의 Details에서 Blackboard를 연결
  • Blackboard에는 Behavior Tree에서 쓸 변수를 저장하니, 순찰에 필요한 (PatrolPath, CurrentWaypointIndex), 추적에 필요한(PlayerDetected, TargetActor) 변수들을 추가
SelfActor : Behavior Tree를 참조하는 변수(지우지 마셈)             

bool PlayerDetected : 현재 AI 캐릭터의 시야에 플레이어 캐릭터가 들어 왔는 지  

Object(BP_MyCharacter) TargetActor : 추적할 플레이어 캐릭터          

Object(BP_PatrolPath) PatrolPath : 순찰할 지점을 저장하는 PatrolPath 객체    

int32 CurrentWaypointIndex : PatrolPath에서 순찰해야할 target point를 위한 index

★ Blackboard의 Details창에서 BaseClass와 Default값을 설정할 수 있음
  • Behavior Tree는 Root 노드에서부터 시작하여 Simple Parallel, Seletor, Sequece 노드들로 연결이 될 수 있다(각 노드들의 동작은 사진 주석 참고)
  • 지금까지가 Behavior Tree와 Blackboard에 대한 기본 설정이라고 볼 수 있고 이제 AI 캐릭터가 어떻게 동작할 지를 Behavior Tree에서 설정
  • AI는 순찰/추적 이 두 동작을 해야하니 Selector를 사용하여 분기
  • 여기서 분기를 하기 위해선 조건이 필요한데 이 조건의 대상(?), 비교의 대상(?)이 필요함 => 이전에 만들어 둔 PlayerDetected 변수를 사용하는 조건을 추가
    • 노드에 조건을 추가하기 위해선 노드 우클릭 → Add Decorator → Blackboard → Details에서 조건에 맞게 수정
  • 이제 각 분기에 맞는 행동을 해야하는데, 이 때 Task를 사용
    • BTTask_BlueprintBase를 누르면 Task를 만드는 창이 나오는데 이름만 설정해주면 됨
    • 여기까지가 Behavior Tree에서 해야할 일
  • 이제 각 Task에서 동작을 구현
    • Patrol Task 구현
      • 순찰에 필요한 경로를 알아야하는데 이 경로는 Blackboard의 Patrol Path 변수에 있음, 이걸 가져오려면 Task의 Event Graph에서 get blackboard value를 검색해서 가져오려는 변수의 타입에 맞는 노드를 추가하면 됨
      • 이 노드를 추가하면 해당 변수의 값을 가져오므로 Event Graph에서 쓸 수 있도록 변수로 승격시키고 Details창에서 Instance Editable을 활성화 => 변수를 수정할 수 있도록
      • Behavior Tree의 Task의 Details 창에서 해당 변수에 Blackboard의 PatrolPath 할당
      • 이동을 위한 Event Graph
        • AI MoveTo 노드 : C++코드에서 AIController의 MoveToCurrentPatrolPoint() 함수와 대응
        • Bind Event to MoveCompleted 노드 : C++코드에서 AIController의 OnMoveCompleted() 함수와 대응
          • Event ReceiveExcuteAI 노드의 Owner Controller에서 끌어와야 Bind Event to MoveCompleted 노드를 찾을 수 있음
    • 이렇게 하면 순찰 Task는 완성이 된 것, 하지만 아직 AIController와 Behavior Tree를 연결하지 않았고 + BP_PatrolPath를 배치만 해놨지 Blackboard에 연결하지는 않음
      • C++에서 OnPossess() 함수에서 이동 함수를 호출한 것처럼 Event On Possess 노드에서 Run Behavior Tree 노드를 실행하도록 하여 직접 만든 BT_EnemyAI가 실행되도록 함
      • 그리고 World에 배치한 BP_PatrolPath를 가져와서 Blackboard의 PatrolPath변수에 할당
    • Chase Task 구현
      • AI 캐릭터가 플레이어를 쫓으면 되니까 Chase Task는 별 것 없이 Blackboard에서 있는 Target Actor를 가져와서 AI 캐릭터를 Target Actor가 있는 곳까지 움직이게 하면 됨
    • 그럼 캐릭터 감지를 해야하는데 이건 언리얼 엔진에서 AI Perception(시야, 청각 등)를 구현해서 제공을 해줌
      • AI 캐릭터의 컴포넌트에 AI Perception 컴포넌트 추가 및 [Details→AI Perception→Senses Config 추가→AI Sight config 설정] => AI 캐릭터가 [인지]를 할 수 있음
      • AI Perception Component의 Details창을 내려보면 On Target Perception Updated Event를 볼 수 있고, 이 Event가 발생했을 때 Blackboard의 PlayerDetected변수를 변경하면 Behavior Tree의 분기를 [PatrolTask에서 ChaseTask]로 바뀔 수 있음 + 인지한 Actor가 뭔지 확인해서 플레이어 캐릭터이면 Blackboard의 TargetActor변수에 해당 플레이어 캐릭터를 할당
        • On Target Perception Updated Event : AI 캐릭터가 AI Perception으로 계속 무언가를 인식/인지하려고 하는데, 무언가 인지가 된 순간에 발생하는 Event
    • 여기까지하면
      1. C++코드로 AI 캐릭터가 소환되거나 World에 있으면 AIController가 바로 빙의가 되도록 만듦
      2. AIController에서 Behavior Tree를 통해 AI 캐릭터를 순찰할 수 있도록 만들었고
      3. AI 캐릭터가 Player 캐릭터를 인지하게 되면 Player 캐릭터를 추적하도록 만들었음
    • 하지만 마지막으로 플레이어 캐릭터가 인지가 될 수 있도록 만들어야 한다
      • AI 캐릭터와 비슷하지만, 다르게 AI Perception Stimuli Source Component를 추가하고 Details를 수정(AI 캐릭터에서 AI Sight로 설정했으니 여기서도 AI Sight로 설정)
언리얼 엔진은 충돌 때도 이런 것과 비슷하게 짝이 맞아야 block이 되고 overlap이 되고 그랬는데, AI Perception에서도 서로가 합이 맞아야 제대로 작동하도록 시스템을 구현해놓은 것 같다

구현하면서 이상한 점들을 몇 가지 찾았는데
1. 순찰을 제대로 하지 않는다는 점
2. 플레이어 캐릭터를 인지하더라도 현재 순찰 목표 지점을 찍어야 함(목표 지점을 찍고 나서 시야에 들어가야 쫒아옴)
3. 그리고 지금 순찰을 제대로 하지 않아서 이런 동작을 하는 것 같음. 만약 순찰 제대로하면 시야에 들어와도 안 쫓아올 것 같음.

나중에 블루프린트로 만든거 C++로 구현해볼 때 이런 문제들도 해결해보기(따라가기도 바빠서 부트캠프 끝나고나서 해볼지도...)



갑자기 질문 : AI Controller에 그냥 Run Behavior Tree했는데 이러면 연결이 되는 건가? C++이였다면 Behavior Tree 객체가 있을건데 BP에서는 어떻게 관리를 하는가? -> 그럼 Blackboard는? Run Behavior Tree 노드는 그래도 어떤 Behavior Tree를 실행할지 정하는게 있는데 Blackboard가 여러개 잇는 경우에는 Get Blackboard가 되나?


C++에서 Task를 구현하고 싶다면 BTTaskNode를 상속받아서 구현할 수 있음







참조 사이트
1. https://mingyu0403.tistory.com/251

블루프린트로 만든 걸 C++ 코드로 만들 때 참고할 만한 사이트
1. https://dev.epicgames.com/documentation/ko-kr/unreal-engine/basic-navigation-in-unreal-engine#4-%EC%B2%AB%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0
2. https://dev.epicgames.com/documentation/ko-kr/unreal-engine/using-avoidance-with-the-navigation-system-in-unreal-engine
3. https://forums.unrealengine.com/t/behavior-tree-tutorial/105
4. https://makerejoicegames.tistory.com/480
5. https://mingyu0403.tistory.com/262#google_vignette 여기는 관련 카테고리까지 확인
6. https://makerejoicegames.tistory.com/477 여기도

profile
드가자

0개의 댓글