이전 포스트에서 언급한 것 처럼, 현재는 본편에서 사용할 오픈월드 엔진을 직접 개발하고 있습니다.
오픈월드 엔진이라고 하면, 씬 분할 및 스트리밍(로드/언로드) 기능과 여러 최적화 기능들을 합친 툴이 되겠죠. 그 외에도 시차나 날씨 컨트롤, 부동 소수점 문제 해결 등이 포함된 툴이어야 겠군요.
데모버전에서는 에셋스토어에 올라와있는 WorldStreamer2를 사용했습니다.

물론 충분히 좋은 툴이지만, 간혹 버그도 발생하고 일부 부족한 기능들이 눈에 띄었습니다.
굳이 나열해보자면 이런 것들입니다.
1. 어드레서블 에셋 시스템을 사용하지 않고 기존의 씬 포함 방식을 사용해서, 씬 관리가 어려움
2. 문자열로만 씬을 캐싱하기 때문에 지명 변경 등의 이유로 씬의 이름이 바뀌게 되면 경로를 전부 수정해줘야함
3. 씬을 분할할때, 원점을 중심으로 한 타일을 기준으로 하지 않기 때문에 각 타일 범위를 겹치지 않게 하기 어려움
4. 분할 씬을 생성할 때, 매번 원본 씬에서 오브젝트를 복사해서 붙여넣은 후 사용해야함. 그리고 미리 정해놓은 작업용 씬에서만 해당 작업을 해야함.
5. 디버그 기능이 부족함
6. (y=0)을 기준으로 그리드 기즈모만 그리기 때문에 월드의 각 부분이 어느 타일에 포함될지 미리 알기가 어려움. 게다가 그리드를 그리는 범위도 너무 짧음
7. 빠르게 이동할때, 거의 무조건 오류 메세지가 출력됨
8. 여러 콜라이더로 콜라이더 씬의 트리거를 설정해도, 맨 위의 콜라이더만 베이크됨. 그래서 빌드할때마다 직접 콜라이더를 다시 추가하거나 콜라이더를 하나만 써야함.
9. 콜라이더 씬 내에서 별개의 씬 분할을 사용하기 어려움(콜라이더 씬 내에 또 다른 콜라이더 씬을 만드는 식으로만 가능)
그냥 소스코드를 수정해서 사용할까 고민하다가, WorldStreamer2에 없는 다른 최적화 기능들과 오픈월드식 시스템들도 합쳐서 하나의 프로그램으로 만들어버리자는 생각으로 개발을 시작했습니다.


WorldStreamer2에서 씬을 관리하는 방식. 이대로는 씬을 관리하기 어렵고 버그가 나기도 쉽다
일단 어드레서블 에셋 시스템을 사용해서 씬을 관리하도록 바꿨습니다.
초기화 시, 어드레서블 그룹 및 하위 폴더들을 자동으로 생성합니다.
그리고 분할된 씬을 생성할 때, 어떤 씬에서 작업하고 있어도 상관 없도록 만들었습니다. WorldStreamer2에서는 분할 씬 생성을 위한 작업용 씬을 만들어서 해당 씬에서만 작업해야 했지만, 이젠 아무때나 씬을 생성하고 싶을때 생성하면 됩니다.
게다가 씬 생성 도중에는 임시 폴더를 생성 후 만들기 때문에 중간에 씬 생성을 종료하거나 오류로 인해 씬 생성 과정이 중단돼도 이전에 만들었던 작업물들에 아무런 영향을 끼치지 않게 만들었습니다.


어드레서블 에셋 시스템을 사용한 결과. 빌드 세팅의 씬 목록은 깔끔해졌고, 문자열 변경으로 인한 오류를 걱정하지 않아도 된다
현재 플레이어 위치에서 어떤 씬들이 로드/언로드 되는지 (되어야 하는지) 바로 알 수 있도록 기즈모도 추가했습니다.
Gizmo.DrawWireCube나, Handles.DrawWireCube는 수백개 이상을 그리면 꽤 렉이 걸려서, 더 낮은 수준의 그래픽 라이브러리인 GL을 사용해서 그렸습니다.
왜냐하면 저 기즈모를 그리는 코드로 분할될 씬의 범위를 전부 표현할 수 있어야 하거든요. WorldStreamer2는 기즈모 그리는 범위가 너무 짧아서, 전체 월드에서 어떤 부분이 어떤 씬에 포함될지 알기가 어렵습니다.
이것 말고도 씬 분할 생성, 로드/언로드, 디버그 기능 등 여러개를 만들었는데, 지금 잠깐 구조를 고치느라 코드를 바꿔놔서 스크린샷 찍기가 애매하네요. 미리 찍어놓을 걸 그랬습니다.

현재 플레이어 위치에서 로드될 씬의 위치 및 좌표를 보여주는 기즈모. 간단하지만 유용한 기능이다
글을 쓰다보니, 어째 WorldStreamer2를 까는 내용이 주가 되어버렸네요. 누가 보면 보이콧을 주장하는 줄 알겠군요!
다시 한 번 말하지만 WorldStreamer2도 충분히 좋은 툴입니다. 애초에 현재 에셋 스토어엔 이것보다 좋은 오픈월드용 씬 관리 툴은 없는 것 같습니다. Gaia Pro에서도 씬 분할 및 스트리밍 기능이 있지만, 월드 디자인 자체를 Gaia로 할 게 아니면 사실 매력적인 툴은 아닙니다.
Gaia의 월드 디자인도 많은 기능들이 있지만, 워낙 많은 부분들을 자체 시스템에 포함시켜놓다보니 Gaia만 사용하는 게 아니라 여러 에셋들을 복합적으로 사용하려면 Gaia는 조금 애매한 선택지가 되는 것 같더군요.
그리고 이 외의 씬 분할 및 스트리밍 에셋은 사실 없다시피합니다. Gaia를 만들었던 Procedural World에서 얼마 전에 SceneOptimizer라는 툴을 출시한 것 같지만, 씬 분할 및 스트리밍 기능이 있는지는 잘 모르겠네요. 거리에 따라 오브젝트 및 그림자를 컬링하거나 공간 분할로 메쉬를 결합하긴 하지만, 아예 메모리에 올리지 않으려면 씬을 나누는 수밖에 없으니, 추가적인 최적화 방법이 될 순 있어도 저것만 쓰긴 어려워 보이더군요.
다른 게임엔진은 사용한 적이 없어서 모르겠으나, 적어도 유니티에서 오픈월드를 개발하려면 씬 분할은 필수인 것 같습니다. 아니면 DOTS랑 ECS가 빨리 개발되어서 Subscene 기능을 사용할 수 있으면 좋을텐데… 도대체 프리뷰 시작한지가 언젠데 끝나질 않아…
앞으로도 종종 오픈월드 엔진 개발 과정을 올리겠습니다. 그리고 본편을 전부 만들고 나면 에셋스토어에 올릴 예정입니다. 싸게 올릴테니 싸게싸게 사가시길 ㅋ