프로덕트

아직 프로덕트가 없습니다.

아티클

전체 보기
민서

민서

생성/파괴 vs Object Pooling

들어가며

Instantiate와 Destroy는 Unity에서 제공하는 오브젝트 생성, 파괴를 담당하는 코드입니다. Object Pooling은 이러한 생성과 파괴 비용이 비싸기 때문에 대안으로 나온 최적화 기법입니다. Object Pooling의 경우는 자료구조 Queue만 알고 있어도 원리를 알고 사용할 수 있다는 장점 덕에 Unity를 입문하시는 분들이 가장 먼저 배우는 최적화 기법이기도 합니다. 그래서 저는 저번 주에 이 두 가지 방식을 직접 구현해보고 비교해봤습니다. 근데 생각치도 못한 난관에 생각보다 오래 걸려서 어제 작성해야 했는데 오늘까지 분석해보고 이제서야 결과를 내게 되네요. 지금까지의 생각보다 험난했던 과정을 여러분들께 소개해드리도록 하겠습니다.

Object Pooling이 나오게 된 배경

C#의 주목할만한 특징 몇 가지 중에 재밌는 특징이 있습니다. C#은 Garbage Collector라는 친구가 있어서 아무도 안쓰는 메모리가 있다면 알아서 수거해가는 친구인데요, 이 덕분에 C++ 처럼 메모리 관리를 안해줘도 괜찮다는 장점이 있습니다. 하지만 단점은 개발자가 메모리 관리를 할 수 없어요(?). Garbage Collector가 작동하는 방식은 프로그램을 멈추고 메모리를 회수한 뒤에 다시 프로그램을 재개하기 때문에 멈추고 재개하는 그 과정에서 프레임 드랍이 일어납니다. 소위 랙이라고 불리는 현상이 나타나죠. 그렇기 때문에 개발자들은 가비지가 안나오게끔 코드를 짜는 방식으로 발전해옵니다. 메모리 관리를 하지 않게 하기 위해 Garbage Collector를 만들었는데 정작 개발자들은 Garbage Collector가 사용되지 않게끔 코드를 짜는 아이러니한 일이 일어나고 있습니다. 그 중 하나가 Object Pooling입니다. Object Pooling은 간단합니다. 오브젝트를 제거할 때 이러한 가비지가 나오는 것이 거의 확정이기 때문에 미리 만들어두고 Queue에 넣어둔 뒤 필요할 때 꺼냈다가 삭제하는 것이 아닌 다시 Queue로 넣어 재사용한다는 목적으로 만든 최적화 기법이예요.

직접 구현해서 비교해보자

image.pngimage.png

생각보다 gif 파일 용량이 커서 사진으로 대체하겠습니다

생성/파괴의 경우 최저 프레임이 28프레임이 나왔고

Object Pooling의 경우 최저 프레임이 35프레임이 나왔습니다.

Garbage Collector(황토색?)와 Script(파란색)의 그래프만 뜨도록 표시를 했는데 분명 차이가 있어야 하는데 차이가 없어서 당황했습니다.

image.png

차이가 있긴 하지만 이대로 끝내긴 너무 찝찝했습니다. 생각했던 것보다 Instantiate/Destroy에서의 Garbage Collector가 활발하지 않았고 성능 차이도 10프레임 정도의 차이 밖에 안났던 것입니다.

그래서 선배께 도움을 청해 제 코드가 잘못된 것인지 확인해주실 수 있는지 요청을 드렸고 결과는 비슷했습니다.

image.png

솔직히 아무 의심도 없이 당연히 무조건 써야한다, 좋다고만 생각했던 Object Pooling과의 성능 차이가 얼마 나지 않아 뒤통수를 쌔게 맞은 느낌이 들었습니다.

그래서 결국 의미는 없었는가

선배와 제가 그때 당시 내린 결론은 Unity 2022 에서 제공하는 Instantiate와 Destroy의 코드는 최적화를 잘해서인지 그렇게 비용이 비싸지 않았던 것 같다. 그래서 Unity 2017 버전에서 돌리면 유의미한 차이가 있을 것이다 였습니다. 그리고나서 계속해서 테스트를 해본 결과

image.png

드디어 납득이 갈만한 결과가 나왔습니다. 저희가 테스트했던 환경에서는 생성했던 객체(총알)이 그저 앞으로 나가는 기능밖에 없었기 때문에 별 차이가 없었으나 실제 게임은 다릅니다. 이것보다 오브젝트 하나하나가 훨씬 무겁겠죠.

결론은 생성되는 오브젝트가 무거우면 무거울수록 Object Pooling에 의미를 갖게 되는 것 이였습니다.

오브젝트가 생성할 때 실행되는 초기화 코드(Start() 함수, Awake() 함수)가 계속 실행되느냐 vs 한번 실행되느냐 의 차이는 컸기 때문입니다.

마무리하며

사실 원래 여기에 DOTS(Data Oriented Technology Stack) 방식으로 구현한 탄막 게임까지 같이 비교하여 포트폴리오를 작성하려고 했으나 생각지도 못했던 부분에서 오래걸리기도 하였고, DOTS 방식의 코딩 스타일이 기존의 객체 지향 프로그래밍 방식과 전혀 달라 일주일 내로는 무리였습니다. 하지만 당연하게 사용해왔던 이러한 최적화 기법을 직접 비교해보고 관찰함의 과정을 통해 더 유의미한 시간을 보낼 수 있었던 것 같습니다. 이 에피소드는 앞으로도 기억에 남을 것 같네요 재밌었습니다. 다음주에는 혹은 다다음주에는 DOTS를 구현하고 찾아뵙도록 하겠습니다.

4
2
민서

민서

효율적인 UI 구조를 위해 MVP 패턴을 사용해보았습니다

들어가며

안녕하세요 저는 특성화고 컴퓨터게임제작과를 다니며 현재는 특기자 전형을 위한 포트폴리오를 준비 중인 고3 민서라고 합니다. 저는 현재 포트폴리오에 저의 가치를 드러낼 수 있는 내용을 적기 위해 고민하고 개발해나가고 있는데요. 포트폴리오를 채워나가면서 구조 설계, 디자인 패턴과 관련된 얘기가 들어가면 좋겠다고 생각하여 오늘 소개할 주제를 선정하게 되었습니다.

MVP 패턴은 무엇일까

MVP 패턴은 Model, View, Presenter 세 가지 요소로 UI를 관리하는 디자인 패턴입니다. Model은 데이터를 가지고 있고, View는 사용자에게 보여지는 UI를 Presenter는 Model의 데이터와 View의 UI 기능을 관리하는 친구입니다. 제가 공부했던 블로그의 사진은 이렇게 소개하고 있네요

image.png

UI는 너무 활용도가 넓고 한 객체마다 어디까지 책임을 부여하고 분리해야 할지 판단하기 어려웠고 UI 구성을 조금이라도 바꾸면 이곳저곳에서 나오는 오류들 때문에 항상 게임 개발할 때마다 UI 작업을 제일 싫어했던 기억이 나네요. 그렇기에 지금이라도 공부해야 한다고 생각했습니다.

뭔가 구현했던 구조들을 다 설명하려고 했는데 그렇기에는 글이 너무 길어지고 TMI일 것 같아서 싹 다 지우고 어떤 형태로 구현했는지만 간단하게 그려보겠습니다.

image.png

이건 실제로 제가 테스트해보기 위해서 만들었던 팝업창 UI입니다. Bind 함수를 통해 팝업창에서 어떤 텍스트가 띄워질 것인지, 버튼에 표시되는 텍스트는 무엇인지, 버튼이 어떤 기능을 수행하는지를 넘겨주면 Presenter에서 정보를 받아 View로 표시해주는 거죠. Unity와 C#의 특징 덕분에 구현하다 보니 앞에 보여줬던 사진의 구조와는 다르게 View는 Presenter를 몰라도 되고 Presenter는 Model을 몰라도 되게 되었습니다. 결합도가 느슨해졌다는 것이니 오히려 좋아요

더 개선할 수 있지 않을까?

제가 짠 코드에서 View 기능을 담당하는 코드는 ViewBase라는 객체에서 모든 역할을 다 해주기 때문에 사실 View 코드는 매우 간단합니다.

image.png

View에서는 사실 보여지는 부분만 관리하기 때문에 각 View마다의 차이점은 UI가 어떻게 구성되어있는지 밖에 없습니다. 그렇기 때문에 저는 ViewBase 라는 부모 클래스를 만들어주고 UI의 공통적인 특성(ESC를 통해 닫을 수 있다, 중첩이 가능하다, 게임이 시작하자마자 보인다 등등)을 정의해놨기 때문에 View의 코드는 매우 간단해질 수 있었던 것이죠. 하지만 어차피 다른 프로젝트에서도 계속 재활용하며 사용할 코드라면 더 편리했으면 좋겠습니다.

CodeDOM을 사용해보자

CodeDOM은 C#에서 제공하는 기능 중 하나로 무려 런타임 중에 코드를 생성할 수 있게 해줍니다. 물론 그만큼 느리기 때문에 보통 인게임에서는 사용하지 않고 제 경우와 같은 에디터를 만들거나 개발 툴을 만들 때 사용합니다.

CodeDOM을 사용해서 우선 UI를 배치하고 그 UI를 인식하여 코드를 짜주는 친구를 만들면 좋지 않을까라는 아이디어로 시작해서 결국

녹화_2023_09_03_23_00_50_112.gif

성공했습니다! 처음 다뤄보는 기능이기도 하고 자료가 공식 문서밖에 없어서 중간에 오류가 많았지만 잘 작동하는 것을 보니 기분이 좋네요

마무리하며

제가 여러 현업 개발자들을 만나며 얻었던 조언 중 하나는 게임 개발자들을 각자 자신만의 ‘자산’을 보유하고 있다는 것이었습니다. 자산이 잘 쌓여있는 개발자는 코드 조금만 짜도 하나의 게임을 금방 완성할 수 있을 정도라고 하더라구요. 처음 맨땅에서 게임을 만드는 것은 무척이나 어려운 일이기 때문에 이번 주에 만든 MVP 패턴과 같이 미리 구조와 기능을 만들어두면 이 코드는 모든 프로젝트에서 쓸 수 있게 되고 그것이 저의 자산이 된다는 이야기였습니다. 이번 주는 그런 오래 두고두고 쓸 ‘자산’을 만들었다는 점에서 매우 의미가 깊었던 그리고 배울 것이 많았던 주였던 것 같습니다.

다음 주에 만들 것

다음 주는 포트폴리오에 최적화에 관한 이야기가 들어가면 좋을 것 같아서 이번에 새로 Unity에서 선보인 DOTS(Data Oriented Technical Stack) 와 기존 C#의 Garbage Collector의 단점을 보완하기 위해 만든 Object Pooling, 이 두 가지 최적화 기법을 비교하는 보고서를 작성해보려고 합니다. DOTS는 멀티스레드와 연관이 있는 내용이라 얼마나 어려울지, 오래 걸릴지는 감도 안 잡히지만 그래서 그런지 기대가 되기도 합니다.

사실 이런 글을 써보는 게 거의 처음이라, 다른 분들의 글을 보면서 어디까지 간결하게 쓰고, 어디까지 자세하게 써야 할지 모르겠어서 고민하며 지우고 쓰고를 반복하다 보니 꽤 오래 걸렸습니다. 최대한 간결하지만 자세하게(?) 쓰려고 하다 보니까 글이 길어질 수밖에 없었네요. 부족한 글 읽어주셔서 감사합니다. 좋은 밤 보내세요!

8
1

포스트

아직 포스트가 없습니다.