2023. 4. 5. 01:09ㆍProjects/Charon
일반적인 게임에서는 플레이어를 방해하는 적(Enemy)들이 존재합니다. 이들은 플레이어를 공격하거나, 플레이어가 게임 진행을 원활하게 진행하는 것을 방해하는 목적을 가지고 있습니다. 이 게임에서도 적들이 존재하며, 각각 어떻게 구현했는지에 대해 얘기하는 시간을 가져보려고 합니다.
1. AI Navigation 기능을 통한 적의 맵 내 이동
기본적으로 적들은 맵 내에서 이동 가능한 지역만을 돌아다녀야 합니다. 이동 불가능한 지역을 뚫고 간다던가, 목표 지점을 앞에 벽이 있는데도 옆으로 돌아가지 않고 계속 벽을 향해 이동한다면 게임 진행이 안 될 것입니다.
또한, 자기가 이동 중인 방향을 바라보며 이동해야 하며, 방향 전환을 위해 회전을 했는데 어설프게 회전하는 것도 게임에 대한 몰입을 깰 것입니다.
이러한 내용들을 구현하기 위해, 기본적으로 맵 내에서 어떤 부분은 이동 가능한 길이고, 어떤 부분은 아니라는 것을 적 AI에게 알려줘야 하는데, 그걸 지원해주는 것이 바로 AI Navigation 기능입니다.
맵 내의 지형 조건에 대해 알려주었다면, 적 AI에 대해 다양한 조건들을 세팅해줘야 했습니다. 목적지 좌표, 회전 속도, 이동 속도 등등의 내용들을 말이죠. 적 AI 오브젝트에 NavMeshAgent 컴포넌트를 부착하고, 필요한 값들을 세팅해주었습니다.
그런데, 한 가지 문제점이 있었습니다. 회전을 다 하지 않았는데 이동을 하거나, 회전속도가 너무 느려 방향 전환이 느린 부분이 문제가 되었습니다. 이 부분은 NavMeshAgent의 자체 회전 기능을 사용하니 어색하였던 것이라, 자체 회전 기능을 끄고 직접 구현해야 했습니다. (구글링을 해보니 좋은 자료가 있었는데, 링크를 못 찾겠네요...)
agent.updateRotation = false;
그리고, 플레이어에게서도 적용했었던 상태 기계(State Machine)을 적 AI에게도 똑같이 만들어 적용해주었습니다.
2. 상태 기계(State Machine) 재사용
1) 자기가 이동 중인 방향을 바라보게 하기
여러 상태에 대해 다 설명하기에는 내용이 많고, 가장 핵심적인 이동에 대해서만 살펴보겠습니다. 즉, 이동 상태 클래스의 내용이지요. 위에서 말했던, 적 AI는 자기가 이동하는 방향을 바라봐야 한다라는 내용을 구현한 것부터 보겠습니다.
desiredVelocity는 일반적인 velocity와 달리 물리적인 속도뿐만 아니라, 앞에 있는 장애물 회피까지 고려한 속도를 의미합니다. 즉, desiredVelocity가 곧 적 AI가 바라볼 방향이 된다는 것이죠.
그 후에는 어려운 게 없었습니다. 방향만 바라보면 되니 y값은 0으로 해주고, 해당 방향으로 회전시켜주면 되었습니다.
그리고 NavMeshAgent Inspector 창에서 회전 속도(Angular Speed)와 가속(Acceleartion)을 120으로 같게 해주니 회전 속도가 빨라 방향 전환이 자연스럽게 이루어지는 것을 볼 수 있었습니다.
2) 적 AI 이동 구현하기
이동 상태에서는 플레이어가 적의 시야 사거리 내에 들어왔는지, 적의 공격 사거리 내에 도착하여 공격으로 전환할 준비가 되었는지, 그게 모두 아니라면 적을 향해 이동할 것인지 등을 구현해야 합니다.
OnUpdateState()는 Update() 함수와 같이 계속 매 프레임마다 실행되는 함수입니다. 적이 탐지되었다면 적을 바라보며 적을 향해 이동하게 되고, 공격 사거리 내에 도착하였다면 공격 모션으로 전환을 하게 됩니다.
물론 위 GIF 시점은 제가 적 AI의 모든 기능을 다 구현하고 난 후 녹화한 시점이라 AI가 공격도 하긴 합니다.
그게 중요한 것이 아니라, 보면 장애물도 피해서 플레이어를 따라다니고, 자기가 이동하는 방향을 바라보면서 자연스럽게 따라오는 것을 볼 수 있습니다. 이런 방식을 통해 확장해나가며, 적 AI의 공격 상태, 스킬이 있다면 스킬 사용 상태 등을 추가할 수 있습니다.
3) 공격 상태 추가하기
AI Navigation과 관련하여 가장 핵심적인 내용이 이동(Move)이었으므로, 해당 내용만 다루고 끝내려고 했는데, 공격 기능까지만 같이 다뤄보려고 합니다.
공격 로직은 매 프레임마다 다음을 체크하여 확인합니다.
- 적은 플레이어가 자신의 유효 공격 사거리 내에 들어왔는지를 검사합니다.
- 공격 중인 상태가 아니고, 적이 사거리 밖에 있다면 다시 이동(Move) 상태로 전환합니다. - 유효 사거리 내에 들어왔다면, 그 순간의 플레이어의 위치를 체크합니다.
- 적과 플레이어와의 각도를 계산하고, 회전 타이머를 초기화하기 위함입니다. - 적과 플레이어와의 각도가 거의 차이가 안 날때까지 해당 각도로 적이 회전합니다.
- 차이를 0으로 설정하면 근접 공격 적이 로직에서 무한 루프를 도므로, 5도 정도로 주었습니다.
- 타이머는 혹시나의 버그로 인해, if문에서 빠져나오지 못할까봐 넣어둔 안전 장치입니다. - 위 로직을 모두 통과했다면, 공격할 수 있는 상태이므로 공격을 해줍니다.
적이 쓰는 무기 타입에 따라 필요한 것들을 만들어주고, 애니메이션을 설정하였습니다. 화살(투사체)를 사용하는 궁병 적의 경우, 화살을 오브젝트 풀링 방식으로 관리하게 만들어줬습니다. 무기 타입은 다 다르지만, 위에서 설명한 로직은 다 동일하게 작용합니다.
보병, 궁병, 장군(창병)의 경우에는 이동, 공격, 죽음 세 가지 상태밖에 없기에 무기와 애니메이션만 교체해주면 되기에 편합니다. 하지만, 진광(보스몹)의 경우에는 일반 몹들과 다른 매커니즘을 가지고 있습니다.
거창하게 구현한 건 없지만, 다음 글에서 진광(보스몹)을 어떻게 구현했는지에 대해 다뤄보겠습니다.
감사합니다.
'Projects > Charon' 카테고리의 다른 글
[Charon] #Final. 간단한 보스몹 만들기 + 카메라 셰이킹(with 시네머신) (3) | 2023.04.05 |
---|---|
[Charon] #10. "카론의 노" 무기 공격 및 스킬 구현하기 (9) | 2023.04.04 |
[Charon] #9. 대시(Dash) 선입력(Input Buffer) 기능 추가하기 (0) | 2022.11.16 |
[Charon] #8. 플레이어와 카메라 사이의 오브젝트 투명화하기 (6) | 2022.11.16 |
[Charon] #7. 무기 기본 3타 콤보 공격 구현하기 (긴 글 주의) (0) | 2022.10.06 |