50. PatrolRoute로 정찰 / 개곡선, 폐곡선
Enemy가 PatrolRoute 따라서 이동하기
BP_PatrolRoute에서 추가한 스플라인 포인트들의 위치를 받아와서 BP_Enemy가 해당 경로로 이동하도록 한다.
먼저 BP_PatrolComponent에서 Get Destination 함수를 추가했다.
Get Destination 함수
PatrolRoute 변수가 유효한지 검사를 먼저 한다.
만약 유효하지 않다면 그대로 반환하며 함수가 끝나게 되고, 유효하다면 PatrolRoute 안에 있을 Spline에서
Get Location at Spline Point로 각각의 SplinePoint 마다의 Location을 반환하도록 한다.
Point Index는 변수로 승격하여 변수(Index)로 만든 뒤, Index의 값을 증가 / 감소 시키면서 SplinePoint를 바꿔줄 것이다.
Coordinate Space는 SplinePoint의 Location을 Local / World 기준 중 무엇으로 할지 설정해준다.
마지막으로 성공적으로 Location을 받아왔는지 판별해주기 위해 반환노드에서 bResult(Boolean)을 추가하였다.
Spline Component
Spline Component로 Enemy가 정찰하는 루트를 만들었다.
해당 컴포넌트 디테일 중, Closed Loop의 활성화 여부에 따라 정찰 루트가 바뀐다.
- Closed Loop 활성화 : 마지막 SplinePoint 도착 시, 제일 처음 SplinePoint(시작점)으로 간다 - 폐곡선
- Closed Loop 비활성화 : 마지막 SplinePoint 도착 시, 왔던 SplinePoint들을 다시 되돌아가며 시작점으로 간다 - 개곡선
Set Index to Next 함수
BP_PatrolComponent에서 SetIndextoNext함수를 추가해준다.
이 함수는 GetDestination함수에서 사용하는 PointIndex를 갱신해주는 역할을 할 것이다.
먼저 bReverse(Boolean) 변수를 추가하여 Patrol이 역방향인지 아닌지를 판단해줄 것이다.
마찬가지로 PatrolRoute 변수가 유효한지 검사를 통해, 유효하다면 PatrolRoute 내의 Spline 기반으로 로컬 변수(Route)를 생성해주어 값을 초기화 시켜준다.
이후 bReverse의 변수 값에 따라 Branch로 분기를 나눈다.(True라면 역방향 / False라면 정방향 Patrol이다)
● bReverse가 False일 때
bReverse가 False = 정방향이라는 뜻이므로 Index를 ++로 증가시킨다.
그리고 Route - Get Number Of Spline Points를 통해서 Spline의 총 Point수를 가져와서 증가된 Index와 같은지 확인한다.
만약 Index와 같다면 SplinePoint 끝에 도착했다는 것이므로, Route - Is Closed Loop를 검사하여 폐곡선 / 개곡선 여부를 판단한다.
Is Closed Loop가 True가면 폐곡선으로 Index를 바로 0으로 초기화시켜 Enemy가 바로 SplinePoint 0으로 가도록 한다.
False라면 개곡선으로 왔던 SplinePoint를 돌아가야하는데, 마지막 SplinePoint에서 해당 함수가 호출되면 마지막 PointIndex에 ++ 증가된 값이 현재 Index에 있으므로 -2 감소해주어야 바로 전 SplinePoint로 돌아갈 수 있다.
이후 bReverse변수를 True로 바꿔준다.
● bReverse가 True일 때
현재 Patrol이 역방향이라는 뜻으로, Index를 -- 감소 시켜서 바로 전 Index로 보낸다.
만약 계속 감소하여 다시 시작 Point 0 으로 돌아왔다면 그때 Index는 감소된 -1값이므로, -1과 == 같은지 여부로 시작점에 도착했는지를 구분할 수 있다.
True라면 현재 Index : -1에 +2 값을 더하여 다시 정방향 Patrol로 돌려보낸다.
마지막에 bReverse는 다시 False가 된다.
BTT_Patrol 태스크 만들기
BT_Enemy_Melee에서 새 태스크 BTT_Patrol을 추가한다. 이 태스크는 Wait 태스크를 대체할 것이다.
BTT_Patrol 함수 내부에서 Receive Execute AI 이벤트를 추가 후, 함수로 변환한다.
Get Controlled Pawn으로 받아와 BP_Patrol Component를 받아올 것이다.
Get Component by Class를 변수로 승격하여 PatrolComponent 변수를 만들어 유효성 검사를 통해 혹시 값이 없다면 바로 값을 넣어주고 Is Valid를 통해 유효성 검사를 한번 더 진행한다.
만약 유효하지 않다면(Is Not Valid) 바로 Finish Execute로 종료시킨다.
유효하다면 PatrolComponent 변수의 Get Destination함수(아까 BP_PatrolComponent에서 만든 함수)를 불러 성공 여부를 판단한다.
만약 Get Destination 함수가 성공적으로 반환했다면 Move to Location을 통해 Patrol Point로 이동시킬 것이다.
* Move to Location
- 타깃 : Owner Controller(Enemy의 컨트롤러)
- Dest : Spline Point의 Location 위치
- Acceptance Radius : 지정한 값 반경 안에 들어올 시 이동 완료(변수로 승격 후, 인스턴스 편집 활성화하여 조절해줌)
- Project Destination to Navigation : 목표가 내비게이션 외부에 위치했다면, 내비게이션 상에 목표 위치가 존재하도록
목표 지점을 내비게이션에 투영해 새로운 값을 만들어냄
Move to Location에서 나온 값을 스위치를 통해 각각의 상황에 따라 결과가 나뉘게 된다.
* EPathFollowingRequestResult에 따른 스위치
- Failed(실패) : 도착에 실패했다면 Finish Execute : False로 종료
- Already At Goal(이미 도착) : PatrolPoint에 도착했다면, PatrolComponent의 Set Index to Next를 불러 다음 PatrolPoint를 지정해준 뒤 True로 종료
- Request Successful(성공적으로 요청) : 요청이 성공적으로 됬다면 True로 종료
이후 BTT_Enemy_Melee 비헤이비어 트리에서 Wait 태스크를 대신하여 BTT_Patrol 태스크를 넣어준다.
여기서 BTT_Patrol의 Acceptance Radius를 100.0으로 설정하여준다.
이제 실행해보면 PatrolRoute에 따라서 이동하는 것을 볼 수 있다.
Spline의 Closed Loop 값에 따라서 정찰 경로가 달라지는 것 또한 볼 수 있다.
Enemy 사망 시 해당 비헤이비어 트리 정지시키기
Enemy가 사망하고 Player 이동 시, 시체가 계속 Player를 주시하는 문제가 있다.
이는 사망하고도 Enemy의 비헤이비어 트리에서는 MoveTo로 계속 추격하고 있기 때문이라 사망하면 비헤이비어 트리를 꺼줄 필요가 있다.
BP_Enemy에서 AnyDamage 이벤트를 하나 추가했다. 먼저 부모의 AnyDamage(BP_Character)가 실행된 뒤, State가 Died인지 검사하여 True라면 현재 Enemy의 Get AIController의 Brain Component를 받아와 Stop Logic으로 정지시켰다.