안녕하세요! '언리얼로 만들어보는 RPG'의 필자입니다. 정말 부끄러우나 마지막 글로부터 1년 3개월 정도만에 나머지 내용을 정리하려고 돌아왔네요. 변명을 조금 해보자면 회사에서 폐 안끼치려고 부족한 실력 끌어모아 몇달을 달려왔더니 그럴 여유가 전혀 없었습니다. 지금은 이미 시간이 너무 지나버려서 계속 보시는 분들이 없겠지만 그분들에게 심심한 사죄의 말씀을 올립니다.
<대충 지친사회인 짤>
너무 긴시간 글을 안썼더니 어떻게 시작해야할지 모르겠어서 잠깐 제 작업물을 확인하고 올 필요성이 있습니다.
피자도 좀 먹고 그간의 게시글도 확인좀 하느라 늦었습니다. 예전(약 1년전)의 방식대로 저희는 간략한 견적을 작성해보고 시작하도록 하겠습니다.
1. 몬스터의 행동을 컨트롤할 AIContoller 작성
2. 생명을 불어넣어주는 AnimInstance 작성
우선적으로 쓸데없는 이야기인데 제목과 넘버링을 어떻게 해야할지 조금 고민했습니다. 몬스터(1)로 갈지 AI(1)로 갈지 말이죠 어짜피 생각해보면 몬스터를 구성하기 위한 모든 과정이라 몬스터를 넘버링으로 사용해도 될거 같았는데 나중에 확인하기 쉽도록 분할해서 넘버링을 메기는게 나을것 같아서 AI로 가기로 했습니다.
정말 쓸데없는 이야기는 이정도로 끝내도록 하고 몬스터 클래스를 만들기도 전에 AIController를 만들러 출발하도록 하겠습니다.
AEnemyAI.h
이것이 앞으로 몬스터를 조종하게될 Controller입니다. 지금 보니까 전방선언 해두고 쓰지도 않는게 두개나 있네요. 거기에 더해서 사실 저 AIOwner는 따로 선언해서 사용할 필요는 없었습니다. AIController의 2계층 위에 있는 Controller에 빙의할때 대상을 Pawn이라는 변수에 저장하거든요. 거기까지 확인하지 않았던 필자의 잘못이니 지금은 그러려니 하고 넘어가도록 합시다.
사실 필자 작업한 AEnemyAI 자체에서 하는일은 크게 없습니다. 그걸 왜 이야기 하냐면 BTData라는 변수때문에 나온 이야기입니다. 해당 BehaviorTree는 실제 AI의 행동을 관리하고 상태를 변경시키는데 활용하는 객체입니다. 물론 AIController가 아무것도 하지 않는다는것은 아닙니다.
어째서냐? 하면, 플레이어의 눈을 Camera가 대신 하는것처럼 몬스터 같은 NPC에게도 눈과 같은 역할을 할 수 있는게 있습니다. 해당 친구는 PerceptionComponent 인데 시각, 청각 등의 감각에 대한 반응을 설정할 수 있습니다. 좀더 디테일하게 파고들어가면 좋겠지만 필자 본인도 거기까지는 하지 않아서 말을 줄이겠습니다.
함수가 두가지 구현되어있는데 Possess는 몬스터에 빙의할때 실행되고 나머지 하나인 UpdatePerception 함수가 중요하다고 볼 수 있습니다.
AEnemyAI.cpp
몬스터에게 빙의를 하면 BTData를 이용해 RunBehaviorTree함수를 실행합니다. 해당 함수는 AIController에 선언 및 정의되어 있으며 인자로 들어온 BehaviorTree객체를 BrainComponent로 사용하도록 합니다. 그리고 PerceptionComponent가 Null이 아니라면 자극이 들어올때에 대한 처리를 하기위해 작성한 UpdatePerception 함수를 AddDynamic으로 델리게이트에 바인딩 시켜줍니다.
필자가 바인딩 시킨 함수를 보시면 새로운 자극을 가한 객체들이 TArray<AActor*> 형태로 전달됩니다. 몬스터는 싸워야할 대상인 Player인지 확인해보고 맞다면 BlackBoard에서 해당 변수를 가져와 NULL인지 아닌지에 따라서 처리가 나뉘게 됩니다. 이러한 작업에는 이유가 있는데 이 Perception이 자극을 받는 경우가 시각을 예로들자면 범위 안에 들어올때와 나갈때 둘다 반응하기 때문입니다. 시야 밖으로 나가는 경우에는 BlackBoard에 있는 Player변수를 NULL로 변경해야 그에 따른 행동을 취할 수 있기 때문이죠
이쯤 오니까 마구잡이로 글을 쓰기전에 몬스터의 AI를 구성하기 위해서 필자가 사용했던 엔진 기능들에 대해서 먼저 정리를 할 필요성이 있다는걸 느꼈습니다. 이번 첫글에서는 AIController, BehaviorTree, BlackBoard, AIPerception에 대해서 필자 나름대로 이해한 내용들을 적어내려가겠습니다.
AIController
크게 설명할것은 없습니다. 플레이어블 캐릭터를 조종할때 사용하는 컨트롤러가 PlayerController라면 반대로 NPC를 조종하기 위해서 사용하는 컨트롤러는 AIController입니다. 지금까지 글을 작성하며 언급한 BehaviorTree나 AIPerception등 상태의 변경이나 관리, 행동을 변경시키는 객체들을 담아두고 관리하는 용도로 사용하면 된다고 생각합니다.(?)
문서의 링크를 남겨두기는 하지만 문서에도 딱히 적힌 내용은 별로 없어서 실망하실 수 있습니다.
https://docs.unrealengine.com/ko/Gameplay/Framework/Controller/AIController/index.html
BehaviorTree(행동트리)
NPC에게 행동을 지시하는 두뇌와 같은 역할을 하는 객체입니다. 언리얼에서의 행동트리는 Task, Decorator, Service 세가지의 실행 노드가 존재하며 Task는 가장 마지막인 잎 위치에 있어야하고 Decorator나 Service 노드의 경우 Composite라고 부르는 흐름제어 노드 (Selector등)와 Task에 함께 사용할 수 있습니다. Decorator는 일종의 조건문이라고 생각하시면 편하고 Service는 필자가 사용은 하지 않았으나 상당히 중요한 기능인 EQS를 실행시키거나 BlackBoard에 있는 정보의 최신화를 하는등에 사용가능합니다.
행동트리 문서는 안타깝게도 한글로 번역이 안되어 있는데 필자가 설명한 정도만 알고 있어도 크게 어렵지는 않으리라 예상합니다. 또한 퀵 스타트 가이드를 따라가다 보면 금새 감을 잡으실꺼라 믿습니다.
잠깐 옆길로 빠져서 한창 필자가 공부할때 (물론 지금도 공부를 안할 수 없지만) FSM이라 하는 유한상태기계를 통한 AI를 구성하는 방식을 사용하기도 했었습니다. 그게 정말 FSM인지 아닌지는 잘 모르겠으나 AI 구성에 관해서 관심이 있다면 이거저거 찾아보시는것도 나쁘지 않으리라 생각합니다. 물론 언리얼에서는 행동트리를 쓰는게 좋지 않을까요?
BlackBoard
행동트리를 설명하며 두뇌와 같다는 이야기를 했었습니다. 아쉽게도 행동트리 혼자서는 온전한 두뇌는 어렵고 신경망정도라고 생각하면 이해가 쉽지 않을까 생각합니다. 그 이유라하면 BlackBoard는 두뇌의 저장공간과도 같은 역할로써 자신의 현재 상태와 기억하고 있어야할 다양한 정보를 관리하는 객체이기 때문입니다. BlackBoard에서 적에 대한 정보가 변경이 되면 그걸 기반으로 행동트리는 필요한 행동을 하도록 지시를 내리는 것이 되는것이죠
AIPerception
몬스터의 상태에 따른 행동을 관리하는게 BehaviorTree였다면 상태의 변경에 관여를 하는건 방금 언급한 PerceptionComponent입니다. 이 Perception에 새로운 자극 예를들어 설정한 시야범위 안에 어느 객체가 등장했다거나 청각범위안에서 소리가 들리는등의 일들이 벌어지면 OnPerceptionUpdate라는 델리게이트를 호출하게 됩니다.
특별한 내용이 있지는 않으나 더 많은 정보를 알고 싶으시다면 문서를 적극 추천드립니다.
https://docs.unrealengine.com/ko/Engine/ArtificialIntelligence/AIPerception/index.html
뭐 얼마 적은거 같지도 않은데 시간은 시간대로 지나고 허리는 허리대로 아프군요. 예전처럼 2~3일에 한개씩 올리는건 좀 어려울거 같고 7~10일 정도에 한번씩 올릴거 같습니다. 시간을 내어서 이 글을 읽어주신 분들에게 감사의 말씀을 남기며 필자는 다음에 다시 돌아오겠습니다!
'프로그래밍 > 언리얼' 카테고리의 다른 글
언리얼로 만들어보는 RPG - AI(3) (0) | 2021.01.24 |
---|---|
언리얼로 만들어보는 RPG - AI(2) (0) | 2020.08.28 |
언리얼로 만들어보는 RPG - 스킬창 구현(3) (0) | 2019.04.10 |
언리얼로 만들어보는 RPG - 스킬창 구현(2) (0) | 2019.04.04 |
언리얼로 만들어보는 RPG - 스킬창 구현(1) (0) | 2019.03.27 |