게임 심화편 내비게이션 시스템
안녕하세요 이영훈입니다
이번 시간에 알아볼 내용은 내비게이션 시스템입니다
이번 단원에서는 언리얼의 길 찾기 시스템인
내비게이션 시스템에 대해 학습하겠습니다
그리고 Navigation Invoker를 이용해
실시간으로 길을 생성하고
목적지를 향해 이동하는 방법을 학습하겠습니다
또한 이를 이용해 패트롤 기능을
구현하는 방법을 학습하겠습니다
내비게이션 시스템을 이용한 길 찾기
이번에는 길 찾기 처리를 해볼 건데요
기본적으로 에너미가 플레이어를 향해서 이렇게 오죠
그런데 만약에 사이에 장애물이 있다면 어떻게 될까요?
큐브를 하나 배치해서 결과를 잠깐 보도록 할게요
이렇게 장애물을 만들고 실행을 해보면
에너미가 플레이어 쪽으로 올 수가 없습니다
그거는 왜냐면 에너미는 지금 직진을 하고 있거든요
앞쪽으로 플레이어 방향으로 가기 위해서
이렇게 방향을 정하고
그 방향으로 이렇게 AddMovementInput을 통해서
그 direction으로 쭉 오고 있는 거예요
그런데 그 방향에 어떤 장애물이 있거나
그렇게 되었을 때는 제대로 이동할 수가 없겠죠
그래서 이런 언리얼이라던지 이런 엔진에서는
이런 것들을 처리하기 위해서
길 찾기 시스템을 제공하고 있는데요
보통 그런 것들을 영어로 하면
Path Finding이라고 하죠
이렇게 얘기를 하고
다른 말로 Navigation이라고 합니다
이렇게 부르기도 하고요
그러면 언리얼에 있는 내비게이션 시스템을 이용해서
이것을 제작을 해볼 건데요
보통 Navigation, Path Finding 이런 걸 하면
대부분 가장 유명한 알고리즘이 있죠
알고리즘 중에 A* 알고리즘이 있습니다
이거는 기본적으로 이렇게 면이 있으면
여기에서 최단거리 길 찾기를 하는 개념이고요
예를 들어서 이렇게
배열처럼 이렇게 되어 있는 것들이 있다면
예를 들어 여기에서 여기까지 빨리 가고 싶어요
그러면 여기에 만약에 장애물이 이렇게 있어요
그러면 예를 들어 이렇게 장애물이 있다고 해보죠
그러면 원래는 이렇게 이렇게 가는 게 가장 빠른데
이렇게 이렇게 이동하게 되겠죠
이게 더 복잡해질수록
더 많은 그런 계산을 하게 될 겁니다
기본적으로 이 A* 알고리즘을 이용해서
길 찾기를 하게 되고요
사실 A* 알고리즘은 이렇게 사각형 형태만
할 수 있는 게 아니라
삼각형도 할 수 있고요
이렇게 생긴 삼각형을 이용할 수도 있고요
혹은 이렇게 오각형이나 육각형, 팔각형
이런 것들을 이용해서도 할 수가 있습니다
그래서 현재 이 면과 인접한 다른 면의
길을 만들고 가장 가까운 최단거리를
구하는 공식이라고 보시면 되는데요
언리얼에도 이런 식으로 구현이 되어 있고요
그러면 이것을 처리하기 위해서 기본적으로
어떤 데이터를 만들어야 되거든요
그래서 이거를 좀 간단하게 말하면
길을 만들어야 된다고 할 수 있어요
길이 있어야지 이동을 할 수가 있고요
그러면 언리얼에서 이 길 찾기를 하기 위해서
필요한 요소가 크게 보면 두 가지가 있습니다
하나는 길입니다
길이 있어야 돼요 그래야지 갈 수 있고요
두 번째는 실제로 찾아가는 주체가 있어야 돼요
길을 찾는 컨트롤러라고 할 수 있죠
어떻게 보면 길 찾기는 인공지능이죠
이거를 AI라고도 할 수 있어요
인공지능인데 이렇게 해서
그 인공지능에 해당하는 처리하는 녀석이 바로
길을 찾는 컨트롤러라고 할 수 있고
여기에서는 AIController라고 합니다
이렇게 얘기를 할 수 있어요
그래서 먼저 길과 컨트롤 이렇게 만들어 보면 되는데요
기본적으로 길을 만들기 위해서는
여기 보시면 Place Actors에
Volumes이라는 부분이 있습니다
Volumes이라는 곳으로 오셔서 여기에서
Nav Mesh Bounds Volume이라는 게 있어요
이거를 이렇게 꺼냅니다
이게 이제 길을 만들어내는 그런 볼륨
어떤 영역을 만들어낼 수가 있는데요
이걸 이용해서 한번 해보도록 할게요
그러면 Volume을 보시면
여기에 크기를 지정할 수가 있습니다
크기를 일단은 센터에 놓고요
크기를 넓혀주면 되는데
여기 보시면 Brush Settings이라는 곳이 있어요
이거를 건드려도 되고
아니면 Scale을 건드려도 됩니다
둘 중 하나를 만져주시면 되는데
저는 여기 있는 Brush Settings으로 해볼게요
이것을 선택하고요
적당하게 한 2000이라고 해볼까요?
그러면 이정도 되네요
그러면 더 커야 될 것 같습니다
지금 보니까 한 4배? 8000이라고 해볼까요?
그러면 이 정도 되네요 그리고 Y로도 8000
이렇게 하면 가득 차게 되죠
Z는 굳이 안 만져도 될 것 같고요
이 상태에서 실제로 길이 어딘지 볼륨을 보려면
아무것도 누르지 않은 상태에서 여기
뷰포트를 선택한 상태에서
P버튼을 한번 눌러보세요 키보드에 P키가 있죠?
P를 누르시면 이렇게 영역이 나옵니다
여기가 이제 길이 되는 거예요
그러면 이제 길이니까 이 길 위에
저 AI 에너미가 이렇게 길 위에서
이렇게 돌아서 올 수 있게 하면 되겠죠
그래서 장애물을 조금 더 배치를 해볼까요? 길게
이렇게 좀 더 넓히겠습니다
이렇게 배치를 한 다음에 길을 찾게 만들면 되는데
이번에는 에너미를 잠깐 볼게요
에너미를 선택하고 에너미의 디테일 부분을 보시면
쭉 내려보면 Pawn이라는 부분이 있습니다 여기 보면
Pawn이 있는데요 Pawn을 보면
여기 이렇게 되어있죠?
AI Controller Class에 AIController라는 게
이렇게 끼워져 있습니다 그렇죠? 그리고
Auto Possess AI는 Placed in World라고 되어있어요
이렇게 되어있으면 어떻게 되냐면
기본적으로 이 녀석의 컨트롤러는 AIController입니다
여러분 언리얼은 기본적으로 이런 개념이에요
말판이죠 이렇게 월드가 있는데 이 월드에
이런 캐릭터 같은 것들이 이렇게 있는 거죠
Pawn이나 캐릭터들이 있고요
그러면 사용자가 여기 있는 말 중에
어떤 것을 잡고 움직일 것인가라는 형태로 이제
제어를 한다고 보시면 돼요
그래서 보통 Player Controller가 있고요
이렇게 있고 그 다음에
이거는 이제 입력을 받아서 입력값을 이렇게 넣어줘서
그걸 제어할 수 있는 개념이 되는 거고
이거 말고 길 찾기라든지
이런 것들은 AIController라는 게 따로 있습니다
이렇게 있고 그래서 AI도요 얘를 붙드는 거예요
포제스하는 개념이 되는 거죠
플레이어는 이걸로 포제스하고 있고
이거는 AI가 포제스하고 있고
이런 개념으로 이해하시면 됩니다
그럼 기본적으로 이 녀석은 이렇게 되어있죠 아 그리고
이 Auto Possess AI가 뭐냐면 여기 펼쳐보시면 이렇게
두 가지가 있습니다 Spawned와 Placed in World
이렇게 있고 이거는 이제 둘 다 할 수 있는 거죠
이건 무슨 뜻이냐면 이 캐릭터를 레벨에 배치할 때
이렇게 정적으로 우리가 이렇게
드래그&드롭으로 이렇게 배치를 할 수도 있잖아요
그런데 그게 아니라 스폰 액터를 통해서
여기다가 레벨에 배치를 할 수도 있어요
스폰을 시킬 수도 있다는 거죠
그러면 스폰을 했을 때 이 컨트롤러를
Possess AI, 포제스를 어떻게 할 것이냐라는 거예요
그래서 여기 보면 이렇게 되어 있는데
이렇게 하면 예를 들어서 여러분들이 에너미를
생성하는 생성기를 만들었다고 해볼게요
그러면 그 녀석은 AIController가 포제스 하지 않게 돼요
왜냐하면 이거는 월드에 배치되어 있는 놈만
자동으로 컨트롤러가 이렇게
포제스를 하는 개념이기 때문이죠
그래서 이거를 이렇게 바꿔주셔야
나중에 여러분들이 에너미를 생성하는 걸로 만들었다
그러면 에너미를 스폰시키면 자동으로 AIController가
에너미를 포제스를 딱 해서 시작을 하게 됩니다
이렇게 구성을 해주시면 되고요
근데 우리는 에너미 생성기를 만들지는 않을 거예요
그래서 한 마리만 할 거라서 사실 이거는
크게 의미는 없지만 여러분들이 작업하시다 보면
이것 때문에 좀 곤란을 겪을 수가 있습니다
그래서 한번 말씀드렸고요
보통 이렇게 해놓고 작업하시면 됩니다
한번 저장해주고요
그러면 에너미를 한번 이동을 시켜볼 건데요
그러면 Move에서 처리를 해야겠죠
그런데 아까 두 가지 조건이 있다고 했는데
하나는 길이에요 길은 만들었죠 볼륨으로
두 번째는 AIController를 가지고 진행을 해야 됩니다
그래서 EnemyFSM에 보면
AIController를 가지고 있지 않죠
그래서 여기다가 AIController를 가지고 있도록
변수를 하나 만들어줄게요
UPROPERTY라고 하고요
class AAIController
이 컨트롤러들은 A로 시작합니다
액터 형식으로 되어있어요
플레이어 컨트롤러도 마찬가지입니다 A로 시작합니다
이렇게 하고 AI라고 하겠습니다
그러면 이 값을 채워야겠죠
BeginPlay에서 채워주도록 할게요
여기에서 AI는 Me에 있겠죠 Me가 가지고 있으니까요
Me에 GetController 하시면 가지고 올 수 있고요
이것을 캐스팅해서 사용하도록 할게요
Cast라고 하고요
여기는 AAIController가 되겠습니다
그러면 이걸 가지고 왔으니까
이걸 가지고 이동을 해볼 건데요
TickMove로 가서 AI를 이용해서 이동해 볼 건데요
여기 있는 Me에 AddMovementInput은 주석을 치시고
그 다음에 AI를 가지고 이동을 하겠습니다
AI에 보면 Move라고 검색해 보시면
MoveTo 혹은 MoveToActor, Location
이런 것들이 있습니다 이거를 사용하면 되고요
저는 여기에서 MoveToLocation을 이용해 볼게요
이 함수는요 여기에 보면 목적지를 넣게 되어 있죠
그리고 이렇게 되어있는데
이거를 보기가 좀 어려우니까
블루프린트에서 한번 잠깐 보고 갈게요
여기에서 AI MoveTo라고 하시면
이런 것이 나오는데요
여기 보면 목적지에 어떤 위치를 넣거나
그 목적지에 해당하는 Target Actor를 넣으면
그쪽으로 이동할 수 있고요
그 다음에 이 Acceptance Radius는
그 근처까지 가까이 가면 도착했다라는
어떤 범위 값입니다
그래서 예를 들어서 그런 거 있잖아요
정확하게 그 포지션에 이 로케이션에
딱 일치해야지 원래 도착한건데
그렇게 하면 사실 정확하게 계산하기가 어렵죠
그래서 좀 범위를 주는 거예요
지금 보면 5cm로 되어있는데 그러면 조금 어렵거든요
그래서 이거를 한 50cm 정도 이런 식으로 주면 되고요
이거는 여러분들이 컨셉에 맞게
적당한 값을 넣어주시면 됩니다
그래서 도착하게 되면 여기에서
이런 신호가 나오는 거죠 이렇게 생겼어요
그래서 이거랑 비슷하게 여기도 값을 채워주면 되는데요
여기 보시면 Dest.가, Destination이 들어가죠
목적지가 들어가고 그 다음에 역시 마찬가지로
AcceptanceRadius가 들어갑니다
그 다음에 비슷하게 StopOnOverlap
이런 게 들어가고, 그렇죠? 이렇게 되어있는데요
여기에서 이제 채워줘야 될 것은
가장 중요한 것은 이거죠 Destination, 목적지
목적지는 사실 Target의 GetActorLocation입니다
이게 목적지예요
그래서 이거를 FVector로 일단 따로 가지고 있을게요
destination이라고 하고 이렇게 가지고 있겠습니다
그러면 이것을 direction을 이렇게 바꿔주고요
그 다음에 destination을 여기다가 이렇게 넣어주겠다는 거죠
그리고 적당히 이제 도착했나라고 처리할 때는
예를 들어 50 이렇게 넣어주면 되고요
그런데 이거를 사용하려면
그냥은 사용할 수가 없습니다
모듈을 추가를 해줘야 돼요
그래서 여기에서 Build.cs 파일로 가서
여기 기억나시죠? 모듈 추가하는 곳
여기다가 이렇게 추가를 할 건데요
두 개를 추가할 거예요
그런데 이거도 한번 보면 좋을 것 같아요
그러면 AIController니까
한번 검색을 해볼까요?
AIController를 보면
이것은 AIModule이라는 것을 사용해야 됩니다
그래서 이 모듈을
여기다가 이렇게 추가를 해주겠습니다
AIModule이라고 합니다
그리고 이왕 하는 김에 한 가지 더 넣을게요
여기 보시면 AIModule도 있는데
우리가 길 찾기를 할 때는
UNavigationSystemV1 이라는 게 있거든요
이 클래스를 사용할 건데
이것도 같이 모듈을 미리 추가해 놓겠습니다
여기 보면 여기 모듈은 NavigationSystem이에요
이거를 그대로 여기다가 이렇게 작성을 해주겠습니다
그래서 AIModule과 NavigationSystem
이렇게 두 가지 모듈을 추가를 하고요
그리고 돌아오겠습니다
이쪽으로 와서 이걸 사용할 거고요
이거 헤더 추가를 해야겠죠
여기에서 헤더는 이렇게
AIController라는 헤더를 이렇게 추가를 해주시면 되고요
이것도 보시면 이렇게 추가하면 되는 거죠
이렇게요
이렇게 추가하면 됩니다
그리고 여기에서 만들었으니까 한번 실행을 해볼까요
이제 진짜로 가는지
여기에서 일단은 언리얼을 잠깐 끄고요
이거는 필요 없으니까 삭제하겠습니다
잠깐 꺼주고요
여기에서 빌드를 한번 해볼게요
다시 실행을 하고요
그러면 에너미가 이제 저 장애물을 피해서
주인공을 향해서 이동하는 것을 볼 수 있고요
이렇게 도망가면 이렇게 잘 따라오게 됩니다
이게 기본적으로 제공되는
길 찾기 시스템이라고 보시면 돼요
그래서 가장 핵심은 길을 만들고
그리고 AIController를 이용해서 이동을 한다
길을 찾아간다라고 보시면 됩니다
길 찾기는 결국은 목적지를 향해서 가는 거니까요
이렇게 destination의 개념을 넣어주시면 되고요
Navigation Invoker를 이용한 길 찾기와 패트롤 기능
그러면 이 기능을 활용해서 우리가 길 찾기를 하는데
이런 개념을 만들 거예요
보시면 이렇게 전체 길이 이렇게 있습니다
이렇게 있는데 에너미가 이렇게 서 있어요
그런데 이제 주인공은 여기 있죠
그래서 에너미가 주인공을 감지할 수 있는 거리가 있고
그 다음에 만약에 아무것도 감지를 못했을 수도 있잖아요
그러면 자기가 임의의 점을 하나 찍어서
그쪽으로 이동할 거에요 이렇게 이동하고
여기 도착하면 다시 검색을 해서 또 다른 곳으로 가고
그렇게 막 무작위로 랜덤으로 움직이다가
만약에 에너미의 감지 범위 안에
주인공이 이렇게 들어오게 된다면
주인공이 이렇게 해서 이렇게 들어오게 된다면
이렇게요
그러면 기존에 가던 곳을 가는 것이 아니라
에너미가 이쪽을 향해서 주인공을 향해서 이동하는 거죠
그리고 주인공이 다시 도망갔어요
이렇게 도망을 가서 멀어지게 되면
일정 거리 이상 멀어지게 되면
다시 원래 자기가 가던 곳으로 계속해서 순찰하는 개념
그래서 순찰이라는 개념과
그 다음에 추적이라는 개념을 만들고 싶습니다
이걸 만들기 위해서는 방법은 여러가지가 있어요
상태를 만들어도 돼요
이동 상태에서 내가 순찰하거나 추적하거나
이렇게 처리를 할 수도 있고요
다양한 방법이 있는데
언리얼에는 길에 대한 처리를 할 수 있는 방법이
여러가지가 있는데 그 중에 인보커라는 게 있습니다
인보커라는 게 있는데 인보커는 이런 개념이에요
길이라는 게 굉장히 정적으로 만들어지는 거거든요
처음에는, 무슨 말이냐면
이렇게 엄청나게 큰 길이 있다고 해볼게요
굉장히 큰 맵을 만들었는데
거기에 길 정보를 만들었어요
그러면 처음에 길 정보를 만드는데
시간이 좀 많이 걸리겠죠
그래서 이제 다 만들어졌으면 그 길 위에
AI가 이렇게 있어서 거기를 이동을 하게 된다
이런 개념인데 만약에 이게 오픈월드다
이러면 좀 문제가 되겠죠
왜냐하면 이 맵이 엄청나게 클 거 아닙니까?
그러면 이 맵의 오픈월드에 대한 레벨을 수정할 때마다
다시 길을 또 만들어야 되고
그런 일을 해야 되는데
그러면 부하가 걸릴 수 있고
성능적인 이슈가 발생할 수가 있어요
그래서 어떤 개념이 있냐면
이 인보커라는 건 뭐냐면
전체 길을 물론 정적으로
그러니까 정적이라는 개념이 있고
동적이라는 개념이 있어요
정적이라는 건 미리 다 해놓는 거죠
미리 만들어 놓는 것
이걸 얘기하는 거고
동적이라는 것은 그때그때 만드는 거죠
그렇죠?
그래서 인보커를 이용하면 이런 식으로
그때그때 길을 만들어서 처리를 할 수가 있어요
그러면 인보커에서는 길을 만들 때 어떻게 되냐면
내가 이제 새로 검색할 영역이 있고
그 다음에 이렇게 이동을 하잖아요
그러면 길이 이미 있었을 거 아닙니까?
그렇죠? 그럼 새롭게 이렇게 길을 만들어낼 건데
그러면 기존에 있는 길은 범위를 벗어났잖아요
그러면 이걸 삭제할 거예요
그래서 동적으로 계속해서 만들어서
자기 주변만 이렇게 길로 인식을 하는 그런 개념입니다
그래서 멀리 있으면 길 위에 있는 것이 아니라
자기의 인식 범위 밖에 있다고 이해해서
길이 아닌 거죠
이걸 이용하면 순찰, 추적 이런 것들을 할 수가 있겠죠
실제로 거리를 측정해서 해도 되는데
이런 식으로 인보커를 이용합니다
NavigationInvoker라고 하는데
이것을 이용해서 또 만들 수가 있어요
그래서 이런 것들을 한번 학습을 해보려고 합니다
이것을 처리하기 위해서 일단 인보커를 등록을 해줄 건데
일단 에너미로 갈게요
에너미에다가 이렇게 Add 컴포넌트를 하나 추가할 겁니다
이렇게 해서 invoker라고 적어보세요
NavigationInvoker라는 게 있습니다
이렇게 추가하면 되는데 근데 이렇게 하면 나중에 또
에너미를 새로 만들거나 그러면
이게 없잖아요 그렇죠?
그래서 이것은 코드로 생성해서 넣어주도록 하겠습니다
여기 보시면 Navigation의 Generation Radius와
Removal Radius 두 가지가 있어요
이게 그것입니다
이것은 새로 만드는 길의 영역이고
이동한 후에 멀어졌잖아요
그러면 이 정도까지 멀어졌으면
기존의 그 부분에 대해서는 삭제하겠다
이런 개념이라고 보시면 됩니다
그래서 저는 이것을 한 500, 800 정도
이렇게 배치를 하고 작업을 할 거거든요
그러면 이것을 코드로 먼저 작성을 한 다음에
그 다음에 다시 이어서 할게요
에너미의 헤더 파일로 갑니다
여기다 만들게요
그래서 UPROPERTY
(EditAnywhere)라고 하고요
class UNavigationInvokerComponent
이런 게 있습니다
이거는 NavInvoker라고 할게요
Comp라고 적어볼까요?
이걸 생성해 주겠습니다
그러면 Enemy의 생성자로 가서
여기 있네요 바로 밑에다가
이어서 작성하겠습니다
인보커도 보시면 이렇게 점선이 있죠?
여기 실선이 하나 있네요
선 아래쪽에 있죠?
여기 있는 것들은 액터 컴포넌트들이에요
위에 있는 게 씬 컴포넌트들이고요
그렇게 보시면 돼서
이거는 밑에 있으니까 어태치 할 필요가 없죠
여기에서 생성을 해 주겠습니다
CreateDefaultSubobject라고 하고요
여기는 UNavigationInvokerComponent
이름을 지어주시고요
그리고 여기에 값을 넣어주면 되겠죠?
아까 전에 우리가 여기에
Generation Radius와 Removal Radius
이렇게 500, 800으로 줬습니다
5m, 8m 이렇게 작성을 한 거고요
이렇게 쓰고
Generation Radius
여기 있네요
이렇게 넣어주면 되고요
여기에 Generation Radius와
Removal Radius를 작성을 해주면 됩니다
500, 800 이렇게 적어주도록 하겠습니다
그리고 이거는 헤더 추가를 해야겠죠?
헤더는 이렇게 추가했고요
이것도 이렇게 보면
이렇게 넣어주시면 됩니다
언리얼은 잠깐 껐다가 다시 컴파일해서 열겠습니다
그러면 잘 반영되었는지 확인해 볼게요
이거는 삭제를 하고요
선택해보시면 여기 잘 되어있죠?
그리고 이제 프로젝트 설정을 좀 해줘야 되는데요
Edit의 Project Settings에 보시면
여기에 쭉 내려보시면... 여기 있네요
내비게이션 관련된 항목이 두 가지가 있습니다
Mesh하고 System이 있는데요
Navigation Mesh부터 보면
좀 내려보시면
여기 보면 Runtime에
Generation을 어떻게 할 것인지가 적혀있는데요
여기 Static으로 되어있습니다
이게 아까 얘기한 길을 만들 때
처음부터 만들어 놓고 시작한다는 뜻이에요
근데 인보커를 사용한다는 것은
실시간으로 만들 거기 때문에
이것을 펼쳐서 Dynamic으로 교체해 주겠습니다
그리고 이 Navigation System으로 오셔서
여기 보시면 Navigation
Invoker를 사용할지에 대한 체크박스가 있습니다
이거를 체크를 해 주십시오
이 두 가지 설정을 해주시면 되고요
돌아와서 전체 저장을 한번 해주고요
그러면 이제 이 사각형이 좀 달라졌죠
아까는 전체가 다 길이였는데
이번에는 에너미의 주변 부분만 이렇게 길이 되는 걸 볼 수 있습니다
에너미를 조금 더 뒤로 밀고요
그러면 여기만 이제 길인 거죠
실행을 해보면 에너미가 제자리 걸음을 합니다
주인공을 찾지를 못하죠
가까이 가도
가까이 가면 이제 찾아올 수 있고요
멀리 떨어지면 이렇게 도망을 가보면 찾아올 수가 없습니다
이걸 좀 원활히 하기 위해서
에너미의 움직임의 크기를 주인공보다 좀 느리게 하겠습니다
Enemy Character Movement의 Walk Speed를 한 400으로 좀 줄이겠습니다
컴파일 하고요 그리고
그러면 이거를 이제 구현을 해야겠죠
EnemyFSM의 TickMove로 오셔서
여기에다가 이제 작성을 해보겠습니다
목적지를 검색하기 위해서
먼저 경로를 탐색해야 되는데요
탐색을 하기 위해서
auto라고 하고요
ns라고 할게요 이게 뭐냐면
Navigation System을 얘기하는 거고요
그래서 UNavigationSystemV1이라는 게 있어요
이것에서 GetNavigationSystem을 가져오겠습니다
여기에는 World가 들어가죠
GetWorld 넣어주시면 되고요
그리고 이거를 가지고 할 건데
결론적으로 ns에 보면
FindPath... Sync, Async 두 가지가 있어요
Async는 비동기로 이제 하는 거고
우리는 동기로 처리를 하겠습니다
이렇게 Sync라고 하고요
여기에 이제 값을 넣으면 되는데
여기 보면 PathFindingQuery가 들어가고
그 다음에 PathFindingMode가 들어가는데
이거는 그냥 기본적으로 들어가니까요
Query를 만들어줘야 될 필요가 있습니다 그래서
FPathFindingQuery
이 변수를 이렇게 만들어줄게요
Query라고 하고요
이거를 여기에 이렇게 넣어주시면 됩니다
그 다음에 AI에 보면
여기에는 BuildPathfinding 이런 게 있어요
그래서 이걸로 길을 실제로 빌딩을 하는 거거든요
이렇게 해서 여기에는 이제 4개 변수가
FAIMoveRequest 여기 들어가고
그 다음에 Query가 들어갑니다
그래서 FAIMoveRequest
변수가 또 하나 있어야겠죠
이거는 req라고 할게요
Request에는 요청이니까
뭐가 들어가냐면 AcceptanceRadius가 들어갑니다
아까 전에 우리 50으로 넣으면 될 것 같아요 이렇게
이거는 이제 근처 50cm까지 근처에 오면
도착했다 이런 식의 처리니까요
그 다음에 요청할 때
목적지를 넣어야겠죠 여기에
SetGoalLocation 이렇게 해서
destination을 이렇게 넣어주도록 하겠습니다
그리고 이것을 여기다가 넣으면 되는데요
여기에 req를 넣고
그 다음에 query를 이렇게 넣어주시면 되겠죠
그러면 req를 통해서
query가 채워지는 그러한 형태가 되고요
이거 사용하시면 이런거 다 헤더를 추가하셔야 돼요
저는 위에 추가가 되어 있는 것 같아요
이렇게 추가되어 있는데
이렇게 Navigation System도 추가를 해주십시오
여기 검색해보시면 이렇게
이렇게 해서 여기 이 내용을
저 상단에다가 헤더 추가를 해주시면 되겠습니다
이렇게 하면 일단 길을 찾았습니다 그러면
이것을 가지고 뭔가 처리를 해야겠죠
이거에 이제 반환값이 있어요
여기 보면 FPathFindingResult라는 게 있는데요
이걸 받을게요
auto r 이렇게 받겠습니다
그리고 만약에 이것이 성공했다면
아 r이 아니죠 r에 Result가 있습니다
이겁니다 이걸로 해야되고
== 하면 되고요
이거는 이제 ENavigationQueryResult에요
그러면 이거거든요
여기에서 보면
성공 실패 이런 것들이 나와요
성공했을 때만, 어떻게 해요?
이때는 추적이죠
여기에서는 이제 destination을 향해서 가는 거니까
이 destination은 여기 보면
Target의 Location입니다 그렇죠?
그래서 목적지가
우리가 에너미 입장이니까
플레이어를 목적지로 했다면
이제 쭉 가면 되는 거고요 그쪽으로
그래서 기존에 구현했던 내용이
여기에 추적으로 들어가면 되고요
근데 인보커는 현재 전체 길에서
자기 영역만 딱 체크하는 거잖아요
그러니까 길 밖에 있을 수도 있는 거죠
else인 경우에는
목적지가 여기가 아닌 거죠
여기가 아니라 랜덤한 위치를 넣고 싶어요
그래서 RandomLocation
목적지를 기억을 하고 있어야겠죠?
그래서 변수를 하나 기억하고 있겠습니다
그러면 RandomLocation
이렇게 변수를 하나 만들어줄게요
그러면 이 일을 먼저 해야겠죠
이걸 쓰려면 랜덤한 위치를 만들기 위해서
여기 헤더에다가 선언을 해줄게요
제일 아래쪽에다가 할까요?
이렇게 해서 FVector로
RandomLocation
이렇게 기억하고 있을 거고
그럼 이것을 채우는 일을 해야겠죠
채우기 위해서 함수를 하나 만들건데
bool 타입으로 할 거고요
GetRandomLocation
이라는 함수를 만들까요
InNavMesh
이렇게 해주겠습니다
그럼 첫번째 인자는 원점이에요
원점에서부터 얼마의 반경 안에
하나의 점을 찍을 거니까요
그래서 FVector origin
이렇게 할 거고요
그 다음에 float radius
그리고 결과를 반환
여기 리턴 값이 bool이니까
그러면 실제로 어떤 값을 넣어서
여기에서 채워주면 좋을 것 같아요 그래서
FVector& 이렇게 해서 Out
결과 리턴 값을 여기다가 받겠습니다
OutLocation
이런 식으로 변수를 만들어줄게요
이렇게 변수를 선언을 해주고
그 다음에 이걸 구현부로 만들겠습니다
이렇게 만들고요
여기도 이제 Navigation System이 필요해요
그래서 auto ns라고 하고요
UNavigationSystemV1에서
GetNavigationSystem
가지고 오도록 하겠습니다 GetWorld로
그럼 ns에 보면
random
GetRandom 하시면
보시면 이렇게 함수가 있어요
포인트 같은 경우에는
GetRandomPointInNavigableRadius
이거는 전체 길에서 랜덤 값을
탁 찍는 거고요
Reachable은 내가 이동할 수 있는 위치
랜덤 지역 중에서 이동할 수 있는
위치를 의미한다고 보시면 돼요
그래서 이걸로 가져오도록 하겠습니다
이렇게 하고
매개변수가 인자값을 뭘 넣어야되냐 보면
여기 origin이 들어가죠
origin 넣어주시고요
그 다음에 radius가 들어가요
반지름이 들어가고 그 다음에
FNavLocation & 해서
이런 변수를 하나 넣어줘야 됩니다
그럼 여기에 결과를 이렇게 담아주는 방식이죠
그래서 FNavLocation 이렇게 해주고요
이거를 로케이션이라고 할까요?
이렇게 넣어주도록 하겠습니다
그러면 여기에 실제로 담기겠죠
그리고 이것은 bool을 반환해요
성공, 실패가 있다는 거죠
그래서 그것을 받겠습니다
bool bResult = 이렇게 받을게요
그리고 이걸 반환할 거고요
그리고 만약에 결과가 참이면
성공했으면 그때
OutLoc에다가 여기에서 담아온
로케이션 값이 있죠 이거를
이렇게 담아주도록 하겠습니다
이러한 함수를 하나 만들면 되고요
이 함수는 처음에 시작할 때
BeginPlay에서
여기에서 랜덤 위치를 하나 정하고 갈 거예요
이렇게 해서 origin이니까 GetActor
이거는 지금 컴포넌트니까요
여기에서는 Me에서 가져오면 되겠네요
Me의 GetActorLocation
이렇게 가져오고요
반지름은 500이라고 넣겠습니다
한 5m정도
그리고 이제 결과를 넣어야 되는데
여기는 랜덤 로케이션을 넣어주겠습니다
그러면 여기에 채워지겠죠
RandomLocation에
이렇게 생긴 함수를 이용해서
이 값을 채울거고요
그러면 이렇게 채워놓고 다시
TickMove 쪽으로 이동해서
이걸 이어서 구현해줄 건데
그러면 여기에서 이렇게 할 거고
여기에서는 이제
일단은 랜덤 위치로 이동하고요
만약에 도착하거나
그 위치에 문제가 있다면
다시 랜덤 위치를 정해야 되잖아요
위치에 문제가 있다면
다시 랜덤 위치를 정하고 싶다
이런 일을 하고 싶어요
그래서 제가 여기 복사해서 가져왔고요
이동을 하는데 결과를 봐야겠죠
도착했는지를 알려면 결과를 알아야 되니까요
결과를 알기 위해서는 이렇게 할 수 있습니다
실제로 MoveToLocation은 반환값이 있어요
그래서 이 반환값을 가지고
결과를 알 수가 있겠죠
auto라고 하고요
이건 patrol이죠
patrolResult
이렇게 받을게요
어 그런데 Set이 빠졌네요 여기 보니까
Set 이렇게 넣어주겠습니다
그 다음에 patrolResult를 받았으니까
이걸 가지고 비교를 해보면 되겠죠
만약에 도착했다면
patrolResult == 해서
이렇게 해서 EPathFollowingRequestResult
여기에 보면 AlreadyAtGoal이라는 게 있습니다
그러면 이게 도착했다면이라는 거고
혹은 그 위치에 문제가 있다면
둘 중에 하나라도 참이라면 이 일을 하겠다는 거예요
랜덤한 위치를 정하는 일을
그래서 이렇게 구성을 할 거고요
여기다가 or 해가지고
한 가지 더 조건을 달게요
이 조건은 Failed라면
실패했다면, 실패했어도 뭔가 못 가잖아요
그러니까 더 이상 갈 수 없는 상황이라면
도착했거나 문제가 생겨서
갈 수가 없는 상황이라면
다시 랜덤한 위치로 목적지를 정하겠다
이런 뜻이 됩니다
이렇게 해서 한번 빌드를 해볼게요
실패를 했는데요
지금 이걸 사용하려면
헤더를 추가를 해야 될 것 같습니다
그래서 이쪽으로 와서
헤더를 추가하겠습니다
추가를 하고 빌드를 다시 해볼게요
네 이제 성공했죠
이런 구성을 하면 되고요
언리얼을 실행해서 잘 동작하는지 볼게요
여기 키보드에서 실행 중에는
Enter 왼쪽에 있는 따옴표 있죠?
그걸 누르면 이렇게 나오거든요
저기 파란색으로 보이는 부분이
실제로 길 영역이라고 보시면 됩니다
에너미가 움직이면서
저 길 영역 안에 랜덤한 위치 하나를 잡는 거예요
그리고 제가 가까이 가면
이제 제가 길 위에 있으니까 저한테 오죠
그리고 도망을 가볼게요
도망을 이렇게 가면
거리가 멀어지면 이제 저한테 오지 않고
다시 랜덤한 위치를 찍어서
그쪽으로 이동을 하는 형태가 되는 걸 볼 수 있습니다
도착하고 공격하고 다시 도망가면
이렇게 쫓아오고
혹은 패트롤 하고
이런 식으로 처리가 되고요
문제가 뭐냐면
이런 문제가 있습니다
가까이 오고 있을 때 탕 쏘면
미끄러지면서 저한테 와요 그렇죠?
이거를 좀 해결을 해줄 건데요
지금 총에 맞았다
상태가 있습니다 그렇죠?
그러면 여기에 상태들을 처리하는 곳이 있는데
Damage인 경우 혹은 Die인 경우
그리고 공격하는 중 이런 경우에는
길을 이렇게 Move를 하면 안 되죠
그래서 이 세 경우
Idle도 마찬가지인데요
TickMove일 때만 실제로
AI가 길을 찾는 일을 활성화하고
나머지 상태에서는 멈춰야 될 것 같습니다
그래서 그걸 처리를 해줄 건데
이거 일일이 다 끼워넣으면 좀 너무 애매하고
어디다가 작업할지 어려우니까요
기본적으로 우리가 상태를 전환하는
SetState 함수가 있습니다
여기에서 만약에 newState가
MoveState가 아니라면
AI를 멈추고 싶다
이렇게 한번 처리를 해보죠
그럼 여기에서 간단하게 조건문을 이렇게 걸고요
newState가 Move라면
이게 아니죠
newState가 이렇게 해서
Move가 아니라면이니까
여기에다가 느낌표를 이렇게 넣어주시면 되겠죠
Move가 아니라면 AI를 stop하고 싶어요
자 StopMovement라는 함수가 있습니다
이렇게 멈춰버리고 싶다는 거죠
다시 이것을 저장하고
결과를 볼게요 컴파일하고
가까이 가고요
탕 하면 제자리 멈추죠
다시 오고요
공격하면서도 탕 하면 잘 죽습니다
이번 단원에서 학습한 내용을 정리하도록 하겠습니다
내비게이션 시스템을 이용한 길 찾기 길 찾기 시스템 AIModule, NavigationSystem 모듈을 추가함 레벨에 NavMeshBoundsVolume을 배치함
Brush Setting의 X값과 Y값을 바닥을 다 덮을 수 있게 설정해 길을 만듦 AIController로 에너미가 길을 찾아가게 설정함
Navigaton Invoker를 이용한 길 찾기와 패트롤 기능 길 찾기 정적으로 길을 만들 때 레벨에 배치된 맵의 크기가 너무 크면 성능 이슈가 생김
Navigation Invoker 기능을 이용해 길을 동적으로 생성해 해결함
ProjectSettings의 Navigation 관련 메뉴에서 길을 동적으로 생성하게 하고 Navigation Invoker 기능을 활성화함
Project Settings에서 Navigation Mesh를 Dynamic으로 설정함
Project Settings에서 Navigation System을 Navigation Invoker를 사용함으로 체크함
패트롤 기능 추가하기 UNavigationSystemV1 클래스를 활용함
목적지가 현재 길 위에 있다면 그곳으로 이동하고, 그렇지 않다면 랜덤으로 목적지를 정해서 이동함