개발일지 목록
노션 | 디시
잔말
하나의 일지인데 글이 길어져서 나눈 것이다 보니, 지난 번과 동일하게 #33이에요.
사실 어제 이어서 적었어야 했는데 적다보니 진이 빠져버린거에요.
이런 글을 적다보면 느끼지만 맞춤법을 더 맞춰야 하나
그냥 취향대로 적어야 하나 고민이 되는거에요.
여태까지 지적이 들어오지 않는 것을 보면 하나의 말투로 봐주시는 것 같아 유지중이지만요.

근데 오늘 일어나서 보니 ecs 떡밥은 나때문인가...? 내가미아내...

길찾기에 Job System 적용하기
제목 글자 수 제한으로 上/下 편으로 나누었지만,
자세히 하자면 입문/적용 편이 되지 않을까 싶어요.
이번 편에서는 드디어 제 길찾기 알고리즘이 구려서 해결하기 위한 마지막 방법,
멀티쓰레딩으로 처리속도를 높이도록 해보아요.
들어가기에 앞서 제 길찾기는 Cost Field -> Flow Field로 두 개의 과정이 있고,
Cost Field와 Flow Field 만들기 사이에 추가 과정이 하나 있어요.
추가 과정은 몬스터가 지상 타입인지, 공중인지에 따라 2가지 지상/공중 Flow Field가 존재하므로
Cost Field로부터 지상/공중 Flow Field에 동기화 해주는 과정이에요.
자 그럼 Job부터 만들어보도록 해요.
Update Cost Field
Cost Field라는 것은 맵의 타일 별 비용을 가지고 있어요.
제 Cost Field에는 이용자의 수에 따라 비용이 증감한다던지
진흙탕같은 감속 구간이 있다던지와 같은 것은 없고,
단순히 이동 가능/불가능 지점만 판단해요.
이 이동 가능/불가능 지점을 판단하는 방법은 여러가지가 있겠지만,
저의 경우에는 Physcis2D.OverlapCircle 함수를 사용하여 판단해요.
이렇게 한 이유는 Tilemap.GetTile 함수 사용하는 것도 그다지 성능이 좋지 않고,
Tilemap을 사용하지 않고 콜라이더를 부착한 오브젝트를 사용해서 장애물을 배치하는 경우도 있기 때문이에요.
하지만 여기서 문제가 발생 !
Job System에서는 Physcis2D.OverlapCircle 함수를 사용할수가 없어요.
지난 편을 보았다면 "잡 시스템은 무적이고 유니티는 신이다!" 라고 생각했을 수 있겠지만,
사실 이런저런 제약사항이 있는 거에요.
이유에 대해선 직접 알아보도록 하는거에요.
이에 대한 해결 방법으로 잡 시스템에서 제공되는 기능들이 몇가지 있는데,
SpherecastCommand라던지, OverlapSphereCommand 등의 기능들이 있어요.
Overlap시리즈는 아마 유니티 2022버전부터 사용이 가능한 것 같아요.
따라서 아쉽지만 2021버전을 사용하는 제게는 패스.
해결할 수도 있겠지만, 어짜피 지금 성능 개선을 하려고 하는 부분은
cost field가 아닌 flow field거든요.
왜냐하면 cost field는 1개만 사용하는데,
4인 플레이 * 2가지 이동타입이라면 flow field는 8개나 필요한 상황이에요.
cost field에 overlap 함수를 7000번이나 쓰면 너무 성능소모가 심할것 같다 ! 싶겠지만
어짜피 플레이어는 한번에 여러 칸을 이동하는 것이 아니다보니
내부적으로 이동한 칸 만큼 새로워진 칸만 overlap함수를 사용해 충돌체를 감지하고
나머지 칸은 배열을 옮기는 방식으로 최적화를 해 주었어요.
따라서 Job을 사용하지 않으니 핵심적인 부분만 보고 가도록 해요.

UpdateCostField 함수를 사용하여 코스트 필드를 매 프레임마다 업데이트 해줘요.
이 때 위치가 변하지 않았다면 업데이트 하지 않아요.
위치가 변하여 업데이트 시에는, 이동 거리가 범위보다 클 경우 전체 다시 업데이트,
범위 이내일 경우에는 일부 업데이트 및 옮기기 작업을 해주어요.
이 때, 좌표들은 유니티 잡 시스템과 함께 사용하기 위해 Vector2Int가 아닌 int2를 사용해 주었어요.
따라서 int2를 int index로 바꿔주기 위한 Flatten 함수도 존재해요.
Reset Flow Field Job
아쉽게도 Cost Field는 Job을 사용하지 못했지만,
Flow Field는 정말로 데이터 작업 뿐이므로 사용이 가능해요.
그 중 위에서 설명한 Cost Field로부터 Flow Field를 초기화 해주는 잡을 만들어보아요.

cost Field의 값을 갖고있는 native array 하나,
flow field에게 작성해주기 위한 native array,
지상 타입인지, 공중 타입인지를 위한 Option 값 세가지만 있으면 돼요.
이를 이용해 cost field 해당 셀의 값에 옵션 값이 들어있으면 Collider로 체크를 해주어요.
당연하게도 셀과 셀 사이 종속성이 존재하지 않으므로 ParallelFor을 사용하였고요.
이제 이것을 사용하는 방법은 아래와 같아요.

만약 플레이어가 길찾기 범위 밖에 존재하게 되는 경우에는 reset 작업을 해주기만 하고 flow field 업데이트는 하지 않아요.
그렇게 되는 경우 flow field에는 초기값만 존재하게 되고, 적들은 초기값일 경우 마지막 방향을 유지하도록 해주었어요.
Update Flow Field Job
드디어 이번 일지의 핵심적인 부분이에요.
먼저 각 타일들을 Cell이라는 구조체로 만들어 거리와 방향을 담아주었어요.

그리고 Update Flow Field Job인데,
경로 설정을 해주는 부분은 잡작업이고 이쁘지도 않으므로 생략.
큐에다가 인접 셀을 접근해서 처음 접근할 시 enqueue해주는 작업을 반복하며
BFS의 방식으로 Flow Field를 만드는 작업이에요.
relative Point라는 것은 해당 flow field를 가진 플레이어의 위치라고 생각하면 될 것 같아요.

결국 위 reset job과 이어져서 아래와 같은 flow field update 함수가 완성되었어요.

활용
그럼 이제 위의 Job들을 활용해보아요.
길찾기를 시작하면, 매 프레임마다 Cost field를 업데이트 할 수 있는지 체크하여 업데이트 해주고,
각 플레이어마다 플로우 필드 클래스가 하나씩 할당되어 있는데,
요것의 위치가 변경되었거나 코스트 필드가 변경된 경우 flow field를 업데이트 !
마지막으로 위 잡들이 종료되었다면 메인쓰레드를 진행해요.

완성 !
이렇게 해서 완성 !
그럼 다들 성능이 얼마나 개선되었는지 궁금할거에요.
그 전에 결과물 한번 확인하고 가도록 해요.

용량 때문에 사이즈를 많이 줄여버렸는데, 언덕 아래를 보시면 화살표가 바뀌는걸 더 잘 보실 수 있어요.
참고로 공중형 적은 언덕을 무시하고 날아다닐 예정이에요.
두가지 Flow field를 아래 짤에서 확인하실 수 있어요.

마지막으로 대망의 4인 플레이 !

8개의 플로우 필드가 아주 잘 적용되는 것을 확인할 수 있어요.
성능
마지막으로 성능이 얼마나 개선되었는지 빌드 후 확인해보아요.
먼저 기존 시스템으로 실사용할 약 7000개의 타일기준으로
코스트 필드 + 플로우 필드 * 4인 플레이 * 2가지 이동타입을 갱신하는 경우
약 4ms의 소요시간이 발생해요.

그럼 이제 개선된 작업을 확인해보아요.
조건은 위와 동일.

0.5ms 소요.

0.5ms 소요.

4ms -> 0.5ms






엄청나게 많이 줄어든 것을 확인할 수 있어요.
하지만 우린 한가지 간과한 것이 있어요.
바로 코스트 필드 작업은 잡을 사용하지 않았다는 것.
두 소요시간에는 코스트 필드 작업이 깔려있으므로
실제로 줄어든 시간은 90%이상이라는 것.
결론
유니티 잡... 사용할수 밖에 없는 시스템이에요.
물론 이런 대량의 데이터 처리가 아닌 이상 굳이 안써도 컴퓨터 성능은 뛰어나니 걱정할 필요 없지만요.
다음에도 이런 생산적인 주제가 일지 주제가 되면 좋을 것 같아요.
그럼

근데 디시 사진 첨부 왜이리 안되는거임?
댓글 영역
획득법
① NFT 발행
작성한 게시물을 NFT로 발행하면 일주일 동안 사용할 수 있습니다. (최초 1회)
② NFT 구매
다른 이용자의 NFT를 구매하면 한 달 동안 사용할 수 있습니다. (구매 시마다 갱신)
사용법
디시콘에서지갑연결시 바로 사용 가능합니다.