AI는 잠입 액션 게임에서는 수색자가 플레이어를 찾기 위해서 주변을 수색하고, 슈팅 게임에서는 총알이 떨어지면 탄창을 갈고 체력이 부족해지면 회복 아이템을 사용하고 사격을 받으면 엄폐물 뒤로 숨는 등의 게임의 난이도를 적절하게 조절하고 게임의 생명력을 불어넣는 역할을 한다.
못 만든 게임은 여러 가지 요소가 플레이어를 거슬리게 만들지만 멍청하게 구는 AI 역시 게임의 재미를 떨어뜨리는 아주 중요한 요소이다. 낮은 난이도라고 AI가 플레이어가 눈 앞에 있어도 멍청하게 서있거나 수 초가 지나서야 반응하거나, 높은 난이도라고 플레이어가 대응할 시간도 없이 죽여버리는 것은 AI를 통한 난이도 조절에 완전히 실패한 경우라고 볼 수 있다. AI를 통한 난이도 조절을 제대로 하기 위해서는 무작정 반응 속도가 빠른 것이 아니라, 플레이어의 심리를 교묘하게 찌르는 전략적인 움직임을 AI가 할 수 있도록 만들어야 한다.
앞에서 사설이 길었지만, 이러한 AI를 제작하기 위한 방법으로는 여러 가지가 있다. 간단한 방법으로는 상태 머신(State Machine)부터 어려운 방법으로는 머신 러닝(Machine Learning)까지 말이다. 이러한 여러 가지 기법 중에서 언리얼 엔진은 비헤이비어 트리(Behavior Tree)라는 방식으로 AI 기능을 제공한다.
언리얼 엔진에서 제공하는 비헤이비어 트리는 블랙보드(BlackBoard)와 비헤이비어 트리(Behavior Tree), 이 두 가지 유형의 에셋의 조합으로 이루어진다.
블랙보드는 AI의 기억 저장소로 AI가 판단을 내리는데 필요한 데이터들을 저장하는 역할을 하고, 비헤이비어 트리는 블랙보드가 가진 데이터를 토대로 의사결정을 내리고 이를 실행으로 옮기는 역할을 한다.
비헤이비어 트리 노드의 종류
비헤이비터 트리의 노드는 루트(Root), 컴포짓(Composite), 데코레이터(Decorator), 서비스(Service), 태스크(Task). 이렇게 다섯 가지 종류가 있다.
루트(Root)
루트 노드는 비헤이비어 트리의 시작점이며, 아래쪽으로 향하는 단 하나의 연결만을 가질 수 있고, 데코레이터나 서비스를 덧붙일 수 없다.
루트 노드 자체에는 별다른 프로퍼티가 없지만, 루트 노드를 선택하면 디테일 패널에서 이 비헤이비어 트리에서 사용할 블랙보드 애셋을 설정할 수 있다.
컴포짓(Composite)
컴포짓 노드는 해당 분기가 실행되는 기본 규칙을 정의한다. 데코레이터를 통해서 분기로 들어가는 조건을 변경하거나, 중간에 실행이 취소되도록 만들거나, 서비스를 덧붙여서 컴포짓 노드의 자손이 실행되는 동안 서비스가 작동되도록 만들 수도 있다.
컴포짓 노드는 셀렉터(Selector), 시퀀스(Sequence), 심플 페러렐(Simple Parallel). 세 가지 종류가 있다.
셀렉터(Selector)
셀렉터 노드는 선택기 노드라고도 하며, 자손 노드를 왼쪽에서 오른쪽 순서로 실행하며, 자손 노드 중 하나가 실행에 성공하면 자손의 실행을 멈춘다. 셀렉터의 자손이 실행에 성공하면 셀렉터의 작동은 성공한 것이 되고, 모든 자손의 실행이 실패하면 셀렉터의 작동은 실패한 것이 된다.
시퀀스(Sequence)
시퀀스 노드는 자손 노드를 왼쪽에서 오른쪽 순서로 실행하며, 자손 중 하나가 실패하면 자손의 실행을 멈춘다. 자손 노드가 실행에 실패하면 시퀀스는 실패하며, 모든 자손 노드가 실행에 성공해야 시퀀스가 성공한다.
심플 페러렐(Simple Parallel)
심플 페러렐 노드는 단순 병렬 노드라고도 하며, 전체 노드 트리와 동시에 하나의 태스크를 실행할 수 있다. 예를 들어, 적을 향해 이동하면서 사격한다던지 하는 행동을 할 수 있게 해준다.
Finish Mode 설정을 통해서 메인 테스크가 완료되면, 서브 트리를 중단시키고 즉시(Immediate) 노드를 완료시킬지, 아니면 서브 트리를 완료할 때까지 지연(Delayed)시킬지를 설정할 수 있다.
데코레이터(Decorator)
데코레이터는 다른 비헤이비어 시스템에서 조건절이라고도 부르는 것으로, 컴포짓이나 태스크에 붙여서 분기나 노드가 실행될 것인지를 정의한다.
데코레이터의 종류는 기본적으로 16가지가 있고 필요하다면 비헤이비어 트리 에디터의 상단 메뉴 바에서 새 데코레이터 버튼을 클릭해서 커스텀 데코레이터를 추가할 수 있다.
프로그래밍 작업을 할 때 캐스팅, 즉 형 변환은 상당히 중요하다. 특히 여러 클래스가 상속으로 엮여있는 상황이라면 더더욱 중요해진다. 언리얼 엔진에서는 대부분의 클래스가 AActor 클래스를 상속받고 있고, 개발자가 만들어내는 클래스 역시 상당수는 AActor를 상속받게 된다.
C++
그렇기 때문에 몇몇 함수들은 이렇게 메인이 되는 부모 클래스를 매개변수로 받거나 돌려준다. 간단한 예를 들자면 다음 함수가 있다.
NotifyActorBeginOverlap() 함수는 액터의 콜리전에 콜리전을 가진 다른 액터가 들어오기 시작했을 때 호출되는 함수로, 매개변수를 통해서 자신의 콜리전과 접촉한 액터를 알려준다. 언리얼 엔진에서는 레벨에 배치되는 모든 오브젝트는 AActor 클래스를 상속받기 때문에, 콜리전과 접촉한 액터가 어떤 클래스던지 상관없이 무조건 AActor 클래스로 보내주는 것이다.
만약 콜리전 체크를 하는 액터가 겹침 이벤트가 발생할때마다 데미지를 입는 클래스인데 데미지를 입힐 수 있는 클래스가 AProjectile 클래스라고 가정했을 때, 위의 예시 코드처럼 별도의 검사를 하지 않는다면, 액터가 아무 물체에나 스칠 때마다 데미지를 입어버릴 것이다.
그래서 필요한 것이 바로 캐스팅이다. 언리얼 엔진에서는 Cast<T>() 라는 함수로 기본적인 캐스팅을 제공한다.
void AActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
AProjectile* Projectile = Cast<AProjectile>(OtherActor);
if (Projectile)
{
// Damage Process
}
}
바로 위의 예시 코드처럼 캐스팅을 진행하면 된다. 만약 콜리전에 검출된 액터가 AProjectile 클래스가 아니라면 캐스팅에 실패할 것이고 Projectile 변수의 값을 nullptr이 되기 때문에 if문 안으로 진행하지 못해서 Damage Process가 진행되지 않는다.
블루프린트
블루프린트 작업에서도 캐스팅이 가능하다.
블루프린트 컨텍스트 메뉴에서 "형변환"이나, 캐스팅하고자 하는 타입의 클래스 명을 검색하면 해당 클래스로 형변환할 수 있는 노드를 추가할 수 있다.
위의 코드는 틱이 작동하는 동안 bSeeingThrough의 상태에 따라서 다이내믹 머티리얼 인스턴스의 머티리얼 파라미터 "Opacity"를 0에서 1로 만들거나 1에서 0으로 만들고 작동이 끝나면 Tick() 함수의 작동을 멈추게 한다. 참고로 머티리얼 파라미터 "Opacity"는 이후 작업에서 추가한다.
SetShowSeeingThrough() 함수는 bShow 변수를 받아서 액터가 투명해지기 시작하는지 불투명해지기 시작하는지 결정한 뒤 Tick() 함수를 작동시킨다.
코드 작업이 끝났다면 솔루션 탐색기에서 프로젝트를 빌드한 뒤, 에디터로 돌아간다.
투명해지는 만들기
이번에는 SeeingThroughActor가 사용할 머티리얼을 만들 차례이다. Props 폴더 안에 Materials 폴더를 만든 다음, 콘텐츠 브라우저 패널의 파일 창에 우클릭해서 머티리얼을 선택한다. 그리고 생성된 머티리얼의 이름을 M_SeeingThrough로 한다.
머티리얼을 더블클릭해서 머티리얼 에디터를 열고 디테일 패널에서 Material 카테고리의 Blend Mode를 Translucent로 설정한다.
그리고 TextureSamleParameter2D와 ScalarParameter를 추가하고 각각 이름을 Texture와 Opacity로 한다.
그리고 Opacity 파라미터 노드를 선택한 뒤, 디테일 패널에서 Default Value를 1로 설정한다.
그 다음, 적용과 저장을 하고 머티리얼 에디터를 닫는다.
언리얼 에디터로 돌아와서, 콘텐츠 브라우저 패널에서 방금 만든 머티리얼을 우클릭하고 머티리얼 인스턴스 생성을 선택한다.
머티리얼 인스턴스가 만들어지면 더블클릭해서 머티리얼 인스턴스 에디터를 열고 디테일 패널에서 Opacity를 체크해주고 머티리얼 인스턴스를 저장한 뒤, 머티리얼 인스턴스 에디터를 닫는다.
SeeingThroughActor 배치
이제 SeeingThroughActor를 배치할 차례이다. 콘텐츠 브라우저 패널에서 SeeingTroughActor를 찾아서 벽 토대 위에 배치한다.
콜리전(Collision)은 언리얼 엔진에서 물리적인 충돌이나 레이 캐스팅 실시간 처리를 해준다. 이러한 물리 시뮬레이션은 Collision Response(콜리전 반응) 및 Trace Response(트레이스 반응) 설정을 통해서 다른 오브젝트 유형과 어떻게 상호작용할지 정의된다.
블록(Block), 겹침(Overlap), 무시(Ignore)
충돌 작용은 블록(Block), 겹침(Overlap), 무시(Ignore)로 나누어진다.
블록은 충돌하는 두 오브젝트가 모두 블록이어야 두 오브젝트가 충돌했을 때, 겹치지 않고 서로에게 막히게 된다. 콜리전 옵션 중에 Simulation Generates Hit Event 프로퍼티를 true로 설정하면 이러한 충돌이 발생했을 때 Event Hit을 받아서 블루프린트나 디스트럭터블 액터, 트리거 등에 사용할 수 있고 이를 응용해서 총알에 맞아서 깨지는 유리창 등을 구현할 수 있다.
겹침은 다른 오브젝트를 통과시키지만, 만약 Generate Overlap Event가 활성화된 상태라면 Overlap Event를 발생시킨다. 이 겹침 이벤트가 발생하려면 두 오브젝트 모두 겹침 이상으로 설정되어 있어야 한다. 만약 한 쪽은 겹침이고 다른 한 쪽이 무시인 상태라면 겹침 이벤트는 발생하지 않는다.
무시는 모든 오브젝트와 트레이스를 통과시키며 어떠한 이벤트도 발생시키지 않는다.
콜리전 이벤트
콜리전이 발생시키는 이벤트를 다루는 방법에 대해서 배워보자.
히트 이벤트(Hit Event)
히트 이벤트는 블록 상태인 오브젝트들이 서로 충돌했을 때 발생하는 이벤트로 해당 오브젝트에게 통지된다. 히트 이벤트를 발생하게 하기 위해서는 우선 콜리전의 프로퍼티 중에 Simulation Generates Hit Event를 true로 설정해줘야 한다.
우선 히트 이벤트를 받아서 처리하기 위한 클래스를 BlockCollisionActor라는 이름으로 Actor 클래스를 상속받아서 생성하자.
그리고 비주얼 스튜디오로 가서 BlockCollisionActor.h에 다음 멤버 변수와 함수 선언 코드를 추가한다.
NotifyActorBeginOverlap() 함수는 겹침이 시작되었을 때 실행되는 함수이고 NotifyActorEndOverlap() 함수는 겹침이 끝났을 때 실행되는 함수이다. 이 두함수를 통해서 우리는 언제 겹침이 시작되었는지, 언제 겹침이 끝났는지를 알 수 있다.
BoxMesh는 사실 큰 필요는 없지만 구체가 통과해서 지나갔음을 보여주기 위해서 추가한다. 하지만 만약 스태틱 메시를 사용하지 않을 것이라면 BoxComponent나 SphereComponent, CapsuleComponent 같은 콜리전 컴포넌트를 사용해야 한다.
그 다음엔 OverlapCollisionActor.cpp로 가서 스태틱 메시 컴포넌트를 사용하기 위해 다음 전처리기를 추가한다.
언리얼 엔진에서 표준 머티리얼을 생성하고 수정하는 작업은 꽤나 시간을 많이 소모하는 작업에 속한다. 특히 머티리얼을 수정하고 실제 엔진에 적용되는 시간이 꽤나 걸린다.
수정 작업이 머티리얼의 그래프를 수정해야하는 작업이라면 감수할 수 밖에 없는 시간 소모이지만, 머티리얼 그래프에 대한 수정이 아니라 단순 수치 조정에 불과한 작업이라면 적당한 수치를 찾을 때까지 소모하는 시간이 만만치 않게될 것이다.
혹은 머티리얼 A와 머티리얼 B의 동작은 똑같지만 텍스쳐가 다르거나 러프니스 값이 달라서 반짝임 정도만 다르다고 할 때, 완전히 새로운 표준 머티리얼을 만드는 것은 분명히 시간과 노력의 낭비가 될 것임에 틀림이 없다.
이러한 시간 소모를 줄이기 위해서 언리얼 엔진에서는 표준 머티리얼에 값이나 텍스쳐 등을 바꿀 수 있는 머티리얼 파라미터(Material Parameter)와 표준 머티리얼을 상속받아 머티리얼 파라미터를 수정해서 다른 머티리얼처럼 사용할 수 있는 특수한 유형의 머티리얼인 머티리얼 인스턴스(Material Instance)를 제공한다.
이렇게 표준 머티리얼에서 바리에이션이라고 할 수 있는 머티리얼 인스턴스를 만드는 작업을 머티리얼 인스턴싱(Material Instancing)이라고 한다.
우선 파라미터와 인스턴스를 배우기 이전에 프로젝트에서 새 머티리얼을 추가하자. 머티리얼을 추가하는 방법은 콘텐츠 브라우저 패널에서 신규 추가 버튼을 누른 다음 머티리얼 항목을 선택하면 된다.
새로 추가한 머티리얼의 이름은 TestMaterial로 하자. 생성된 머티리얼을 더블클릭하면 머티리얼 에디터가 열린다.
머티리얼 파라미터(Material Parameter)
머티리얼 파라미터란 표준 머티리얼을 다시 컴파일하지 않고도 머티리얼 인스턴스에서 머티리얼을 수정할 수 있도록 해주는 특수한 머티리얼 표현식이다. 이 머티리얼 파라미터 노드는 표준 머티리얼의 머티리얼 그래프 안에서 다른 노드과 비슷하게 동작하지만, 머티리얼을 컴파일하고 머티리얼 인스턴스에서 사용할 때는 머티리얼 파라미터의 값을 실시간으로 수정하거나, 머티리얼을 새로 컴파일하지 않고도 머티리얼의 모양과 느낌을 다르게 바꿀 수 있다.
머티리얼 파라미터 생성하기
기존 머티리얼 노드를 파라미터로 변환
기존의 머티리얼 노드에 우클릭하여 파라미터로 변환을 선택하면 머티리얼 파라미터로 변환된다.
이 방법이 머티리얼 파라미터를 만드는 가장 간단한 방법이지만, 머티리얼 그래프에서 모든 노드가 머티리얼 파라미터로 변환되지는 않는다. 파라미터로 변환 메뉴는 머티리얼 파라미터로 변환이 가능한 노드에서만 표시된다.
팔레트에서 파라미터 추가하기
머티리얼 에디터의 팔레트 패널에서 Parameter를 검색하면 머티리얼 그래프에 추가할 수 있는 파라미터가 모두 나온다. 이중에 필요한 파라미터를 머티리얼 그래프에 드래그함으로써 머티리얼 파라미터를 추가할 수 있다.
우클릭 메뉴에서 파라미터 추가하기
머티리얼 그래프 빈 자리에 우클릭해서 뜨는 컨텍스트 메뉴에 Parameter를 검색해서 머티리얼 파라미터를 추가할 수 있다.
머티리얼 파라미터 이름 변경하기
머티리얼 파라미터의 이름은 매우 중요하다. 만약 C++ 코드나 블루프린트에서 머티리얼 파라미터의 값을 변경하고자 한다면 이 머티리얼 파라미터의 이름을 알고 있어야 하기 때문이다. 물론 여러개의 파라미터를 만들면 자동으로 Param, Param_1 같이 이름이 자동으로 지어지지만, 원활한 작업을 위해서는 파라미터의 이름을 명확하게 짓는것이 중요하다.
파라미터의 이름을 바꾸는 법을 배우기 전에 우선 머티리얼 그래프에 VectorParameter를 하나 추가하고 제일 위의 하얀색 소켓과 머티리얼의 베이스 컬러 소켓을 연결하자.
이 VectorParameter[각주:1]는 머티리얼의 색상을 결정하는 파라미터로 사용할 것이다. 이 파라미터의 이름은 Color가 적당할 것이다. 그럼 이 파라미터의 이름을 Color로 바꾸는 방법을 배워보자.
방법 1. 머티리얼 파라미터의 이름 클릭
첫 번째 방법은 머티리얼 파라미터를 선택한 상태에서 파라미터의 이름을 클릭하는 것이다. 그러면 아래의 이미지와 같이 파라미터의 이름을 변경할 수 있게 된다.
방법 2. 디테일 패널에서 변경
두 번째 방법은 머티리얼 파라미터를 선택한 다음, 디테일 패널에서 Parameter Name을 변경하는 것이다.
파라미터의 이름을 변경하는 방법을 배웠으니 편한 방법을 선택해서 VectorParameter의 이름을 Color로 변경한다.
그 다음엔 ScalarParameter[각주:2]를 두 개 만들고 이름을 각각 'Metallic', 'Roughness'로 정하고 메탈릭 소켓과 러프니스 소켓에 연결해주자.
머티리얼 파라미터 기본값 변경하기
표준 머티리얼에서는 머티리얼 파라미터의 기본값을 설정할 수 있다. 나중에 머티리얼 인스턴스를 처음 생성할 때, 머티리얼 파라미터의 기본값은 이것을 따르게 된다.
파라미터의 기본값은 머티리얼 파라미터 노드를 선택한 다음 디폴트 패널에서 수정할 수 있다.
표준 머티리얼에서의 Color 기본값은 {1.0, 0.5, 0.0, 0.0}으로 설정해주자.
기본값을 변경했다면, 상단의 메뉴바에서 적용 버튼을 누르고 저장 버튼을 누른 뒤, 머티리얼 에디터를 닫는다.
덤으로, 파라미터의 기본값을 변경했을 때 프리뷰에 적용되는 시간이 걸리고, 적용 버튼을 눌렀을 때 변경내용을 원본 머티리얼과 월드의 사용된 곳에 적용한다는 프로그레스바가 뜨는 것을 봤을 것이다. 지금은 머티리얼이 매우 간단하고 월드에 적용한 곳이 없어서 적용이 매우 빨랐지만, 복잡한 머티리얼이고 월드에 사용된 곳이 많았다면 이 적용되는 시간이 훨씬 길었을 것이다. 앞에서도 말했지만, 이러한 요소는 개발 시간을 소모하는 중요한 요소로 가능하다면 머티리얼 인스턴싱을 사용해서 개발 시간에 있어서 불필요하게 소모되는 시간을 줄여야 한다. 이것은 두 번 강조해도 모자람이 없는 일이다.
머티리얼 인스턴스(Material Instance)
머티리얼 인스턴스는 하나의 머티리얼을 부모로 상속받아서 그 부모 머티리얼의 구조대로 동작하되, 부모 머티리얼이 파라미터로 내어주는 부분을 수정해서 부모 머티리얼의 다양한 바리에이션을 만들어낼 수 있도록 하는 기능이다.
머티리얼 인스턴스 생성하기
머티리얼 인스턴스를 생성하기 위해서는 부모로 삼고자하는 머티리얼에 우클릭한 뒤 머티리얼 인스턴스 생성를 선택하면 된다.
머티리얼 파라미터 변경하기
머티리얼 인스턴스가 생성되고 나서 더블클릭해서 머티리얼 에디터를 열어보면 표준 머티리얼의 머티리얼 에디터와는 구성이 다른 것을 확인할 수 있다.
디테일 패널에서 부모인 TestMaterial 표준 머티리얼에서 파라미터로 공개한 파라미터들을 볼 수 있다. 각 파라미터의 앞에 있는 체크박스에 체크함으로써 해당 파라미터를 사용하고 수정할 수 있다.
Color 파라미터의 체크박스를 체크하고 값을 변경해보면 표준 머티리얼에서 값을 변경할 때와는 달리 리컴파일 없이 값을 빠르게 바꿀 수 있음을 확인할 수 있다.
콘스턴트 / 다이내믹 인스턴스
머티리얼 인스턴스 유형은 머티리얼 인스턴스 콘스턴트(Material Instance Constant), 머티리얼 인스턴스 다이내믹(Material Instance Dynamic) 두 가지가 있다.
머티리얼 인스턴스 콘스턴트(Material Instance Constant)
머티리얼 인스턴스 콘스턴트(MIC)는 실행시간 전에 한 번만 계산되는 머티리얼 인스턴스이다. 그렇기 때문에 게임플레이 도중에는 변경이 불가능하지만, 그 이상의 컴파일이 필요하지 않기 때문에 퍼포먼스 상에서의 이점이 있다.
머티리얼 인스턴스 다이내믹(Material Instance Dynamic)
머티리얼 인스턴스 다이내믹(MID)은 게임플레이 도중에 머티리얼 파라미터의 값을 계산하고 변경할 수 있는 머티리얼 인스턴스이다. 게임플레이 도중에 C++코드나 블루프린트 코드를 이용해서 머티리얼 파라미터의 값을 바꿀 수 있기 때문에, 캐릭터가 피격당했을 때 색깔이 바뀐다든지, 연속으로 발사해서 과열된 총열이 빨갛게 달아오르는 등의 연출을 사용한다든지 하는 다양한 연출을 할 수 있게 된다.
MID의 생성은 에디터가 아닌 C++ 코드나 블루프린트 그래프 내에서 이루어진다. Create Dynamic Material Instance 노드를 통해서 MID를 생성하고 Set Parameter Value 노드를 통해 파라미터들의 값을 변경할 수 있다.
머티리얼 에디터에서 Vector Parameter는 RGBA 채널을 가지는 색상 파라미터 역할을 한다. 흰색 소켓은 RGBA 모든 채널의 색상이 혼합된 색을 의미하고, 빨간 소켓은 R 채널, 초록 소켓은 G 채널, 파란 소켓은 B 채널, 회색 소켓은 A 채널을 의미한다. [본문으로]
ScalarParameter는 프로그래밍에서 1개의 float값, 즉 하나의 실수를 담을 수 있는 파라미터이다. [본문으로]
[유니티 어필리에이트 프로그램]
아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.
이전 섹션에서는 C++ 내려보기 템플릿을 참고해서 일반적인 RPG처럼 마우스 클릭을 통해 캐릭터를 이동시키는 방법을 구현해보고 코드를 분석해본다.
프로젝트 세팅
RpgProject 라는 이름으로 새 프로젝트를 하나 만든다.
RpgProject를 생성한 뒤에는 내려보기 템플릿으로도 새 프로젝트를 하나 만드는데, 이것은 캐릭터의 메시와 애니메이션을 가져오기 위함이다.
제일 먼저 할 일은 내려보기 프로젝트에서 캐릭터의 메시와 애니메이션을 가져올 것이다. 방금 만든 내려보기 프로젝트의 콘텐츠 패널의 콘텐츠 폴더 하위에 Mannequin 폴더가 보일 것이다. 이 안에 캐릭터의 메시와 애니메이션이 들어있다. 이 폴더의 내용물들을 RpgProject로 옮겨야 한다. 이렇게 프로젝트에 포함된 애셋들을 다른 프로젝트로 옮기는 작업을 이주(Migrate)라고 한다. 직접 파일을 옮기지 않아도 언리얼 엔진에서는 이것을 도와주는 기능을 제공한다.
Mannequin 폴더에 우클릭을 하고, 이주... 를 선택한다. 애셋 리포트 창이 뜨면 리스트를 체크하고 확인 버튼을 누른다.
그 다음 대상 콘텐츠 폴더 선택 대화상자가 열리면 RpgProject 프로젝트의 Content 폴더를 찾아서 폴더 선택을 한다.
이주 작업이 끝난 뒤에 RpgProject로 가서 콘텐츠 브라우저 패널을 확인하면 내려보기 프로젝트에 있던 Mannequin 폴더와 그 안의 애셋들이 RpgProject에 성공적으로 옮겨진 것을 확인할 수 있다.
캐릭터의 메시와 애니메이션을 모두 이주시켰으면 그 다음은, 비주얼 스튜티오를 열고 솔루션 탐색기에서 RpgProject.Build.cs를 찾아서 소스파일을 연다.
Build.cs에서는 게임을 개발하면서 사용할 모듈을 추가하거나 뺄 수 있는데, 여기서는 두 가지 모듈을 추가할 것이다. 아래의 예시 코드와 같이 PublicDependencyModuleNames에 "NavigationSystem"과 "AIModule"을 추가해주자. NavigationSystem 모듈은 네비게이션 메시와 관련된 기능에 도움을 주는 모듈이고 AIModule 은 이름 그대로 AI 기능에 관련된 모듈이다. 클릭된 위치로 캐릭터를 이동시킬 때, 처리하는 코드를 일일이 만드는 대신, 이 두 모듈의 기능의 도움으로 내비게이션된 경로를 따라서 움직이도록 만들 예정이다.
if (Distance > 120.0f) { UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, DestLocation); } } }
이 함수에서는 우선 컨트롤러가 소유하고 있는 폰을 가져와서 폰과 목적지 사이의 거리를 측정해서, 그 거리가 120 언리얼 유닛보다 크면 폰을 목적지로 이동시킨다. UAIBlueprintHelperLibrary클래스의 SimpleMoveToLocation() 함수는 프로그래머가 목적지로 폰을 이동시키기 위한 처리를 하는 모든 코드를 일일이 작성하는 대신에 간단한 함수 호출로 그 모든 일을 할 수 있도록 도와준다. 아까 전 프로젝트 세팅 단계에 모듈을 추가한 것은 이 기능을 사용하기 위해서 였다.
이 코드는 캐릭터의 무브먼트를 규정하는 코드로, 캐릭터를 이동시키기 전에 이동 방향과 현재 캐릭터의 방향이 다르면 캐릭터를 이동 방향으로 초당 640도의 회전 속도로 회전시킨다음 이동시킨다. 그리고 캐릭터의 이동을 평면으로 제한하고, 시작할 때 캐릭터의 위치가 평면을 벗어난 상태라면 가까운 평면으로 붙여서 시작되도록 한다. 여기서 평면이란 내비게이션 메시를 의미한다.