상세 컨텐츠

본문 제목

pond 프로젝트 08 - AI 구성

Unreal Engine 4/Project

by hwano 2015. 4. 16. 14:54

본문

 

Fish AI 구성

 

 

강좌를 보고 idle 애니메이션만 적용시켜놓은 상태다.  이 상태로 우선

AI구성을 하여 물고기가 연못 안쪽을 돌아다니도록 해보자.

앞서 페이지에서 잠깐 생각해봤듯이.

 

state

 

Swim

Eat

Search

 

이렇게 기본적인 설정을 하면 어떨까 한다.

 

Swim  -  dault 상태

Eat  -  먹이를 주는 손을 발견하여 이동하고 먹이를 먹는 상태

          ( 필요하면 먹이를 주는 손을 발견한 detect도 있어야 하지 않을까? )

Search  -  먹이를 주던 손이 사라졌을때 잠시 그 손을 다시 찾는다.

 

 

 

 

 

 

Swim 구성

 

 

기본적인 디폴트 상태이며 물고기의 평소 행동을 비슷하게 재현할 수 있는 알고리즘이어야 한다.

1. 물고기가 생성되면 랜덤한 방향으로 움직인다.

2. 변수 curiosity를 하나 설정하여 이 값을 랜덤하게 부여한다.

3. 시간이 지나면 변수값이 차츰 줄어들도록 한다.

4. 변수값이 0이 되면 랜덤한 방향으로 회전을 한다.

5. 벽을 만나면 무조건 회전한다.

 

curiosity 변수환경맵을 설정해 보는것도 괜찮을거 같은데 우선 여기까지만 진행하도록 하자.

 

 

 

 

 

 

 

1-1. 물고기가 랜덤한 위치에서 생성된다.

 

 

이건 예전에 찾아봤던 강좌를 따라하자.

https://www.youtube.com/watch?v=9-7SmkasS_A

Cow가 떨어졌던 강좌인데 이 방식대로 SpawnFishesVolume이라는 액터를 하나 만들어서

랜덤한 위치에 물고기가 생성 되도록 하였다.

 

이렇게 만들어진 spawn들의 목록을 받을 변수 array를 하나 생성한다. spawnedFishes라고 이름을 지었다

여기서 변수 타입이 중요할 텐데 변수 타입을 fishCharacter 클래스로 정해야한다.

마야 플러그인 만들때 C++에서

MVector  n;  
MVector  t; 
MVector  c;

이렇게 클래스를 상속받은 객체를 만드는 과정인거다.

 

 

 

그런 다음 spawn된 애들을 변수에 집어넣는다.

( 근데 변수인건가 객체인건가? 명칭이나 개념을 오래되서 다 잊어뿌렀네 )

저 add는 변수를 array형으로 만들어야만 나온다.

 

 

migrate

원래 강좌에서 보면 떨어뜨린 cow들을 특정키를 눌러 다 폭파 되도록 만든다.

그때 다른 프로젝트에서 파티클어셋을 하나 가져오는데 export하고 import하고 뭐 그러는게 아니고

우클릭 / migrate라는 메뉴로 옮겨갈 경로를 바로 지정해줄 수 있다.

 

이제 이걸 AI 시스템인 Behavior Tree쪽으로 가져가서 움직이도록 해야할텐데

어찌 해야할까

 

 

 

 

 

 

 

1-2. 생성된 물고기가 움직이도록 해보자. 

 

 

Unreal Engine 4 AI Behavior Tree & NavMesh 라는 강좌를 보면

패트롤시 BT의 task중 기본노드인 moveTo를 이용하여 다음 타겟까지 이동하도록 만든다.

moveTo는 캐릭터 클래스 상속 액터라면 자동적으로 움직일수 있도록 만드는거 같다.

타겟은 BB에 지정해 놓고 그 blackboard key값을 moveTo로 불러와서 쓰는 방식이다.

 

하지만 이것은 정해진 target 위치를 지정하고 그곳으로 actor를 이동시키는 방식이고

내 경우에는 물고기 actor가 생성되면 그 물고기 actor가 바라보고 있는 방향(vector)를 기반으로

움직임이 생성되어야 한다. 그래서 저 방법을 바로 적용할 수가 없다.

 

 

두번째로, Unreal Engine 4 AI Behavior Tree & NavMesh 강좌에서 적을 발견하면

BTT_botMoveToEnemy라는 적 위치로 이동하는 task 블루 프린트가 있다.

얘도 moveTo와 거의 비슷한 방법이다. moveTo는 BT에서 바로 작동하는 task이지만

moveToActor라는 task블루프린트 안에서 작동하는 비슷한 노드를 사용한다.

 

역시 적의 위치값을 받아와서 그 위치로 이동하게 한다.

 

-----------------------------------------------------------------

 

즉, 사고 Brain의 자아가 제3자의 위치에 있다. 얘와 얘가 얼마나 가까이에 있고, 어느 범위에

들었으니 얘를 얘 위치로 옮긴다는 식.

 

나는 좀더 fishCharactor 입장에서의 Brain으로 구성해보고 싶다. 내가 특정한 타겟을 두지 않고

앞쪽으로 나아가는데 궁금증 수치가 떨어지면 랜덤하게 방향을 튼다는 방식으로..

 

 

-------------------------------------------------------------------

 

생각해보니 그럴꺼면 그냥 add local transform 으로 현재 vector값을 구해서 조금앞쪽의 값을

더하는 식으로 구현하면 될꺼같다.

 

 

 

근데 외부환경적 선택이 나을지 각 객체기준 선택이 나을지 확실하게 결정을 못하겠다.

외부환경 두뇌로 작업을 한다면 moveTo같은 여러가지 기본 default기능들을 쓸 수 있고

character class의 기본적인 움직임 (jiggle이나 delay같은 기능들)을 쓰기 편할거 같다.

객체기준이 되면 물고기가 회전을 한다던가 할때 곡선 움직임같은걸 전부 내가 구현해야하니

일이 많지 않을까?

 

 

 

 

 

 

 

 

1-3. 외부환경 or 객체기준

 

 

두가지 경우에 대해 진행과정을 상상해보면.

 

외부환경

 

기본적으로 moveTo 개념이다. 즉 A물고기에게 가상의 target을 랜덤하게 지정해준다.

그 타겟은 시야각 180안쪽으로 제한을 둔다면 너무 갑작스러운 움직임을 제한할수 있다.

그 타겟으로 이동하는 도중 (moveTo) 어떠한 벽이나 상대 물고기같은 충돌객체를

만나게 되면 그 즉시 타겟은 변경이 되고 물고기는 방향을 바꾼다.

 

 

객체기준

 

항상 자신의 vector를 기준으로 앞쪽으로 나아가도록 한다. 이동하는 시야반경으로

상대 물고기나 벽같은 충돌체가 나타나면 vector를 큰 방향으로 변화시킨다.

 

뭐, 생각해보면 딱히 둘다 어려운 부분이나 문제될 부분은 없는거 같다.

아무 방식이나 상관없을거 같긴한데 사실 생각해보면 객체기준이 아닌 외부환경 방식이이

좀 더 프로그래밍적이다. 1번 방법으로 가자.

 

 

 

 

 

 

 

1-4-1. 추가 예제

 

 

Unreal Engine 4 AI Behavior Tree & NavMesh 강좌의 씬파일을 다운받을려고 했는데

같은 사람의 추가 AI관련 강좌들이 있어 써먹을게 있는지 훑어보자.

https://forums.unrealengine.com/showthread.php?25073-UPDATED-11-30-A-I-Templates-Bot-Car-amp-Flying-AI

 

강좌 중에 자동차AI가 있다. 플레이 해보면 자동차가 정해진 루트를 달리는거 같아서

spline같은걸로 길을 지정해 준지 알았는데, 이것도 프로젝트를 보면 그냥 미리 target을 심어놓고

순차적으로 이동하는것 뿐이다.

 

근데 재미있는건 자동차는  특성상 방향이 직선으로 바로 꺾이지 않기 때문에

내가 들이받아서 AI차량이 경로를 이탈하게 되면 지 스스로 곡선으로 멀리 돌아오거나

후진을 하여 방향을 새로고침해서 다시 타겟방향으로 달리게 된다.

 

아까 물고기 움직임을 구현하려고 할때 물고기 역시 방향전환이 완만한 곡선을 그리는데

그걸 회전변수를 주어 일일이 곡선의 움직임처럼 만들려고 했었다.

(마치 내 pond프로젝트씬의 카메라 tension 설정처럼.)

 

하지만 차량이라는 클래스가 게임 내부에서 지원이 되니 사용자가 그냥

target을 찍어주면 자동차 캐릭터는 자신의 무브먼트대로 이동하게 된다.

 

이런 부분을 잘 확인하여 작업해야할거 같다.

전문 프로그래머가 아니다 보니 아무래도 매우 비효율적인 방식으로 사고를 하게 된다. 

 

 

 

 

 

Flying AI 프로젝트도 있는데 이건 구성방식이 조금 다르다. BP안쪽 내용이 워낙 복잡하여

정확하게 파악이 되지는 않는다. 대략 훑어본바.

BT를 이용한건 아니고 비행기 BP안에 스크립트로 움직임이 구성되어 있다.

 

위에서 내가 1-2에서 막역히 생각했던 방식으로 add local transform 비슷한 노드인 add actor local offset

으로 이동을 한다. 하지만 이동하는 트리거가 되는건 객체의 사유가 아니고 역시 target을 정해 놓는

방식이다.  이게 윗 문단에서 생각한것처럼 자동차 클래스의 움직임 방식을 정해놓은 클래스와

+ 타겟 지정을 하나의 BP에 몰아 넣은 걸로 보인다.

 

 

 

 

 

 

 

 

1-4-2. BT에 대한 추가 스터디

 

 

CarAIProject와 FlyingAIProject 두 샘플에 대한 좀 더 자세한 파악을 하려고 하는데,

그에 앞서 BT의 개념과 각 노드구조에 대한 이해가 부족한거 같아서 추가 스터디가 필요한거 같다.

네이버와 구글에 한글정보를 좀 더 찾아봤다.

 

한글로 정보를 공유해 주시는 이름모를 모든 분들께 다시 한번 감사의 말씀을 드리고.

http://blog.naver.com/aigis21?Redirect=Log&logNo=220091799765

http://blog.naver.com/devdeepblue?Redirect=Log&logNo=220285521450

블로그 글들을 더 읽어봐야겠다.

 

FSM

 

AI구현 방식 중 많이 쓰는 다른 방식이 FSM라고 한다.

FSM (Finite State Machine)이고 한글로 유한 상태 기계라고 표현한다.

참고페이지

http://blog.naver.com/stingerz?Redirect=Log&logNo=90161141171

 

FSM은 게임에서 사용되는 전통적인 인공지능 기법이다.

State가 일정갯수로 정해져있으며 외부로 부터 입력을 받아 자신의 상태를 변화시킨다.

몬스터가 돌아다니다가 플레이어를 보고 따라와 공격을 한다면

몬스터는 search, detect, attack의 세가지 상태로 구성된 기계라고 볼 수있다.

 

FSM를 사용하는 이유는 위와 같이 코드가 아닌 도표로 나타냄으로서 이해가 쉽고, 각각의

상태로 나누어져 있기 때문에 추가 삭제하기 편하다.

 

 

 

 

게임 인공지능에 대한 설명은 게임 인공지능 최신 연구 동향이란 PDF를 참고하는게 좋겠다.

http://cilab.sejong.ac.kr/home/lib/exe/fetch.php?media=public:paper:kiise_2013.pdf

 

위 pdf에 소개된 FSM를 표현한 도식

FSM은 복잡해지면 위 그림처럼 계층적 FSM의 단계로 넘어가게 되면서 한계를 가질 수 밖에 없다고 한다.

BT는 그러한 해결법 중 하나. (BT가 더 뛰어나다기 보다는 다른 특성을 가진 AI 구현방식의 한 종류

정도로만 생각하는게 좋겠다 )

 

 

 

 

 

 

 

 

 

1-4-3. 캐릭터의 작동방식 파악

 

 

1-4-1까지의 내용을 정리해 보면

 

1-3에서 생각했던 외부환경에 의해 이동경로를 결정할것이냐? 객체 스스로 사고하여 결정할 것이냐?

가 사실 모두 필요한거 같다.  좀더 정확히 표현하면 2번째 객체기준 판단의 경우는 처음에는

tick단위로 매번 내 경로를 탐색하는 실시간 개념으로 생각했던거다.

하지만 이동경로를 결정한 후( 혹은 target으로 외부요인에 의한 결정이든 ) 내가 거기까지 어떻게

가야 할것인지 객체기준의 알고리즘이 필요하다고 봐야겠다.

 

저 경로까지 가는데 중간에 장애물이 있으면 어떤 방식으로 피해가야하는지

나는 자동차나 비행기 캐릭터처럼 방향을 꺾는데 제약이 있거나 후진, 혹은 Strafe가 가능하다면

몸의 제어를 어떻게 할것인가에 대한 객체기준의 코딩이 필요

 

물론 언리얼의 Character Movement 컴포넌트에서 walking swimming flying등 다양한 움직임을

지원한다. 이러한 기능들의 범위가 어디까지인지 파악을 하여 쓸 수 있으면 그냥 가져다 쓰는

정도로 가능하겠지

 

 

 

 

CarAIProject

 

작동방식을 좀 더 정확하게 파악해보자.

우선 sedan이라는 이름을 가진 자동차 블루프린트를 보자.

 

parent class가 character가 아닌 WheeledVehicle이다. 따라서

characterMovement 컴포넌트가 아닌 VehicleMovement 컴포넌트를 기본적으로 가지고 있다.

난 지금까지 캐릭터컴포넌트의 옵션을 만져 자동차모드로 쓰는 건지 알았네.

자동차는 기본적으로 따로 제어할수 있는 기능을 제공한다.

 

 

이벤트 그래프쪽을 살펴보면 특별한거 없이 입력값을 집어넣는 노드구조이다.

대신 vehicle클래스라서 set steering input같은 특정한 노드들이 보인다.

 

앞서 봤던 sedan은 사용자가 직접 조종하는 자동차이고 AIsedan이라는 거의 동일한 블루프린트가 있다.

이 AISedan은 SedanController라는 AIController Class가 지정되어 있다.

이 SedanController 안에서 그림처럼 dedanTrackRun BT를 작동 시킨다.

 

 

BT의 구조는 매우 단순하다.

 

 

BTT_MoveTo라는 task노드를 만들어 location으로 이동하게 하며

update route라는 데코레이터가 달려 있다.

 

BTT_MoveTo라는 BP안쪽을 보면 Cast to 노드들이 많이 보인다. 이에 대한 내용은

https://docs.unrealengine.com/latest/KOR/Engine/Blueprints/UserGuide/BlueprintCommsUsage/index.html

https://docs.unrealengine.com/latest/KOR/Engine/Blueprints/UserGuide/CastNodes/index.html

이쪽을 참고하자. cast to는 형변환으로 번역되어 있다.

 

번역이 어색한건지 내용이 어려운건지 한번 대충보는걸로는 이해가 안된다.

 

 

 

블루프린트 설명
Character (1) Get Player Character 노드가 사용되어 MyCharacter 라는 캐릭터 블루프린트로 형변환하고 있습니다.
PlayerController (2) Get Player Controller 노드가 사용되어 MyController 라는 플레이어 콘트롤러 블루프린트로 형변환하고 있습니다.
Game Mode (3) Get Game Mode 노드가 사용되어 MyGame 이라는 게임 모드 블루프린트로 형변환하고 있습니다.
Pawn (4) Get Controlled PawnGet Player Controller 노드가 사용되어 MyPawn 이라는 폰 블루프린트로 형변환하고 있습니다.
HUD (5) Get HUDGet Player Controller 노드가 사용되어 MyHUD 라는 HUD 블루프린트로 형변환하고 있습니다.

 

큰 개념은 블루프린트간의 통신이라고 보면 된다. 캐릭터가 불꽃의 영역안으로 들어간다.

그러면 불꽃이 캐릭터에게 체력 게이지에 변화가 생겨야 한다고 전달해야한다.

이러한 통신과정에서 필요하다고 한다.

 

블루프린트의 통신방법에는 직접통신도 가능하다. 레벨에 배치된 인스턴스된 액터들끼리는 바로

통신하는게 가능하다. 하지만 주인공 캐릭터가 여러맵을 거쳐 현재의 레벨에 들어온다고 생각해보자.

그러면 그 시점 이점까지는 캐릭터는 맵에 존재하는게 아니기 때문에 타겟으로 정할수 없다.

 

이러한 문제점들때문에 cast to가 필요한 것이다. 근데 역시 개념잡기 어렵다...

----------------------------------------------------------------------------------------------

 

이 정도 훑어봤으면 노드들을 열어봤을때 다 이해가 될줄 알았는데

아직도 봐도 전혀 모르겠다 허허.

 

 

 

 

AIProject

 

AIProject라는 강좌에 있던 샘플도 한번 봐보자. 경비병이 경비루트를 따라 돌다가 캐릭터

(조종하는 메인 캐릭터)를 발견하면(시야에 들어오면) 공격을 하며 따라오는 예제이다.

 

지금 파악이 안되는게 wayPoint들을 값을 읽어서 BT에서 그 위치로 이동 시키게 하는 과정이다.

AIProject에는 다른 프로젝트에는 없던 인터페이스 블루프린트가 있다.

 

프로그래밍에 등장하는 interface라는 개념이라고 한다. 전구 소켓에 일반 전구와 삼파장 전구를

모두 꼽을 수 있는 것처럼 정해진 규격을 얘기한다고 하는데

 

실제적으로 말하자면

FBX.save()

OBJ.save() 이런식이라고 해보자.  그럼 사용자는 각 다른 확장자 클래스지만

저장한다는 .save() 동일하게 쓸수 있다. 뭐 대충 이런 개념인듯.

그냥 개념만 보자면 저렇다는거고 실제론 좀 더 복잡한 내용이다.

 

carAI프로젝트에서는 데코레이터에서 updateRoute를 하여 타겟 wayPoint들을 지정했는데

여기서는 task에서 nextRoute를 만들어 지정한다.

 

왜냐면 자동차는 wayPoint들을 주욱 나열하여 트랙을 만들었던 방식이기 때문에

도착했는지 확인만 하면 되었었고 여기서는 attack이 끝나면 자동으로 next가 실행되어야 하기 때문에

조금 다르다. 내 경우는 이쪽에 더 가깝겠다.

 

// 아, 아니구나. updateRoute는 여전히 moveto 태스크 위에 있다.

 

 

 

 

 

 

 

 

1-5. wayPoint를 지정하여 물고기가 움직이도록 해보자 

 

 

한정없이 샘플만 들여다 보고 있다고 이해가 되질 않으니 가져와 적용하면서

진행해보자. 원래 랜덤 location값이 생성되고 물고기가 그쪽으로 움직여야 하겠지만 우선

wayPoint 두개를 찍고 그 사이를 왔다갔다 하도록 해보자.

 

봐도 어렵다 어려워.

 

 

연못안에 두개의 point를 찍었고 BB에는 key값들을 추가해 주었다.

State - enum형   fishState를 연결

TargetPoint - vector형

Route  - float형

 

AIProject의 일부분인데 나는 이 패트롤 부분들만 구현하면 된다.

nextRoute와 UpdateRoute, MoveTo 부분만 구현해보자.

 

 

 

----------------------------------------------------------------------------------------------

 

 

BTT_nextRoute

 

 

먼저 변수를 설정한다.

 

Blackboard Key타입의 TargetPoint와 Route는 BB에 설정한 그대로

여기서도 똑같이 만들어준다. 두값을 BB에서 가져와 쓸것이다.

MaxRoute는 float로 만들었다.

 

옆에 눈모양을 켜줘야 'editable'이 가능하게 되어 BT상에서도 수정할거나

다른 값을 받아들일수있다.

먼저 event begine과 같은 의미인 event receive execute(실행) 노드로 구문을 시작한다.

BB의 route값을 받아와 maxRoutes( 여기서는 0으로 설정)보다 큰 값인지 확인한다.

 

더 큰값이면 다시 BB의 route를 0으로 맞추고 ( 이건 아직 왜 하는지 모름 )

get all actors of class노드로 wayPoint BP로 씬에 인스턴스해둔 wayPoint들을 가져온다.

(wayPoint Class를 상속받은 inctance들을 array로 가져왔다는것을 잊지말자)

 

이렇게 가져온 wayPoint inctance들을

 

forEachLoopWithBreak노드에 넣고 한바퀴 돌려준다.

comleted에는 finish execute라는 노드를 달아 success에 체크해준다. BT상의 task에서

composite가 필요로 하는 값을 출력하게 해준다. return정도의 의미인것 같다.

array라 노드 이름에 each가 들어간다.  forLoopWithBreak가 따로 있다

forEachLoopWithBreak는 들어온 array들을 한번씩 loop를 돌려주는 거고

(array로 들어온 객체들을 loop를 돌린다는 뜻)

forLoopWithBreak는 fisrt index부터 last index까지 그냥 반복 excute값을 발생시킨다는 뜻이다.

 

withBreak가 안붙은 그냥 루프도 있는데 원래 break가 붙은건 break값을 받기 위해서 쓰는거다.

근데 저기선 쓰지도 않는데 왜 forEachLoopWithBreak를 썼는지 모르겠다.

아마 그냥 큰 의미 없으나 나중에 혹시 쓰일지도 모를 경우를 대비에 forEachLoopWithBreak를

쓴걸로 추측된다.

 

 

 

또 여기서 중요한게 노드를 검색할때 cast to waypoint를 입력하면 그림처럼

wayPoint와 wayPoint Class 두개가 검색된다.

헷갈리지 말아야 할게 위에 꺼는 wayPoint라는 클래스를 상속받은 인스턴스 객체들

각각에 접근할때 쓰이는 거고 아래 아래꺼는 wayPoint라는 클래스 자체에 접근하는 노드이다.

 

또하나 궁금증이 생기는 거는 cast to wayPoint노드를 만들고 라인을 연결하기전 모습을 보면

object란이있다. 왼쪽의 array element와 object를 연결하는 건데

 

이미 노드이름에서 "wayPoint"라는걸 명시하고 있다. 꼭 굳이 cast to ~~처럼 각각의 클래스이름으로

노드들이 필요한 이유가 뭘까?  그냥 cast to class 이런식의 노드를 만들고 옵션에서 원하는 클래스를

선택하도록 했으면 안됐던 걸까?  아마 제목에 클래스이름이 명시되어야 가독성이 높아지기 때문인걸로

추측된다.

 

예를 들어 wayPoint말고 FakeWayPoint라는 블루프린트를 새로만들었다. get all actors of class

노드로 fakeWayPoint들의 어레이를 가져와 똑같은 연결을 만들어 저 cast to wayPoint의

object 부분에 연결해 보았다. 

 

노드 이름에 명시된 wayPoint가 아닌 FakeWayPoint의 인스턴스 객체를 연결한 것이다.

그상태로 컴파일을 해봤는데 에러가 나지 않는다. 아마 개인적으로 추측하건데 cast to ~라는

노드들은 제목만 다를뿐 같은 노드라고 생각된다. 개별 이름만 붙었을뿐 사실 다른 클래스로

cast to 해도 아무 상관없지 않을까 생각된다.

 

////

 

wayPoint 블루프린트에서 order라는 float형 변수를 하나 만들어주고 editable시켜놓는다.

값은 디폴트 0 값으로 세팅

루프돌리는 class inctance 오브젝트의 order값과 route의 값이 같은지 확인한다.

맞으면 navMesh상의 위치값을 targetPoint 변수에 넣어준다.

 

 

 

 

 

 

 

 

 

 

----------------------------------------------------------------------------------------------

 

BTD_updateRoute

 

 

이 데코레이터의 구성은 단순한데 뜻을 잘 모르겠다.

switch on EBTNodeResult에서 succeeded면 BB의 route값을 1올리는 건데

event receive execution finish와 switch on EBTNodeResult 가 무슨 뜻일까???

 

데코레이터의 컨디션이 inverse되어 있는것도 확인하자. 역시 뜻은 모르겠다.

우선 넘어가는 걸로..

 

 

 

 

 

---------------------------------------------------------------------------------------------

 

FishAI

 

 

캐릭터를 만들때 character BP, Player Controller BP, gameMode BP를 세트로 만든다.

fish도 그렇게 했었는데 자세히 보니 이 경우에는 플레이어가 빙의되어 컨트롤 하는게 아니고 순전히

AI로만 움직이는 애들이다. 때문에 Player Controller가 아닌 AIController BP로 만들어야 한다.

 

수정하여 이름은 FishAI로 하였다.

 

 

그 뒤 BT가 작동하지 않아서 방식을 조금 바꿨다. 내 경우에 fish가 화면상에 존재하지 않다가

플레이 시 spawnFishesVolume에 물고기가 랜덤하게 생성되지만 샘플에서는

이미 씬에 fish어셋을 배치하여 놓았다.

우선 나도 spawnFishesVolume을 잠깐 작동중지 시켜놓고 화면에 fish를 하나 배치하여 진행하자.

 

----------------------------------------------------------------------------------------------

 

 

 

 

 

 

플레이를 시켜봤지만 정상적으로 작동하지 않는다.  원래 nextRoute에서는 한 프레임만 머물고

바로 moveTo노드로 넘어가야 하지만 내 경우엔 계속 nextRoute쪽에서

다음 노드로 실행이 넘어가질 않는다.

 

 

 

 

다시 AIProject 샘플을 비교해서 잘못된 부분을 찾아보자.

 

위에서 route가 maxRoutes보다 큰 값인지 확인하고 더 큰값이면 다시route를 0으로 맞추는 과정을

왜 하는지 모르겠다고 적어놨다. nextRoute 태스크 내부에서 maxRoute는 그냥 0.0으로 설정되어 있어서

왜 그런지 몰랐는데

 

 

다시 샘플의 BT를 보니 노드 외부에서 maxRoute값을 설정해 주었다. 보통 값을 설정할때 노드 내부에서

값을 바로 지정해 주지않고 변수를 editable로 만들어 외구 BT상에서 저렇게 바로 설정하는거 같다.

 

 

 

AIProject 샘플의 nextRoute 노드 구성은 단순하다. ->

 

현재의 route값을 확인한다. 다만 route값이 maxRoute로 지정한 수를 넘었으면 0으로 내린다.

그 route값과 같은 order를 가진 wayPoint의 위치를 targetPoint에 집어넣는다.

 

이거다. 이제야 좀 정리가 되네 order는 wayPoint 블루프린트에서 변수를 만들어 놓고 

wayPoint들을 화면에 배치한 후 디테일탭에서 순서대로 숫자를 넣어준다.

 

 

 

이러한 구조인데 왜 nextRoute에서 넘어가질 않을까?  상위 컴포지트가 sequence라

nextRoute노드의 실행이 실해했다고 실행이 중지된건가. 아님 루프에서 빠져나오질 못하고 있는건가.

음..  쓰고 나서 보니 후자인거 같다.

 

AIProject 샘플에서 중간에 printString노드를 끼워넣어보았다. 잘 작동한다.

 

하지만 내 프로젝트에 똑같이 해봐도 프린트가 작동하지 않는다 루프가 제대로 진행되지 않는다는

건데 왜일까???

루프실행문뒤에도 프린트를 붙여봤다. 역시 샘플에서는 잘 작동하는데 내껀 작동하지 않는다.

 

루프자체가 작동을 안하는거 같다.

뭐야 아예 노드가 실행자체가 안되고 있는건가?

 

내 프로젝트에서 노드 실행 이벤트뒤에 프린트스트링을 붙였다.

이건 또 작동한다.  어딘가 노드연결이 끊긴거 같다.

아 -_-;;   위에 그림에서 branch false 연결이 빠져있구나....  이제야 제대로 작동된다.

물고기가 무슨 슈퍼맨처럼 wayPoint사이를 날아다니기 시작했다. ( max walk speed를 줄여줬다..)

 

-----------------------------------------------------------------------------------------------

 

 

 

EBTNodeResult

 

 

이제 위쪽에서 이해가 안갔던 updateRoute쪽을 다시 봐보자. 트리는 이전 nextRoute에서

성공을 반환받았다. ( 혹은 sequence자체가 현재 성공이다. 자식 노드단계에서의 성공인지

composite 단계의 성공을 말하는건지는 잘 모르겠다 ) 암튼 성공을 반환받았으므로

(아래 그림에서의 receive Execution finish ) node Result는 성공상태인거다.

 

그것을 다음 노드 EBTNodeResult로 받아서 현재 succeeded 상태이면 그에 맞는 상황을

Failed 상태이면 그에 맞는 상황을 실행한다는 의미인거 같다.  현재 앞에서 성공이었기 때문에

route를 1더하는 것을 실행해주면 된다.

 

이렇게 이해하면 쉬우나. BT에서 updateRoute를 찍어보면 condition inverseCondition에 체크가 되어있다.

이걸 이해를 못하겠다.

로직을 생각해보면 updateRoute는 데코레이터다. 따라서 어떤 상황 판단 기점을 만들어

만약 true이면 다음노드인 moveTo가 실행되어야 할거다.

하지만 이 경우엔 updateRoute에서 무조건 루트를 1더해주는거 말고는 하는일이 없다.

~일때 성공이다. 라고 반환해주는게 없이 그냥 route만 1 더해서 업데이트 하고 말았으니

계속 false상태이고 그 false 상태를

inverse Condition하여 무조건 true상태로 만들어 MoveTo로 넘어가게 만든 것일까??

 

만약 그런 용도라면 그냥 데코레이터가 아닌 업데이트로 만들 될텐데

굳이 moveTo에 데코레이터로 붙인 이유가 무엇인가??

 

여긴 잘 모르겠다. 우선 넘어가야 할듯.

 

 

 

 

 

 

 

관련글 더보기