상세 컨텐츠

본문 제목

pond 프로젝트 09 - AI 구성

Unreal Engine 4/Project

by hwano 2015. 4. 22. 17:10

본문

 

챕터별 목표

1. 물고기가 정해진 wayPoint들을 옮겨다니도록 하자.

2. 물고기를 spawnVolume을 통해 랜덤하게 생겨나게 하자.

3. 물고기가 랜덤한 위치들을 옮겨다니도록 해보자.

4. spawn되는 wayPoint가 Sphere들을 피해서 생기도록 해보자.

 

 

 

1-1. 물고기들을 wayPoint 사이를 이동하게 만들자.

 

 

이제 기본적인 이동까지는 제대로 작동한다.

wayPoint들 사이에 collision용 구들을 배치해봤다. 물고기들이 구들 사이를 자동으로 피해다닌다.

 

 

현재까지의 상태를 영상으로 찍어놓고 싶은데 실행화면을 동영상으로 캡쳐하는 방법을 찾아보자.

마티네창을 열면 matinee 액터가 하나 생성된다. 마티네 창에서 레코드 아이콘을 누르고 원하는 설정 후 

창을 닫으면 자동으로 프로그램이 꺼지면서 녹화가 시작된다. 녹화된 파일은 avi 무압축으로 프로젝트

폴더 saved에 저장된다. 코덱 압축은 지원하지 않는다고 한다.

 

저장을 해보니 프레임이 너무 떨어진다. 또 이미지 불러오는데 초반에 약간의 텀이 있는데

이미지 불러오는건

http://cafe.naver.com/unrealenginekr/2640

여기 글을 참고하자.

 

근데 챕쳐때마다 프로그램이 꺼지니 매우 불편하다..  아우 너무 불편해서 그냥 캡쳐프로그램쓰기로.

Loilo 로 썼고 저속으로 해야만 캡쳐가 된다.

 

 

먼저 해결해야 할 부분들이다.

 

1. 이동은 부드러운데 로테이션은 뚝뚝 끊기는 문제

2. wayPoint가 변경될때 방향이 갑자기 변경되는 문제

 

 

 

 

1-2. 로테이션 끊기는 현상을 수정하자.

 

 

로테이션이 왜 뚝뚝 끊길까? 현재 fish 캐릭터의 이동은 순전히

캐릭터 클래스의 무브먼트와 moveTo라는 테스크로 자동시스템에 의존한다. 따라서

캐릭터 클래스의 캐릭터무브먼트 컴포넌트의 설정 때문이라는 의심이 들수밖에 없다.

 

한번 AIProject 샘플도 같은 방법으로 구를 만들어 경로를 방해해 봤다.

 

 

이 경우 wayPoint에 들려서 다음 타겟으로 타게팅이 바뀔때는 회전 애니메이션이 나오며 자연스럽지만

그 외에 자동으로 이동이 회전이 되는 경우에는 역시 뚝뚝 끊어지는 현상이 있다.

navMesh 계산 결과가 구 주위가 각이져서 그런게 아닐까 잠시 생각했지만

꼭 구 주위가 아니더라도 회전시 각이 지는건 마찬가지라 이문제는 아닌걸로.

 

 

 

 

1-3. AIProject 샘플의 회전 방식 파악

 

 

AIProject 샘플에서 타게팅이 바뀔 때 회전 애니메이션이 발생하는 과정을 확인해 보자.

botCharacter 액터를 찍어보면 애니메이션 블루 프린트는

owen_exmapleBP이고 botAI라는 AIController의 지배를 받는다.

 

Owen_exampleBP를 열어보면 애니메이션 블루프린트에서도 변수를 만들수 있고

이를 조절해 볼수 있는 왼쪽창이 있다. edit preview에서 수치를 실시간으로 바꿔보며 캐릭터

변화를 테스트 해볼 수 있다.

저 예시에서 speed는 캐릭터가 걷고 뛰는 과정을

yaw는 총 조준 좌우 회전을 테스트 해볼 수 있는 식이다.

 

그리고 빨간색 네모칸 처럼 애니메이션 블루 프린트에도 eventGraph가 따로 있다.

물고기 움직임을 만들때 썼던 그래프는 animGraph였었다. 애니메이션 블루 프린트는

원래 animGraph방식의 그래프창만 주어지는지 알았었다.

 

eventGraph에서는 들어오는 데이터를 가공하거나 변수를 수정하는 등의 작업이 이루어지게 된다.

animGraph에서는 순전히 설정된 변수등으로 어떻게 포즈를 바꿀지만 다룬다고 보면된다.

 

animGraph에서 전에 add state를 하여 포즈를 설정해줬었는데 이게 state machine이라고 한다.

state machine은 이전 페이지에서 AI종류 얘기할때 나왔던

FSM( Finite State Machine ) 유한 상태기계 에서의 state machine과 같은 개념이다.

외부 또는 내부의 이벤트에 대한 반응으로 상태 변화를 모델링하는 과정이다.

 

 

 

 

1-4. animation blueprint 구조 파악

 

 

여기서 예전 '블루프린트 3인칭 게임 제작' 강좌에서 봤던 내용의 개념을 다시 정리해 보자.

 

AnimGraph의 구성은 단순하다. locomotion이 final animation pose로 들어간다.

locomotion은 보행, 보행능력 정도의 의미다. 즉, 보행이라는 state machine을 만들고

state가 walk인지 jump인지 crouch(쪼그리다)인지 결정은 하는 거다.

단 이 경우엔 idle_wak_run이 하나로 묶여 있는데 blend space 블루프린트로 변형되는

하나의 상태로 본것이다. idle_walk_run,  jumpStart 등의 각각의 state로 들어가면

또 그래피가 나오는데 여기는 애니메이션 어셋을 그 상태와 연결해 주는 작업을 하는 공간이다.

 

AnimGraph는 이런식으로

최상위  >  stateMachine의 내부 알고리즘  >  애니메이션 어셋을 실제 state에 연결

기본적인 3가지 단계로 이루어진다.

////////////////////////////////////////////////////////////////////////////////////////////////

 

다시 Owen_exampleBP로 돌아와서 여기저기 찾아봐도 캐릭터 회전에 대한 내용은 없다

(어찌보면 당연하지. 내부 애니메이션 문제가 아니라 액터자체의 회전이 필요한 부분이니까)

타게팅이 바뀌는 순간 회전에 대한 옵션이 어디있는거냐....  ㅁ;ㅣㄴㅇ럼;ㅣ나얾

찾았다.. 

 

 

 

 

 

1-5. 방향전환시 자동회전 적용

 

 

 

CharacterMovement 말고 최상위 캐릭터 클래스에 있다.

pawn / use Controller rotation Yaw를 꺼주면 된다. 찾는데 진짜 드럽게 오래걸렸네

 

이 옵션 이후에도 잔 꺾임현상은 없어지지 않는다. 위에서 의심했던 데로 navMesh때문인거 같아

cell size를 올려 각지게 변경하였더니

 

더더욱 심한 꺾임으로 움직인다. 아무래도 저거 때문이 맞는거 같다.

 

저 계단 현상을 완화시키려면 cell size값을 낮춰야 하는데 값이 1이하로 줄어들지 않도록 되어있다.

1로는 각이 사라지지 않는데 이런 곳에서 씬스케일 문제가 생기는거 같다. 씬이 훨씬 더 커야 상대적으로

각이 작아서 더 부드러울것이다.  지금와서 또 씬 스케일을 더 키우기는 좀 짜증이 나니 다른 방법을

찾아보자.

 

디테일창에서는 1이하의 값이 안들어가지만 블루프린트로 강제로 숫자를 넣어주면 들어가지 않을까?

 

//////////////////////////////////////////////////////////////////////

블루 프린트로 다른 액터의 옵션값을 조절하는 방법을 못찾겠다.

 

charaterMovement에 보면 rotation rate라고 있다.

위에서 use Controller rotation yaw를 껐을때 작동하는데 yaw의 수치를 내려주면

캐릭터의 방향이 바뀌었을 경우 회전이 일어나는 속도이다.   나중에 디테일한 움직임을 잡을때

물고기의 방향이 크게 꺾이면 이값을 올리고 작게 꺾이면 이값을 내리도록

연동시키면 부드럽게 변하지 않을까?.,.  

 

 

 

 

1-5. 루트 모션

 

 

문서를 보다 보니 루트모션이라는게 있다

https://docs.unrealengine.com/latest/KOR/Engine/Animation/RootMotion/index.html

걷거나 뛰는 모션처럼 제자리에서 일어나는 모션이 아닌경우 움직임을 중앙으로 고정시켜주는

방법이다.  블루프린트 인터페이스가 이때 사용된다고 한다.

 

 

 

 

 

 

 

 

 

2-1. spawnFishesVolume

 

 

이제 다시 죽여놓았던 spawnFishesVolume을 enable시켜줄 차례다.

spawnFishesVolume을 작동시키면 화면에 새로운 물고기들이 생겨나긴 하는데 움직이질 않는다.

fishCharacter 블루프린트 안에 들어가보면

 

ai controller class는 fishAI로 되어 있진 하지만 auto possess ai 가 placed in wolrd인 애들만 설정되도록 되어있다. 저걸 spawned로 바꾸면 새로 생겨난 애들의 ai controller 클래스를 fishAI로 할수가 있다.

근데 auto possess ai이니 무조건 오토로 ai controller class를 fishai로 한다는 건데

fishCharater지만 aiController가 각각 다른 클래스로 설정할 필요도 있을꺼다. 그래서 블루프린트안에서

aiControll class를 정하는 방법을 찾아보자.

 

방법을 찾다가 애초에 spawn할때 액터를 만들고 ai를 지정하는게 아니고

ai를 기반으로 spawning하는 노드가 있다.

위쪽 노드가 원래 사용했던 노드고 아래쪽 노드가 ai를 세팅한 체로 spawn하는 노드.

이번에는 aiController가 각각 다를 이유가 없어서 그냥 auto Possess AI 설정을 바꿔주는

방법을 사용하였다.

 

여러마리를 생성하여 움직이도록 해봤는데 서로 마주보면 충돌이 일어나서 움직임이 엉켜버린다.

실제 작업때는 물고기들이 서로 너무 가까워 지면 새로운 targetPoint를 설정해서 서로 비껴나가도록

할 생각이라 우선 무시하고 진행하자.

 

 

 

 

 

 

 

 

3-1. 랜덤 wayPoint 생성

 

 

 

이제 랜덤한 위치에 wayPoint들이 생겨나도록 해보자.

원래 있던 wayPoint들을 삭제하고 게임 플레이시에도 보일수 있도록 wayPoint 블루프린트에서

메쉬를 하나 적용해 주자.

 

 

wayPoint를 랜덤한 위치에 생겨나게 되면 BT를 모조리 수정해야한다.

이전에는 미리 화면에 placed해둔 wayPoint들을 읽어들여서 wayPoint들의 order값을

route값과 비교하여 위치값을 가져오는 방식이었다.

 

하지만 랜덤 spawn방식으로 바꾸게 되면 하나의 wayPoint만 생겨나게 하고 캐릭터가

이동하여 콜리젼이 일어나게 되면 새로운 wayPoint로 바뀌게 해야한다.

 

두번째 방법으로 바꿔야 하지만 스터디용으로 처음 방법에서 조금만 바꿔서 작동이 되는지 확인해보자.

화면에 직접 placed해둔 wayPoint들이 아닌 랜덤하게 3개의 wayPoint들을 spawn하고

wayPoint들에 order를 순서대로 부여하여 움직이도록 해보자.

 

 

 

 

 

3-2. wayPoint 랜덤설정 

 

 

spawn된 wayPoint들에 order명령을 매겨서 작동하는걸 해봤는데 그닥 어렵지 않다. 넘어가자.

 

 

 

 

3-3. wayPoint를 하나씩 생성하자.

 

 

이제 원래 하려고 했던 방식을 적용해보자.

물고기에 하나의 랜덤한 wayPoint들 할당하고 wayPoint로 이동을 마치면

새로운 wayPoint를 생성하여 이동하도록 하자.

 

이게 원래 구조인데 spawnWayPointVolume의 내용이 nextRoute 부문에

들어가면 될거 같다. updateRoute는 아예 없어질테고 BB의 key들도 조금

바뀌어야 겠지.

 

//

먼저 wayPointVolume를 BTT_spawnWayPoint 블루 프린트로 가져와야한다.

(BTT_nextRoute를 BTT_spawnWayPoint로 바꿨다.)

 

여기서 문제가 wayPointVolume이라는 블루프린트를 화면에 place하고

다른 블루프린트로 불러올려고 하는데 방법을 모르겠다.

그냥 outliner에서 드래그해다가 다른 블루프린트 그래프 안으로 넣어도 나타나진 않는다.

 

레벨 블루프린트에서는 그냥 outliner에서 액터를 클릭한 채로 우클릭을 하면

바로 이름이 떠서 레퍼런싱 할 수 있다.

하지만 일반 블루프린트에서는 우클릭해서 검색을 해봐도 클릭한 오브젝트가 검색이

되지 않는다.

 

 

일반적인 블루프린트끼리의 통신이라면

https://docs.unrealengine.com/latest/KOR/Engine/Blueprints/UserGuide/BlueprintCommsUsage/BPComHowTo/index.html#블루프린트인터페이스

위 경로의 예제들을 참고하면 된다. 내경우엔 wayPointVolume을 레벨에 place해놓은 상황이기때문에

직접 블루프린트 통신 부분을 참고하면된다.

 

헌데 똑같은 방법으로 BTT_spawnWayPoint에 오브젝트 변수를 만들고 Behavior Tree에서 연결하려고

하면 적용이 되지 않는다. 이건 버그인지 아님 BB같은 다른 방법을 이용하라는 건지 확실히

모르겠다.

분명히 화면에 배치된 wayPointVolume 액터를 선택할 수 있다고 나오지만 선택을 해도 None에서

바뀌질 않는다. 버그인가 원래 안되는건가?

 

일반 블루프린트들로 테스트해봤을때는 잘된다. BT에 사용하는 task기반의 블루프린트라 안되는게 아닐까

싶어 BB를 이용하려고 한다.

 

 

////////////////////////////////////////////////////////////////////////////////////////

wayPointVolume을 기반으로 wayPoint를 spwan하려면 actor를 레퍼런싱해야하고 그 actor의 box

component에 접근해야하는데( spawn할 위치값을 얻기 위해)

이 방법을 모르겠다. 정확히 설명된걸 못찾겠다.

 

그래서 그냥 클래스를 기반으로 액터를 긁어모으고 클래스를 기반으로 컴포넌트를 긁어모으는

원시적인 방법을 썼다. 씬에 wayPointVolume 하나여서 루프돌려도 상관없었지만 여러개가

생기는 상황이면 난감할듯 싶다. string으로 비교하던지 해야겠지.. 빨리 방법을 알고 싶다.

 

 

우선 현 상태로 구현된 wayPoint 이동

 

 

 

 

 

 

3-4. 물고기의 갯수를 늘려보자. 

 

 

현재까지의 진행상태로 물고기의 마리수를 늘려보자.

물고기의 마리수를 늘리려면

1. BB에 원하는 물고기의 마리수를 입력할 수 있는 변수를 만든다

2. targetPoint가 어레이로 바뀌며 배열컨트롤이 되어야 한다.

등등인데 대충 생각만으로는 파악이 안된다. 진행하며 파악하자.

 

먼저 1번 BB에 fishCount라는 변수를 하나 만들었다.이 숫자대로 loop를 할것이다.

( 생각해보니 이런식이 되면 state같은 것도 다 array화 되어야하나??

moveTo는 각각의 물고기들에 어떻게 개별적으로 적용이 되지? 갑자기 헷갈리기 시작하네 )

 

 

BT에서 짜여진 AI구조자체가 array가 되어야 할거 같은데

ai 캐릭터가 하나가 아닌 여러개로 되어있는 예제를 살펴봐야 하나?

 

 

////

아니다 뭔가 헷갈렸네. 우선 fish가 스폰되는건 BT안에서의 단계가 아니고

spawnFishVolume의 이벤트그래프에서 일어난다. 이걸 먼저 loop로 바꿔보자.

 

BB에 있던 fishCount를 지우고 spawnFishVolume에 다시 변수를 생성하고 5마리를 생성해 보았다.

테스트해보니 각자가 targetPoint들을 만들며 잘 돌아댕긴다.

 

 

 

 

 

 

보기에 영 불편하여 각자의 캐릭터들이 각자의 wayPoint 컬러를 갖도록 수정해 보자.

 

BB상에서 wayPointColor 변수를 만들고 처음 이를 설정하는 task를

만든 다음에 데코레이터에서 BB에 값이 설정되어 있는지 확인하는 식으로 작동을 할수가 있을거 같다.

트리 구성.

 

저기 moveTo 뒤에 removeWayPoint 태스크도 추가 했다. remove보단

ue4에서 destroy란 단어를 써서 destroyWayPoint로 변경

destroy의 구성은 이런식이다.

대략 잘 작동한다.

 

 

 

 

 

4-1. Volume 계산

 

 

현재 wayPoint들이 wayPointVolume안에서 무작위로 생성되는데 그러다 보면 위 영상처럼

방해물 역할을 하는 Sphere들과 겹치는 경우가 생긴다.  fish가 spawn될때도 역시

Sphere들과 겹쳐 Sphere내부에서 물고기가 생성될때도 있다.

 

wayPointVolume에서 Sphere의 볼륨을 제외하고 그 제외한 볼륨에서만

spawn시킬 수 있는 방법을 찾아보자.  그냥 드는 생각은 Sphere들의 중심 위치에서 영역값을 가져와서

그안에서 spawn되면 취소 하고 다시 spawn되게 하면 되겠는데 영역끼리의 더하고 뺄수있는 방법이

없나 찾아보자.

 

못찾겠다.ㅜ 그냥 sphere 범위에 들어왔는지 확인하자. 데코레이터에 보면

cone check처럼 영역 체크하는 애들도 있던데 Sphere 영역 범위를 지원하는 노드가 없을까?

 

찾아보니 trace관련한 노드들이 많다. lineTrace는 예를 들어 캐릭터가 총을 쐈을때

일직선의 line을 그어서 line과 충동하는 물체들을 반환받는 식이다.

 

위의 sphere trace로 개념을 다시 보면 멀티와 싱글 차이는 멀치는 트레이스해서 걸리는 모든 물체를 반환

하는거고 싱글은 첫번째 물체만 반환한다. 또 스타트 엔드는 sweep( 훑는행위)가 시작되고

끝나는 직선의 양끝이다. 저 라인으로 shpere를 움직여 걸리는 물체를 반환한다는 뜻이다.

 

얘네는 게임의 플레이 도중의 반환값을 얻을 때 쓰는거 같은데 나같이 wayPoint를 spawn하는

과정에서 써도 되나?

 

암튼 이걸 써서 수정해보자.

씬에 놓여 있는 Sphere 4개의 리스트를 가져와야 하는데 UE4에는 그냥 간단하게

outliner에서 찍어서 list화 시키는게 없나??..  매번 불편하네.

 

/////

근데 막상 다시 보니 trace는 해당하는 타입의 오브젝트가 걸리는지 확인하는 애들이다.

내 경우에는 특정 위치가 sphere에 포함되는지 체크해야 하는데 그런데 쓰기엔 좀 아닌거 같다.

 

그냥 wayPoint를 위한 랜덤 location을 생성했을때 그 위치가 구와 얼마나 떨어져있는지

distance를 구해서 구현하자..   그냥 결국 원시적인 코드 구현방법이다.

원시적인게 짱.  이제 안찾아볼란다. 시간만 아깝네.

 

vector subtraction노드와 vector length를 연결하여 거리값을 구하면된다.

 

......................................................................................................................................................

 

get all actors of class에서 static mesh actor들을 모두 가져와서

actor has tag로 block이라고 태그값을 준 sphere들만 따로 뽑아내려고 했다.

근데 태그를 달아도 인식하지 못한다.

 

이게 애매한게 지금은 그냥 sphere mesh를 화면에 배치했다.

그러면 static mesh actor가 된다.  하지만 만약 blockSphere라는 블루프린트를 만들고 화면에

배치한다고 해보자. 그러면 블루프린트안에서 static mesh를 만들고 거기에 tag를 달도록

되어있다.

 

즉 내가 sphere에다가 넣는다고 넣던 태그들이 전부 actor기준이 아닌 component에 들어가

있는거 같다. 그래서 aactor has tag노드로 검색해도 한걸리는듯??

 

// 아,  찾았다

태그탭이 아닌 액터 탭의 아래 화살표를 눌러 확장해보면 태그가 따로 숨어있다... 엠뵹ㅁ;ㅣ낯러

관련 내용

https://docs.unrealengine.com/latest/KOR/Gameplay/HowTo/FindingActors/Blueprints/index.html

 

 

 

 

 

 

4-2. 잡다한 코딩에러 

 

물고기들의 움직임을 아주 빠르게 해서 돌려보면 wayPoint들이 지워지지 않고 쌓이는 걸

볼 수 있다.

 

에디트 뷰로 보면 물고기들이 갈수 없는 길이 막힌 부분에 생성된 wayPoint들이 지워지지 않고

쌓인 것임을 알 수 있다. 왜그럴까.

 

이게 현재 behavior tree인데 아마도

moveTo가 완료되지 못했는데 그냥 다시 spawnWayPoint로 돌아온게 아닌가 싶다.

컴포지트가 시퀀스이므로 moveTo가 실패하면 그래프가 멈춰야 되는데 왜 다시 처음으로 갔을까?

이거 원인을 쉽사리 못찾겠다 우선 그냥 넘어가자.

 

 

 

 

 

 

 

5-1. 물고기 경로 겹침

 

 

 

이제 어느 정도 정리는 됐는데

( 위에 단락에서 해결 못했던 wayPoint찌꺼기가 쌓이는 문제가 있는데 어차피 visible을 위한

임시로 만들었던 부분이라 넘어가자. ) 위 영상에서 보면 알 수 있듯이 물고기들이 경로를

따라가다 정면으로 충돌이 일어나는 경우 머리끼리 끼는 현상이 생긴다.

 

언리얼 자체에서 지원하는 AI 시스템이기때문에 군중 컨트롤에 대한 지원이

어느 정도 되어있을거 같다. 서로의 경로가 겹치면 경로를 변경한다던지 하는 지원이

있지 않을까?  군중으로 움직이는 예제를 찾아봐야겠다.

 

관련 용어로 RVO( Reciprocal Velocity Obstacle), Reciprocal Collision Aviodance

PLE 등이 있다.

 

//////////////////////////////////////////////////////////////////

아함, character movement에 보면 avoidance에 관련한 탭이 제공되어 있다.

 

acoidance weight값이 중요한거 같은데 0으로 두면 완전 로보트처럼 피해가고

1로 두면 avoidance 효과가 거의 일어나지 않는거 같다.

보통 0.5로 놓고 쓰는듯?

 

아래는 일부러 병목 현상이 생기도록 길을 좁게 만들고

avoidance weight 수치를 0.5로 준 모습인데 적어도 물고기들이 끼어서 못 움직이는 문제는

없어졌다. 물론 아직 움직임이 엉망진창이다. 

저 좁은 공간에 50마리를 풀어놓아도 어떻게든 지들이 알아서 빠져나온다.

 

 

 

 

 

 

 

 

5-2. Z이동값 추가

 

 

 

지금까지의 AI navigation은 어떻게 보면 2D를 기반으로 하고 있다. Z값의 움직임을 추가하여

물속 3차원 공간을 자유롭게 돌아다니도록 해보자.  avoidance문제도 지금보단 당연

좀 더 수월해 지겠지.

 

Character Movement에 보면 지금 기본 movement mode는 walking으로 되어 있다.

fly, swim등을 지원하므로 어떻게 달라지는지 확인해 보자.

 

/////////////////////////////////////////////////////////////////////////////

약간 다른 경우지만 homing이 있는데 보통 자동 유도장치를 homing이라고 한다.

projectile componenet를 붙여 구현한다. ( projectile 발사체 )

 

////////////////////////////////////////////

 

아무래도 UE4에서 기본적으로 제공하는 navigation mesh로는 3D navigation이

불가능한거 같다.

https://answers.unrealengine.com/questions/80869/navmesh-for-flight-sim.html

https://forums.unrealengine.com/showthread.php?63722-Flying-AI-Pathfinding

 

...  당연히 되는지 알았지..

 

관련 용어로 A* 알고리즘 같은게 있다.

(A star search algorithm)

http://ko.wikipedia.org/wiki/A*_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98

http://theory.stanford.edu/~amitp/GameProgramming/

http://cozycoz.egloos.com/9748811

 

 

관련글 더보기