주의! 글 내에 gif 파일이 많으니 데이터로 보시는 분들은 조심해주세요!
주의! 본 정리 글은 '클래스 101'의 '언리얼 엔진 중급, 영화 퀄리티의 3D 메타버스를 창조해요.' 강의를 수강하고 정리한 글입니다. 해당 글은 아래 원칙 하에 작성되었습니다.
1. 강의에서 다룬 내용 기재를 최소화하고, 강의 흐름에 맞게 개인적으로 학습하는 과정을 기록한다.
2. 강의에서 제공하는 자료를 첨부하지 않는다.
3. 내용을 보충할 자료가 필요한 경우, 직접 제작한 자료를 사용한다.
(2023.06.01 추가)
클래스 101 문의 결과 저작권 문제없다는 답변받았습니다.
(2023.06.02 추가)
언리얼 엔진 중급, 영화 퀄리티의 3D 메타버스를 창조해요. | RYU Russell 러셀
언리얼 엔진 5 가 정식 출시되었습니다! 언리얼 엔진 5는 기존 언리얼 엔진 사용자들을 위해 언리얼 엔진 4와 호환되도록 제작되었으며, 언리얼 엔진 4 사용자라면 쉽게 즉시 언리얼 엔진 5를 사
class101.net
들어가며
오늘 정리할 내용은 'Chapter 5. 캐릭터, 상호작용, 그리고 인공지능'이다.
지금 시점(언리얼 5 출시 이후)에서 러셀님 강의를 들으면 강의도 중요하지만 직접 찾아보며 알아가야 하는 부분이 많다. 러셀님이 강의를 하는 환경은 언리얼 4이고, 내가 직접 해보는 건 언리얼 5이기 때문인데 이로 인해 이제까지 강의를 따라 진행해보며 여러 가지 이슈가 있었고, 글로 정리할 때는 이런 이슈들을 해결하는 과정까지 함께 담았었다.
이번 강의를 들으며 이전과 마찬가지로 여러 이슈들이 생겼는데, 이번은 이런 이슈가 유독 많았다. 언리얼 5로 넘어오면서 애니메이션 리타겟팅을 진행하는 구조가 바뀌어 생기는 이슈라던가, Navmesh를 통해 AI를 제어하던 중 애니메이션이 재생되지 않는 이슈 등 내게는 어려운 이슈들이 많았다.
심지어 작성일 기준(22년 08월)으로 언리얼 5가 나온 지 몇 개월이 되지 않았다 보니 이슈를 해결하기 위한 자료도 부족해서 고생을 많이 했다. 그래서 이번 글은 '나를 위해서'라는 목적도 있지만, '나와 같은 공부를 하는 다른 이'들에 초점을 맞춰서 조금 더 정리를 해보려고 한다.
글을 보는 여러분이 나와 같은 시행착오를 겪지 않기를 바란다. 물론 직접 겪으면서 해결하는 게 좋긴 하겠지만 정리된 내용을 보고 시간을 아껴서 그 시간에 다른 것들을 공부하는 게 더 좋지 않을까? 모두 이 글을 보고 제 시간을 가져가십셔.. 나의 피, 땀, 그리고 눈물..
따라서 이번 글을 통해 정리할 내용은 아래와 같다.
- Animation Retargeting
- 개요 : Unreal 5의 animation retargeting의 정의와 작업 파이프 라인
- Unreal의 애니메이션 제어 방식
- Mixamo에서 캐릭터 모델 다운로드하기
- 캐릭터 IK Rig Retargeting하기
- Playable 캐릭터에 적용하기
- Character Skill 구현
- 개요 : Character Skill 구현 파이프 라인과 결과물
- Mixamo에서 캐릭터 모델 다운로드하기
- Animation Montage와 Animation Notify
- 이펙트 적용
- Character Skill 블루 프린트 구현 시 사용한 기능 소개
- Character Skill 블루 프린트 구현 시 겪었던 이슈 소개
- 적 AI 구현
- 개요 : AI 구현 파이프 라인과 결과물
- Navigation Mesh를 통해 path 찾기
- 에셋 생성 및 연결 과정
- Event Dispatcher로 skill 연결하기
- AI 애니메이션 고정 이슈 소개
- UI를 통해 Kill과 Respawn 알리기
- Widget Blueprint로 UI 만들기
Animation Retargeting
개요 : Unreal 5의 animation retargeting의 정의와 작업 파이프 라인
먼저 animation retargeting 과정에 대해 하나하나 소개하기 전에 animation retargeting의 정의와 작업 파이프 라인에 대해 한 번 정리하고 가도록 하자.
Animation retargeting이란 뭘까? Animation retargeting이란 같은 스켈레톤 구조의 캐릭터들 사이에 애니메이션을 재사용하기 위해 스켈레톤의 비율이나 구성을 맞춰주는 작업을 말한다. 여기서 스켈레톤이란 우리가 흔히 말하는 본(bone)이라고 생각하면 된다.
이 과정을 통해 각각의 캐릭터 모델링에 맞는 애니메이션을 따로 제작할 필요 없이 하나의 본 구조에 맞는 애니메이션을 제작함으로써 이를 같은 스켈레톤 구조를 공유하는 캐릭터들의 애니메이션으로 활용할 수 있게 된다.
더 상세한 내용은 아래의 언리얼 엔진 공식 문서를 살펴보자.
그럼 animation retargeting 작업의 파이프 라인을 간단하게 확인하고 다음 항목으로 넘어가자.
Unreal의 애니메이션 제어 방식
언리얼에서는 animation blueprint를 통해 애니메이션을 제어한다. 유저는 animation blueprint 내에서 다양한 state를 만들 수 있고, 이 state들을 전환하는 조건들 또한 자유롭게 설정하여 상황에 따라 원하는 애니메이션이 재생되도록 구성할 수 있다.
이번 글을 이해하기 위해서 '언리얼에서 애니메이션은 이런 느낌으로 제어된다.' 정도만 이해하면 될 것 같다. 더 자세한 내용은 언리얼 공식 문서의 '애니메이션 블루프린트'와 '애니메이션 블루프린트 에디터'를 참고하자.
Mixamo에서 캐릭터 모델 다운로드하기
Mixamo는 Adobe에서 지원하는 리깅된 캐릭터 에셋 및 애니메이션 사이트이다. 리깅이란 캐릭터 모델링에 스켈레톤 메시, 즉 본을 심는 작업을 말하는데 Mixamo에서는 이런 리깅 작업이 완료된 캐릭터와 이를 이용한 애니메이션을 무료로 다운로드할 수 있다. 이번 강의에서 사용하는 캐릭터와 애니메이션도 Mixamo에서 다운로드하여 진행했다.
여기서 왼쪽 상단의 Characters로 가면 다양한 캐릭터 에셋을 확인할 수 있다. 나는 이곳에서 사용할 캐릭터를 고를 때 내가 리타겟팅 작업이 처음이라는 점을 고려하여 최대한 사람과 가까운 캐릭터를 선택했다. 왼쪽 아래와 같은 설정으로 fbx 파일로 다운로드할 수 있고, 이를 언리얼로 가져오면 오른쪽 아래와 같이 몇 개의 파일이 추가된다.
이렇게 Mixamo에서 캐릭터 모델을 다운로드해와서 import를 진행해봤다.
캐릭터 IK Rig Retargeting하기
IK Rig이란 Inverse kinematics rig의 약자로 간단히 말해서 스켈레톤 구조(인체 관절 구조)에 따라서 캐릭터 모델의 움직임을 자연스럽게 구성하는 것을 말한다.
우리가 가져온 캐릭터에 기존에 있던 애니메이션을 적용하기 위해서는 이 스켈레톤 구조의 비율과 구성을 맞춰줄 필요가 있는데 이 작업을 retargeting이라고 한다.
우선 mixamo에서 가져온 캐릭터 arissa의 스켈레톤 구조를 확인해보자. 이는 arissa의 fbx 파일을 import 할 때 생긴 skeleton 파일에서 확인할 수 있다.
위의 이미지처럼 Show Retargeting Options를 활성화하면 Translation Retargeting Option을 확인할 수 있는데 여기서 Recursively Set Tranlation Retargeting Skeleton을 통해 모든 옵션을 skeleton으로 설정한다.
이 작업은 해당 옵션이 skeleton으로 설정된 본을 IK 알고리즘에 의해 skeleton 기반으로 자연스럽게 움직이는 것처럼 보이게 하겠다는 것이다. 여기서 pelvis, 즉 골반에 해당하는 부분은 AnimationScaled로 설정하는데 골반 부분은 일반적으로 root가 되기에 이렇게 설정하는 것으로 보인다.
자세한 내용은 리타겟팅에 대해 알아볼 때 참고한 '데브 지노 블로그'와 언리얼 공식 문서 '리타기팅된 애니메이션 사용하기'를 확인하자. 언리얼 문서 같은 경우는 언리얼 4에서 가져온 문서이기에 문서 내의 '다른 스켈레톤을 사용한 리타기팅'부터는 언리얼 5와 맞지 않을 수 있다.
이렇게 설정한 skeleton은 저장한 뒤 새로운 IK Rig 파일을 만들어 할당한다. 이에 더불어 source가 될 skeleton도 새로 만든 IK Rig 파일에 할당한다. 그럼 결과는 아래와 같다. 내 경우는 IKRig_Arissa가 리타겟팅할 target이고, IKRig_Quin이 리타겟팅의 기준이 될 source이다.
그럼 target과 source의 스켈레톤 구조를 비교해보자. 확인해보면 아래와 같이 스켈레톤 계층 구조도 다르고 구성도 다른 걸 확인할 수 있다.
여기서 스켈레톤의 구조와 구성이 다르다면 서로 맞는 부위끼리 연결해 줄 필요가 있는데 이때 사용하는 것이 retarget chain이다. 위 이미지의 우측을 보면 Add New Chain이라는 버튼이 있는데 이걸로 retarget chain을 추가하고, 맞는 부위끼리 연결해주어 다른 스켈레톤 구조라도 대강 맞도록 설정할 수 있다. 이에 대한 더 자세한 내용은 언리얼 공식 문서의 'IK Rig 리타기팅'에서 확인할 수 있다.
이 외에 root가 없다면 왼쪽 아래처럼 루트를 설정하고 완료했으면, IK Retargeter 파일을 만들어 source IK Rig 파일을 선택한다.
IK Retargeter를 연 후, target IK Rig 파일을 할당한 뒤 애니메이션을 재생해보면 아래처럼 target과 source의 애니메이션이 다른 것을 확인할 수 있다.
찾아보니까 이 문제는 포즈의 차이 때문에 발생한 문제였는데 target을 mannequin과 같이 A-pose로 조정해서 다시 테스트해봤다.
포즈를 수정하니 팔이 정상 위치로 가서 확실히 괜찮은 것을 확인할 수 있다. 다만 손가락은 retarget chain으로 연결되지 않아서 아직까지 다소 부자연스러운 느낌이 남아있었다. 따라서 아래와 같이 손가락 하나하나 chain을 만들어 연결해주었고, 손가락까지 잘 적용이 된 걸 확인할 수 있었다. 손가락은 계층 구조가 따로 나뉘어있다 보니 손가락 별로 chain을 따로 만들어줘야 했다.
이렇게 리타겟팅하여 적용한 애니메이션은 Export Selected Animations 버튼으로 따로 저장할 수 있다.
Playable 캐릭터에 적용하기
그럼 이렇게 리타겟팅한 결과물을 실제 플레이 가능한 캐릭터로 만들어보자.
언리얼에서 기본적인 playable 캐릭터에 대한 블루 프린트는 BP_ThirdPersonCharacter이다. 해당 블루 프린트를 열면 기본 캐릭터인 mannequin이 보이는데 왼쪽 components 창에서 mesh를 선택한 뒤, 우측 detail 패널의 skeletal mesh에 우리가 import 한 target의 skeletal mesh를 할당한다.
이렇게 실행해보면 아래 gif처럼 캐릭터가 T-pose 상태에서 애니메이션 없이 움직이게 된다. 그럼 우리가 만든 애니메이션은 어떻게 할당해야 될까? 이 부분을 정말 많이 헤맸는데 결론부터 말하자면 source의 animation blueprint를 우클릭한 뒤 retarget animation assets - duplicate and retarget animation blueprint를 통해 만들어 둔 IK retargeter로 target character를 위한 animation blueprint를 따로 만들어줘야 했다.
이렇게 만들어진 결과물은 아래와 같다. IK Retargeter에서 작업할 때 따로 animation을 export 했었는데, export 하지 않아도 알아서 만들어졌다.
여기서 animation blueprint가 2개 만들어진 이유는 source animation blueprint가 manny 캐릭터의 animation blueprint를 child라 자동으로 manny까지 만들어진 것 같다. 아무튼 이 animation blueprint를 BP_ThirdPersonCharacter의 animation class에 할당한다.
위처럼 animation blueprint를 만들고 할당하는 건 'Unreal Engine 5: How To Change & Use Animation Assets - Switch From Walk/Run Animations' 영상을 참고했다.
그런데 이렇게 만든 애니메이션에 또 문제가 생겼다. 바로 root로 설정한 hips 부분이 고정되어 움직이지 않아서 애니메이션이 부자연스럽게 재생되는 문제였는데, 이것 저것 찾아보니 root motion이라는 문제인 것 같았다.
Root motion이란 캐릭터의 애니메이션을 계산할 때 root bone을 기준으로 계산하는 것을 말하는데 이것이 활성화되어 있으면 root bone이 고정이 된다고 이해했다. 현재 캐릭터의 애니메이션을 생각했을 때 큰 움직임이 있는 애니메이션은 없기에 root motion을 비활성화해도 될 것 같았다. 따라서 root motion을 비활성화했고 결과적으로 애니메이션이 정상적으로 잘 재생되는 것을 확인할 수 있었다.
Root motion에 대한 더 자세한 설명을 언리얼 공식 문서의 '루트 모션'을 참고하자.
Character Skill 구현
개요 : Character Skill 구현 파이프 라인과 결과물
Character skill 구현 같은 경우는 간단하게 정리를 하면 아래 이미지와 같다. 애니메이션을 import 하고, animation montage 편집을 통해 animation notify를 추가한 뒤, 블루 프린트로 기능과 연결하면 된다.
이 외에는 대부분 블루 프린트로 원하는 기능을 구현하는 부분이라 따로 설명은 하지 않을 예정이다. 따라서 이번 글에서 다루는 내용은 mixamo에서 애니메이션 다운로드하기, animation montage와 animation notify, 이펙트 적용, character skill 블루 프린트 구현 시 사용한 기능 소개, character skill 블루 프린트 구현 시 겪었던 이슈 소개이다.
만들어 본 스킬 블루 프린트와 테스트 gif를 첨부하고 다음 항목으로 넘어가도록 하겠다.
Mixamo에서 캐릭터 모델 다운로드하기
Mixamo에서는 캐릭터 모델링뿐만 아니라 캐릭터 애니메이션도 다운로드할 수 있다. 원하는 애니메이션을 찾고 왼쪽 아래와 같은 옵션으로 다운로드를 진행한다. 그런 다음 fbx 파일을 언리얼 content browser로 import 하면 오른쪽 아래와 같은 창이 나온다. 여기서 그냥 Import All을 누르면 된다.
나 같은 경우는 bone transform이 다르다는 경고가 계속 나왔는데 이렇게 import 된 애니메이션은 재생이 되지 않았다. 이것저것 찾아보면서 transform 위치도 맞게 조정해봤는데도 해결되지 않았다. 알고 보니까 캐릭터 모델을 다른 걸 선택한 채로 애니메이션을 재생해서 생기는 문제였는데 나는 다운로드 설정 중 without skin이 있어서 어떤 모델을 선택해도 애니메이션 데이터만 다운로드되는 줄 알았다.
아무튼 캐릭터 모델을 잘 맞춰서 다시 다운로드를 진행했고 성공적으로 import를 할 수 있었다.
Animation Montage와 Animation Notify
Mixamo에서 애니메이션을 import 하면 Animation Sequence라는 파일이 생성된다. 이 파일을 우클릭해서 아래와 같이 AnimMontage 파일을 생성해준다.
이렇게 만들어진 파일을 열어보면 아래와 같은 창이 나오는데 이 창이 animation montage 편집 창이다. 편집 창에서 아래 슬라이드를 드래그하여 애니메이션의 원하는 프레임을 볼 수 있으며, 해당 프레임에 특정 이벤트를 재생하고 싶은 경우 animation notify를 추가하여 이벤트 재생을 위한 마크를 남겨둘 수 있다.
Animation을 제어하기 위한 notify는 new notify를 통해 만드는 게 아닌 검색 부분 아래의 montage notify를 통해 만들어야 한다. 그리고 이를 마크로 사용하기 위해서는 우측 아래의 이미지처럼 이름을 설정해주어야 한다.
이런 개념들에 대한 더 자세한 설명은 언리얼 엔진 공식 문서를 확인하자
Montage를 통해 애니메이션을 제어할 수 있는 이유는 animation blueprint의 slot 덕분인데, 이 slot을 통해 다른 애니메이션이 재생 중이더라도 중간에 자연스럽게 애니메이션을 삽입할 수 있다고 한다.
더 자세한 내용은 언리얼 엔진 공식 문서의 '애니메이션 슬롯'을 참고하자.
이렇게 만든 montage와 notify는 블루 프린트 내에서 Play Montage라는 노드를 통해 제어되며, Notify Name 핀을 통해 이름에 따라 다른 기능을 하도록 구성할 수 있다.
Play Montage 노드의 더 자세한 활용 방안은 언리얼 엔진 공식 문서의 '애니메이션 몽타주 사용법'에서 확인할 수 있다.
이펙트 적용
언리얼 엔진에서는 기본적으로 설치가 되어 있는 이펙트들이 몇 가지 있다. 이번 강의에서는 이 이펙트들을 이용하여 스킬 효과를 구현했는데, 블루 프린트 내에서 spawn emitter at location 노드를 이용하여 스킬이 나오거나 피격받는 위치에 이펙트를 재생했다. 해당 노드에 대한 자세한 설명은 언리얼 공식 문서의 'Spawn Emitter at Location'을 참고하자.
이 외에도 간단하게 이펙트의 색을 변경해봤는데 이를 통해 다음 챕터에서 다룰 나이아가라 파티클 시스템에 대해 간략하게나마 알아볼 수 있었다.
이렇게 변경된 이펙트 효과는 아래와 같다.
Character Skill 블루 프린트 구현 시 사용한 기능 소개
Character skill을 블루 프린트로 구현하며 다양한 기능들을 사용해봤다. 그중 macro와 arrow, bind event to on take any damage라는 것을 소개해보려고 한다.
우선 macro는 여러 개의 노드를 하나로 묶어 관리할 수 있는 기능을 말한다. 예를 들면, 블루 프린트로 개발을 할 때 특정 노드 뭉치가 반복되는 경향이 있는데 이때 이 모든 것을 복사해서 붙여놓기보다는 하나의 macro로 만들어 관리하면 조금 더 직관적으로 개발할 수 있다.
아래의 이미지처럼 macro로 묶을 수 있고, 입력과 출력도 지정할 수 있어서 복잡한 블루 프린트를 정리할 때 효과적으로 사용할 수 있어 보였다. 개발할 때 함수처럼 동작한다고 생각해도 될 것 같다. 더 자세한 내용은 언리얼 엔진 공식 문서의 '매크로'를 참고하자.
다음으로 arrow이다. arrow는 선으로 방향을 표시하는 컴포넌트인데 이번 강의에서는 마법이 발사될 위치로 사용되었다. 해당 컴포넌트를 활용할 때는 tranform을 읽어와서 arrow의 위치에서 arrow가 향하는 방향으로 투사체가 나가도록 구현하였다. 더 자세한 내용은 언리얼 공식 문서의 '유틸리티 컴포넌트'를 참고하자.
마지막으로 bind event to on take any damage 노드는 피격 처리를 담당하는 blueprint component에서 사용하는 노드이다. 이 노드를 사용하면 blueprint component가 부착된 오브젝트의 event any damage 노드 역할을 자식인 blueprint component 내에서 제어할 수 있게 된다.
이 기능은 후술 할 event dispatcher와 비슷해 보이는데 일단 단순하게 부모 오브젝트에서 처리해야 할 이벤트를 자식 컴포넌트가 처리할 수 있게 event를 묶는다고 생각하면 될 것 같다. event를 묶으면 부모 오브젝트에서 해당 이벤트가 발생했을 때 묶인 노드가 호출되므로 부모 노드에 기능을 명시하지 않아도 자식 컴포넌트를 통해 제어를 할 수 있는 것이다.
이를 통해 여러 곳에 다양하게 재사용될 수 있는 blueprint component 내에서 한 번에 처리하여 조금 더 간단하게 구현할 수 있었다. 더 자세한 내용은 언리얼 공식 문서의 'On Take Any Damage'와 '이벤트'를 참고하자.
Character Skill 블루 프린트 구현 시 겪었던 이슈 소개
Character skill을 구현하면서 겪었던 이슈는 크게 3가지가 있는데 이번 항목을 통해서 소개해보도록 하겠다. (투사체 크기-루트, 피격 처리-콜리전, 이펙트 루프)
첫 번째로 스킬 사용 시 생성되는 투사체의 크기가 너무 크게 나오는 이슈이다. 나는 투사체의 scale을 조정하여 spawn 하도록 구성했는데 이때 투사체가 arrow의 tranform을 기준으로 생성되다 보니 scale까지 arrow의 scale로 변경되어 크게 나오는 것 같았다. 그래서 핀 분할을 해서 scale을 제외하고 받아오도록 했는데 해결이 되지 않았다.
그러다 문득 강의에서 오브젝트를 가져오는 경우 root component를 참조하기 때문에 root component를 잘 설정해줘야 한다는 내용이 있었다는 게 생각났다.
따라서 sphere가 아니라 sphere collision을 root로 삼아서 실행해봤는데 정상적인 크기로 나오는 것을 확인할 수 있었다. sphere collision은 scale이 1로 고정이었고 radius로 collision의 크기를 조절했는데, root component의 scale을 따로 조정하지 않아서 그런지 잘 동작하였다.
하위에 있는 sphere의 scale은 0.25인데 방금 1로 지정해서 테스트를 한 번 해보고, 원래대로 되돌린 이후 다른 오브젝트들의 부모인 sphere collision의 scale을 0.25로 지정해서 테스트를 해 본 결과, sphere collision이 root component일 경우에 sphere 크기를 조정하면 정상적으로 동작하고, sphere collision의 경우는 무시되었다.
아마도 오브젝트를 spawn 할 경우, root component의 scale이 1로 오버 라이딩되는 것 같았다. 이렇게 오브젝트 생성 시 크기가 달라지는 이슈를 해결했다. 이것과는 별개의 이야기지만 아래 이미지의 collision 설정에 대한 자세한 내용은 언리얼 공식 문서의 '콜리전 개요'를 참고하자.
두 번째로 피격 처리가 이상하게 되는 이슈이다. 투사체에 의한 데미지 테스트를 진행했을 때 데미지가 적용이 될 때도 있고, 안 될 때도 있었는데 이 부분은 생각보다 쉽게 해결했다.
문제는 남아있는 투사체의 콜리전 때문이었는데 강의에서 데미지는 콜리전에 의해 처리가 되도록 구현하였고, 충돌을 한 투사체는 비활성화됐다가 일정 시간 이후 destroy되게 구성했는데, 비활성화가 되더라도 충돌 판정을 하는 콜리전이 남아있어서 생기는 문제였다.
투사체의 콜리전과 마찬가지로 충돌 판정 콜리전 또한 disable되도록 설정하여 해결했다. 문제를 보자마자 '어..? 이거 콜리전처리 제대로 안 됐나?' 싶어서 확인해봤는데 진짜 이게 문제여서 행복했다! 문제가 생기고, 안 생기고는 중요하지 않다.. 제대로 된 이유를 아느냐 모르느냐가 중요하지..
마지막은 이펙트 처리 이슈이다. Spawn emitter at location 노드를 이용해 이펙트를 생성했을 때 특정 이펙트가 잔존해있는 이슈가 생겼는데 이것도 찾아보니까 단순하게 이펙트 속성에 의한 문제였다.
이펙트에는 looping 이라는 속성이 있는데 이게 true인 이펙트를 사용하면 계속해서 남아있게 되는 것이었다. 찾아보니까 나이아가라 파티클 시스템으로 이 looping을 조정할 수 있었는데 일단은 looping이 false인 이펙트를 사용하여 해결했다.
적 AI 구현하기
개요 : AI 구현 파이프 라인과 결과물
언리얼 엔진에서 AI를 구현할 때는 behavior tree를 이용한다. behavior tree는 black board와 함께 활용되는데 black board를 통해 behavior tree의 어떤 branch로 이동할지 결정된다.
이렇게 만든 behavior tree와 black board는 AI controller라는 blueprint class를 만들어 연결해준 뒤 이를 AI 캐릭터 blueprint class에 연결해주어 동작시킬 수 있다.
결과물은 아래 gif에서 확인할 수 있다.
Navigation Mesh를 통해 path 찾기
Navigation Mesh란 AI가 이동할 수 있는 영역에서 목적지까지의 경로를 계산하기 위해 사용하는 메시이다. 아래와 같이 NavMeshBoundsVolume actor를 배치하여 사용할 수 있으며 키보드 P를 눌러 시각화를 할 수 있다. 간단하게 경로를 찾기 위해서 배치하는 액터라고 생각하면 될 것 같다. 더 자세한 내용은 언리얼 공식 문서의 '기본 네비게이션'과 '네비게이션 메시 수정하기'를 참고하자.
에셋 생성 및 연결 과정
먼저 behavior tree를 생성한다. 생성된 behavior tree 에셋을 보면 상단에 New Blackboard라는 버튼이 보이는데 해당 버튼을 눌러 blackboard 또한 만들어준다. 그럼 blackboard의 키 목록이 나오는데 이번 강의에서는 유저 캐릭터를 추적할 예정이기에 TargetPlayer라는 object 키를 만들어줬다.
Blackboard 키까지 만들었다면 AIcontroller의 child class로 새로운 블루 프린트를 만들어준다. 여기서 behavior tree와 blackboard를 가져와 TargetPlayer 키에 유저 캐릭터를 연결한다.
강의에서는 유저 캐릭터와 같은 기능을 하는 적 AI를 만들었기에 ThirdPersonCharacter 블루 프린트 클래스를 복사해서 가져온 뒤 pawn의 AI controller class를 통해 만들었던 AI controller blueprint class를 연결해준다.
위 과정을 거치면 기본적인 연결은 끝났다. 이제 behavior tree를 구성할 차례인데 아래와 같이 task를 만들고 해당 task의 블루 프린트에서 조건에 맞게 이벤트를 생성한 뒤 기능을 구현하면 된다. 조건에 맞는 이벤트를 비롯한 behavior tree 구성 과정에 대해 더 자세히 알고 싶다면 언리얼 공식 문서의 '비헤이비어 트리 퀵 스타트 가이드'를 참고하자.
이때 주의할 점은 task가 끝나는 시점을 명시해야 된다는 것이다. 블루 프린트에서는 finish execute 노드로 이를 알렸는데, 끝났다는 걸 명시하지 않으면 behavior tree에서 task를 전환할 시점을 알지 못해 AI가 멈추거나 순서가 꼬이게 된다. 이렇게 구성한 behavior tree는 아래와 같다.
추가로 유저 캐릭터와 적 캐릭터를 구분하기 위해 material texture를 수정해줬는데, 방법은 bmp 확장자로 export 한 뒤 포토샵과 같은 편집 프로그램으로 수정하고 다시 migrate 하면 된다.
Event Dispatcher로 skill 연결하기
Event dispatcher란 custom event와 마찬가지로 서로 다른 블루 프린트 간의 연결을 위해 사용되지만 custom event 와는 달리 특정 시점에 호출이 가능한 기능이다.
예를 들어, 만들어 둔 behavior tree에 BTTask_Attack이라는 task를 만들어 조건이 성립하면 실행할 수 있게 만들었는데 이 BTTask_Attack을 보면 아래와 같이 구성되어있다.
여기서 'bind event to ~'와 여기에 연결된 이벤트들이 event dispatcher를 통해 특정 시점에 기능이 동작하도록 구현한 거라고 보면 된다. 이 기능을 사용하기 위해서 만들어 둔 AI 캐릭터 블루 프린트의 스킬 매크로를 아래와 같이 구성했는데 'Call ~' 노드에서 해당 event dispatcher를 호출하고 실행한다고 생각하면 된다.
이미지로 예를 들어보면 montage의 attack notify 시점에 도달했을 때 call kick timing이라는 event dispatcher가 호출되고 위 이미지의 bind event to kick timing과 연결된 이벤트가 실행이 되는 것이다.
Event dispatcher를 통해 킬과 리스폰도 구현을 했다. 피격 처리를 담당하는 DamageInteraction에 피격 효과를 정의하고, event dispatcher와 연결하여 호출될 경우, 호출한 캐릭터가 입력을 못 받고 이동이 안 되게 구현했다. Set movement mode의 falling은 리스폰 시 중력을 적용하겠다는 설정인데 자세한 내용은 언리얼 API 공식 문서의 'MovementMode'를 참고하자.
이번 강의에서는 이러한 기능을 통해 캐릭터의 스킬을 구현했다. event dispatcher에 대한 더 자세한 설명은 언리얼 공식 문서의 '이벤트 디스패처'를 참고하자.
AI 애니메이션 고정 이슈 소개
이렇게 적 AI를 구현해봤는데 예상과는 다르게 바로 동작하지는 않았다. 구현한 적 AI의 애니메이션이 Idle 상태로 고정되는 이슈가 발생했는데 이때 여러 이슈에 반쯤 멘탈이 나갔었다.
그래도 이것저것 찾아보던 중 'LeafBranchGames'라는 유튜브 채널의 'Fix AI animation idle sliding - Unreal Engine 5' 영상을 보고 해결할 수 있었는데, 확인해보니까 nav mesh로 경로를 탐색할 때 CharacterMovement의 Use Acceleration for Paths 옵션이 활성화되어 있지 않으면 이동할 때 가속이 적용되지 않아, 가속 값을 조건으로 상태를 전환하는 애니메이션이 제대로 동작하지 않았던 것 같았다.
대략적으로 원리는 이해했는데 별 다른 자료를 찾아볼 수가 없어서 참고할만한 키워드나 문서가 있는지 질문을 했는데 자세한 문서는 없다는 답변만 돌아왔다. 그냥 부딪혀보면서 깨지고 부서지는 수밖에 없는 것 같다.. 아무튼 이렇게 해결을 해서 정상적으로 테스트할 수 있었다.
참고 문헌은 아래와 같다. 별 다른 내용은 없는데 대충 읽어보면 무슨 느낌인지 감은 올 것 같다.
- Unreal forum - Using Navmesh for Player movements
- Unreal forum - Velocity or InputVector in PawnMovementComponent?
- 언리얼 엔진 API 공식 문서 'bRequestedMoveUseAcceleration'
UI를 통해 Kill과 Respawn 알리기
Widget Blueprint로 UI 만들기
Widget blueprint란 언리얼 엔진에서 UI 다루기 위해 사용하는 블루 프린트이다. 이번 강의에서는 이를 이용하여 죽었을 때 UI를 띄우고, UI에는 리스폰까지 남은 시간을 띄워주도록 구현하였다.
우선 왼쪽 아래와 같이 widget blueprint를 생성하고 원하는 UI를 구성한다. 이때 외부에서 폰트를 가져와서 적용할 수도 있다.
UI를 배치하면서 주의할 점은 Canvas Panel을 우선 배치하고 그 하위에 다른 요소들을 배치해야 한다는 것이다. widget blueprint에서 각 요소가 가질 수 있는 child의 수에는 제한이 있기 때문에 canvas panel부터 배치를 해야 한다.
이렇게 UI를 구성했으면 리스폰까지 남은 시간을 제어하기 위해 블루 프린트 작업을 진행한다. 이는 create binding을 통해 해당 요소를 제어할 event graph를 열 수 있다.
제어하기 위해 가져와야 하는 respawn duration의 경우, get player character로 캐릭터를 불러와서 get component by class로 값을 가져왔다. 유니티의 get component와 비슷해 보인다. 이를 set timer by event를 통해 초마다 1씩 감소되도록 구현했다.
UI에는 애니메이션도 추가할 수 있는데 하단의 +animation 버튼을 클릭하여 애니메이션을 추가하고, 원하는 UI에 효과를 넣어 애니메이션을 만들 수 있다. 이렇게 만든 애니메이션은 play animation 노드로 재생할 수 있다.
이렇게 만든 UI는 아래 gif와 같다. 좌우가 조금 잘리게 나와서 해상도 관련해서 이것저것 알아봤는데 결론은 해상도를 맞춰도 안 되길래 그냥 좌우를 늘려서 해결했다. 조금 더 알아봤어야 했는데 피곤했다. 지금도 피곤하다 ㅋㅋㅋ
이번 항목에서는 위와 같이 widget blueprint를 이용하여 UI를 구성하는 방법을 소개해봤다.
후기
이번 강의를 듣고 직접 해보면서 얻은 것들이 정말 많다. 구글링 능력과 이슈 해결에 대한 의지를 키울 수 있었다는 게 가장 크고, 이슈를 해결하면서 언리얼 4와 언리얼 5의 차이를 살펴보거나 원리를 파악하는 과정에서 엔진의 구조를 일부분이나마 이해할 수 있었다는 게 좋은 경험이었다. 이슈를 해결하면서 코드도 약간 살펴봤는데 언젠가 한 번 언리얼 엔진 오픈 소스에 기여를 해보고 싶다고 생각했다.
글을 정리하는 과정은 너무 힘들었는데 그 이유는 3가지로 정리할 수 있을 것 같다.
첫 번째로 양이 방대하다 보니 무엇을, 얼마나, 어떻게 설명을 할지가 너무 막막했다. 중간중간 캡처한 이미지와 영상만 수백에 블루 프린트 구성을 하나하나 설명하기에는 시간이 너무 오래 걸리고 불필요할 것 같아서 글을 어떻게 작성해야 할지 감이 잘 안 왔다.
두 번째로 반쯤 작성했을 때 글이 날아가버렸다. 글을 어떻게 작성해야 할지 막막할 때 일단 시작을 해야 완성을 하니 무엇이든 적어보고 수정을 하자고 생각하며 글을 작성했는데 중간에 글이 날아가버렸다. 나는 이 글이 어떻게 보일지 궁금해서 미리보기를 하고 뒤로 돌아왔던 건데 미리보기에 대한 경고 창인 줄 알고 확인을 눌렀다가 글이 날아가버렸다.
이때는 정말 힘들어서 무리를 하며 완성하기보다는 머리 좀 쉬고, 글의 구성을 생각해보자는 의도로 예정되어 있던 휴가를 떠났다.
마지막으로 늘 있던 문제인 어떻게 서술할 것인가에 대한 문제 때문에 글을 작성하는 내내 찜찜했다. 대충 정리를 하자면 기술이나 어떤 객체에 대한 명칭이면 영어를 쓰고, 서술을 할 때는 한글로 쓰고자 했으며, 영어 명칭은 맨 앞이 아니면 소문자로 쓰려고 했다. 이게 효과적이고, 잘 정리됐는지는 모르겠지만 일단은 이렇게 작성했다. 글을 쓰면서 더 좋은 형태로 개선해야 될 것 같다.
이 외에도 비슷한 표현을 계속 반복하여 보기 안 좋은 경우가 많았다. 예를 들면, 이렇게, 이러한, 더 자세한 내용은, 이와 같이, 이를 등 비슷한 표현을 너무 많이 반복한 것 같다.
이러한(또 쓴다 ㅋㅋㅋ) 문제들로 인해 글을 작성하는 게 너무 힘들었다. 오늘 꿈으로 블루 프린트 만지고 있는 꿈을 꿀 정도로 힘들었다 ㅋㅋㅋㅋ 글을 쓰는 도중에도 '이 글을 작성해서 어떤 사람에게 도움이 될 것인가?', '자기 과시를 위한 글로 변질되지는 않았는가?'라는 생각이 계속 이어져 중간중간 글을 쓰는 목적을 다잡아야 했다.
정리하자면 이렇다. '자기 과시는 개뿔 내가 노력해서 힘들게 글을 작성하는데 이게 왜 자기 과시야. 이렇게 정리하면 나뿐만 아니라 누군가한테는 큰 도움이 되겠지!'이다. 매번 내가 해온 것들을 말하면 분위기가 안 좋아지길래 자기 과시와 기만에 대한 고민이 많았는데 그냥 인정하자.
나는 열심히 살고 있고, 내 삶에 당당하다.
더 이상은 중요하지 않은 것들에 신경을 쓰며 불필요한 노력을 낭비하지 말자. 나는 열심히 살고 있고, 이에 대한 반응이 안 좋다면 심지어 내 행동에 대한 별 다른 피드백조차 없다면 내가 이상한 게 아니다. 애초에 이상하면 어떤가. 만날 사람은 잘 만나고 알아줄 사람은 잘 알아준다.
일에 대해서 일희일비하지 말자며 이를 지키고 살고 있었는데, 이제는 사람에 대해서도 일희일비하지 말고 나를 관철할 필요가 있어 보인다. 물론 고집과 신념 사이에서 충분히 고민을 하면서 말이다!
있어 보이게 인용문을 썼는데 그냥.. 많은 노력이 들어간 글을 마무리 지으려니까 감수성이 풍부해졌다 ㅋㅋㅋ 블로그 조회 수에도 신경 쓰지 말고, 사람들의 시선에 일희일비하지 말고 내 갈 길이나 가자, 상원아.
+)
2023.06.01 추가
작년에 'Chapter 5. 캐릭터, 상호작용, 그리고 인공지능'까지 수강하고, <Lost In Hope> 프로젝트를 시작했다.
당시에 러셀님이 언리얼 5에 맞게 영상 업데이트를 진행할 예정이라고 하시기도 했고, 수강 기간을 확인해 보니까 만료 이후에 101Prime이라는 멤버십에 가입하면 월 1,900원에 계속 수강할 수 있다고 해서 <Lost In Hope> 프로젝트를 진행할 겸 영상 업데이트를 기다리기로 결정했다.
이후로 약 9개월이 지나고, 현재 기준(2023년 6월 1일)으로 아직 업데이트가 이뤄지지 않은 걸 보면 앞으로도 업데이트는 없을 것 같다.
그래서 완강하지 않은 상태로 두는 것도 찜찜하니, 다시 수강하려고 클래스 101에 들어갔다. 이전에 확인했던 101Prime 멤버십을 확인해 보니 이번에는 서비스 종료 예정으로 가입이 불가능한 상태였다. 이에 대해 오늘 문의한 결과, 만료된 강의를 수강하려면 다시 결제를 할 수밖에 없다는 안내를 받았다.
따라서, 이번 '[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리' 시리즈는 여기서 마무리 지으려고 한다. 완강하지 못한 건 아쉽지만, 그동안 강의를 따라가며 배운 게 많기도 했고, 이후에도 다시 언리얼 공부를 시작할 예정이기에, 다음에는 다른 언리얼 시리즈로 돌아오도록 하겠다.
그리고, 확인해 보니까 클래스 101 측에서 만료된 강의를 재수강할 수 있게 하는 이벤트를 오픈한 적이 있던데 운이 좋다면 다시 수강하고 정리할 수도 있지 않을까?
잘 배우고 갑니다!
추가로, 현재 운영 중인 블로그, <Memoria Aeon>은 나의 성장을 기록하기 위한 블로그로 운영하고 있었는데, 최근 아주 소수이긴 하지만 검색으로 찾아오시는 분들이 생겼다.
이에 대해 '아무리 내가 공부한 기록을 올린 거라지만 과연 저작권에 문제가 없을까?'라는 의문이 들었다. 이것저것 찾아보니 큰 문제는 없어 보이긴 하는데, 가장 확실한 건 클래스 101 측에 문의하는 것 같다.
혹시 몰라 모든 정리 글 상단에 작성 원칙을 추가해 두었고, 내일 클래스 101에 문의해보려고 한다.
+)
2023.06.02 추가
클래스 101 측으로 저작권에 문제없다는 답변을 받았다.
'개발 > 언리얼' 카테고리의 다른 글
[Blueprint] 데미지 처리와 콜리전 채널 (0) | 2023.12.25 |
---|---|
[Blueprint] 기묘한 For Loop와 Delay의 관계 (0) | 2023.12.24 |
[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리 (3) (0) | 2022.08.03 |
[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리 (2) (0) | 2022.07.15 |
[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리 (1) (0) | 2022.07.08 |