Search Unity

이번 게시물에서는 새로운 데이터 지향 테크기술 스택(DOTS)  기술 중 한 가지인 ECS(엔티티 컴포넌트 시스템)에 대해 소개해보겠습니다. 현재까지의 개발 상황 및 향후 계획을 공유하고자 합니다.

지난 번 게시물에서 HPC#(High Performance C#)과 버스트를 Unity가 지향해야 할 로우레벨 기반 기술로 소개한 적이 있습니다. 이러한 수준의 스택을 개인적으로 “게임 엔진을 위한 엔진”이라고 부르고 싶습니다. 누구나 이 스택으로 게임 엔진을 만들 수 있습니다. 유니티라면 가능하고 결국 해낼 것입니다. 여러분도 물론 가능합니다. Unity가 마음에 들지 않는다면 자신만의 엔진을 만들거나 Unity를 원하는 대로 수정하실 수 있습니다.

Unity의 컴포넌트 시스템

Unity는 새로운 컴포넌트 시스템을 만들고 있습니다. Unity의 개발 중심에는 언제나 컴포넌트 개념이 자리잡고 있습니다. 게임 오브젝트에 Rigidbody 컴포넌트를 추가하면 오브젝트가 낙하하기 시작합니다. Light 컴포넌트를 추가하면 빛이 방출되기 시작하며, AudioEmitter 컴포넌트를 추가하면 사운드 재생이 시작됩니다.

이 컴포넌트는 프로그래머와 비프로그래머 모두에게 매우 자연스러운 개념으로서 UI를 직관적으고 쉽게 빌드할 수 있게 해 줍니다. 이 개념이 얼마나 체계적으로 구축되어 왔는지를 보면 놀라울 따름입니다. 유니티는 앞으로도 계속 이 개념을 개발의 중심으로 유지하고 싶습니다.

앞으로 좀 더 개선해야 할 부분은 Unity의 컴포넌트 시스템 자체를 구축하는 방식입니다. 이는 객체 지향적인 사고 방식을 통해 작성되었습니다. 컴포넌트와 게임 오브젝트는 “많은 C++” 오브젝트로 이루어져 있습니다. 이것을 생성/제거하려면 id->objectpointers의 전역 리스트를 수정하기 위한 mutex(mutual exclusion, 상호 배제) 락(lock)이 필요합니다. 모든 게임 오브젝트에는 이름이 지정되어 있으며, 각 게임 오브젝트마다 C++ 오브젝트를 참조하는 C# 래퍼 오브젝트가 있습니다. 이 C# 오브젝트는 메모리 공간 어디에든 접근할 수 있습니다. C++ 오브젝트 역시 메모리 공간 어디에든 접근할 수 있습니다. 하지만 많은 캐시 미스(cache miss)가 발생합니다. 이러한 문제를 해결하기 위해 많은 노력을 기울이고 있지만 아직까지는 완벽하지는 않습니다.

하지만 데이터 지향적인 접근 방식을 이용하면 문제를 더 획기적으로 해결할 수 있습니다. Unity의 새로운 컴포넌트 시스템을 이용하면 사용자의 관점에서 유용한 프로퍼티는 그대로 유지(Rigidbody 컴포넌트를 추가하면 낙하 시작)하면서 놀라운 성능 향상과 병렬성(parallelism)을 얻을 수 있습니다.

이 새로운 컴포넌트 시스템을 ECS(Entity Component System, 엔티티 컴포넌트 시스템)라고 합니다. 간단하게 말하면 현재 게임 오브젝트로 수행하는 작업을 새로운 시스템에서는 엔티티를 이용하여 수행하는 것입니다. 컴포넌트는 기존과 동일하게 컴포넌트로 불립니다. 그렇다면 무슨 차이점이 있을까요? 그것은 바로 데이터 레이아웃입니다.

몇 가지 일반적인 데이터 액세스 패턴을 한번 살펴보겠습니다.
Unity에서 기존 방법으로 컴포넌트를 작성한다면 일반적으로 아마 다음과 같이 나타납니다.

 

 

이러한 패턴은 반복적으로 나타납니다. 하나의 컴포넌트는 같은 게임 오브젝트에서 다른 컴포넌트를 하나 이상 찾아 어떤 값을 읽거나 써야 합니다.

이 방법에는 다음과 같은 많은 문제점이 있습니다.

  • 단일 Orbit 컴포넌트에 대해 Update() 메서드를 호출한다고 가정해 봅시다. 다음으로 완전히 다른 컴포넌트에 대해 Update() 메서드가 호출될 수 있으며, 이로 인해 이 코드는 다음 번에 또 다른 Orbit 컴포넌트에 대해 이 프레임을 실행해야 하는데도 캐시에서 사라질 수 있습니다.
  • Update()는 Rigidbody를 찾기 위해 GetComponent()를 사용해야 합니다. 물론 캐싱될 수도 있지만 Rigidbody 컴포넌트가 제거되지 않았는지 주의해야 합니다
  • 사용 중인 나머지 컴포넌트가 메모리 내에서 완전히 다른 영역에 위치합니다.

ECS에서 사용하는 데이터 레이아웃은 이러한 패턴이 매우 일반적으로 발생하고 있음을 인식하고 메모리 레이아웃을 최적화하여 이 작업을 더 빠르게 수행할 수 있습니다.

 

ECS 데이터 레이아웃

ECS는 메모리에서 컴포넌트 세트가 완벽하게 동일한 모든 엔티티를 그룹화합니다.  이때 이 컴포넌트 세트를 원형(archetype)이라고 합니다. 예를 들어 “Position, Velocity, Rigidbody, Collider”는 하나의 원형으로 묶여 있습니다. ECS는 16k의 청크 단위로 메모리를 할당합니다. 각 청크에는 하나의 원형에 속한 엔티티의 컴포넌트 데이터만 포함됩니다.

런타임 시 Orbit 인스턴트별로 사용자 Update 메서드를 사용하여 다른 작업 대상 컴포넌트를 찾는 대신, ECS에서는 “Velocity, Rigidbody 및 Orbit 컴포넌트가 모두 있는 모든 엔티티를 대상으로 작업을 실행하고 싶습니다.”라고 정적으로 선언해야 합니다. 이러한 모든 엔티티를 찾으려면 특정 “컴포넌트 검색 쿼리”와 일치하는 모든 원형을 찾기만 하면 됩니다. 각 원형에는 해당 원형의 엔티티가 저장되어 있는 청크 리스트가 있습니다. Unity는 이러한 모든 청크에 대해 루프를 수행하며, 각 청크 내에서 빽빽하게 패킹된 메모리에 대해 선형 루프(linear loop)를 수행하여 컴포넌트 데이터를 읽고 씁니다. 각 엔티티에서 동일한 코드를 실행하는 이 선형 루프를 통해 버스트에 대한 벡터화에도 도움을 줍니다.

대부분의 경우 이 프로세스는 여러 잡(job)으로 분할됩니다. 이를 통해 ECS 컴포넌트를 작동하는 코드의 경우 코어를 거의 100% 활용하여 실행됩니다.

ECS는 이러한 모든 작업을 자동으로 수행하며 사용자는 각 엔티티에서 실행할 코드만 제공하면 됩니다. (물론 경우에 따라 청크 반복을 수작업으로 할 수도 있습니다.)

엔티티에서 컴포넌트를 추가하거나 제거하면 원형에 변화가 생깁니다. 이 경우 현재 청크에 있는 엔티티가 새로운 원형 청크로 이동하게 되어 해당 청크에 발생된 공백을 메꿀 수 있도록 이전 청크의 마지막 엔티티를 백스왑합니다.

또한 ECS에서는 컴포넌트 데이터로 할 작업을 정적으로 선언할 수 있으며, 읽기 전용 또는 읽기 쓰기로 선언할 수 있습니다. Position 컴포넌트를 읽기 전용으로 선언하면 ECS가 더 효율적으로 작업을 스케줄링할 수 있습니다. Position 컴포넌트에서 읽어와야 하는 다른 잡은 대기할 필요가 없게 됩니다.

이 데이터 레이아웃을 통해 오랫동안 골칫거리였던 로드 시간과 직렬화 성능 문제를 해결할 수 있습니다. ECS 데이터를 용량이 큰 씬에서 로딩/스트리밍할 때는 디스크에서 원시 데이터 몇 바이트만 로딩해서 있는 그대로 사용하면 됩니다.

핸드폰에서 메가시티(Megacity) 데모를 로드하는 데 몇 초밖에 걸리지 않는 이유가 여기에 있습니다.

엔티티의 특징

엔티티는 기존에 게임 오브젝트가 하던 작업을 수행할 뿐 아니라, 경량(lightweight)이기 때문에 더 많은 작업도 수행할 수 있습니다. 엔티티의 실체는 무엇일까요? 사실 이 게시물의 초안을 작성할 때 “엔티티를 청크 단위로 저장합니다(we store entities in chunks).”라고 썼다가 나중에 “엔티티의 컴포넌트 데이터를 청크 단위로 저장합니다(we store component data for entities in chunks).”라고 바꾸었습니다. 이것은 엔티티가 32비트 정수에 불과함을 이해하는 데 중요한 특징입니다. 엔티티에는 컴포넌트의 데이터 외에 저장하거나 할당할 만한 것이 없습니다. 용량이 매우 작기 때문에 게임 오브젝트를 사용하기에 부적합한 시나리오에서도 사용할 수 있습니다. 예를 들어 파티클 시스템의 개별 파티클 각각에 엔티티를 사용할 수 있습니다.

HPC#, 버스트, ECS를 기반으로 한 게임 엔진 구축

유니티에서 구축하려고 하는 그 다음 레이어는 매우 큰 프로젝트로서, “게임 엔진” 레이어이며 “렌더러”, “물리 시스템”, “네트워킹”, “입력”, “애니메이션” 등으로 구성되며 현재까지는 아직 구체적이지 않습니다. 이제 시작 단계이며 하루 아침에 구현 가능한 것은 아닙니다.

실망스럽다고요? 하지만 꼭 그렇게 볼 필요는 없습니다. ECS와 ECS에 탑재된 모든 요소는 C#으로 작성되었기 때문에 기존 Unity에서 실행 가능합니다. Unity 내부에서 동작하기 때문에 ECS 이전의 기능을 사용하는 ECS 컴포넌트도 작성할 수 있습니다. 완전한 ECS 메시 드로잉 시스템은 아직 출시되지 않았습니다. 하지만 완전한 ECS 버전이 출시될 때까지 ECS 이전의 Graphics.DrawMeshIndirect API를 사용하는 ECS MeshRenderSystem을 작성하여 구현할 수 있습니다. 이것은 유니티의 Megacity 데모에서 사용하는 기술이기도 합니다. 완전한 ECS 시스템상에서 로딩/스트리밍/컬링/애니메이션 작업을 할 수 있지만 최종 드로잉 작업은 할 수 없습니다.

따라서 기술을 적절히 조합할 필요가 있습니다. 이렇게 하면 모든 하위 시스템에서 호환되는 완전한 ECS 버전이 출시될 때까지 기다릴 필요 없이 게임 코드에 필요한 버스트 코드 생성과 ECS 성능을 활용할 수 있습니다. 단, 이러한 전환 단계에서 “두 가지 다른 시스템을 함께 사용할 때” 겪을 수 있는 충돌과 부자연스러움을 보고 느끼게 될 수 있습니다.

유니티는 ECS, HPC# 하위 시스템에 모든 소스 코드를 패키지 형태로 제공할 계획입니다. 각 하위 시스템을 검사, 디버그, 수정할 수 있으며 어떤 하위 시스템을 업그레이드할지에 대해 더 강력한 제어 권한을 갖게 될 것입니다. 예를 들어 다른 것들은 그대로 유지한 채 물리 하위 시스템 패키지만 업그레이드할 수도 있습니다.

 

게임 오브젝트의 미래

게임 오브젝트는 그대로 유지됩니다. 10년이 넘는 기간 동안 오브젝트를 사용한 많은 게임이 성공적으로 출시되었습니다. 이러한 기반은 계속 유지될 것입니다.

시간을 두고 지켜봐 주시기 바랍니다. 유니티의 노력으로 게임 오브젝트가 ECS에 맞게 변화하는 모습을 보실 수 있을 것입니다.

 

API 사용성/상용구 코드

ECS에 대한 사용자의 일반적인 인식은 많은 양의 코드를 입력해야 한다는 것입니다. 무언가를 구현하려 하다 보면 자연스럽게 많은 양의 상용구 코드(boilerplate code)를 사용하게 됩니다.

유니티는 상용구 코드 없이도 사용자의 의도를 쉽게 표현할 있도록 개선하고 있습니다.  현재 기본적인 성능 개발에 주력하고 있기 때문에 아직 구현된 개선 사항은 많지 않습니다. 하지만 ECS 게임 코드에 상용구 코드가 많이 사용되거나 MonoBehaviour보다 코드 작성을 더 많이 해야 할 이유는 없습니다.

Tiny 프로젝트 에서는 이미 람다 기반의 반복 API와 같은 일부 개선 사항이 적용되었습니다.

 

Tiny 프로젝트의 ECS 적용

Tiny 프로젝트 는 위에서 설명한 C# ECS를 사용한 우수 사례라고 볼 수 있습니다. Tiny 프로젝트는 다음과 같은 점에서 ECS를 구현한 우수한 사례가 될 것입니다.

  • ECS 전용 환경에서 실행 가능합니다. 과거에 사용하던 그 어떤 것도 필요 없습니다.
  • Tiny 프로젝트는 완전한 ECS이며 작은 규모의 게임에 필요한 모든 ECS 하위 시스템과 함께 제공된다는 의미입니다.
  • 유니티는 작은 규모의 프로젝트뿐만 아니라 모든 ECS 시나리오에서 엔티티를 수정할 수 있도록 Tiny 프로젝트 에디터를 지원할 예정입니다.

유니티 채용 정보

DOTS 스택에 필요한 여러 직무의 채용 공고가 있습니다. 특히 버뱅크(Burbank)와 코펜하겐(Copenhagen)에서 인재를 필요로 합니다. careers.unity.com을 확인하세요.

또한 Unity 엔티티 컴포넌트 시스템 및 C# 잡 시스템 포럼에 참여하여 의견을 제공하고 실험 기능과 프리뷰 기능에 대한 정보를 얻으시기 바랍니다.

 

25 코멘트

코멘트 구독

코멘트를 달 수 없습니다.

  1. Philipp Lenssen

    4월 12, 2019 9:17 오후

    And one day, there will be machine-learning intelligence which converts your GameObjects into Entities…

    pleease…

    In the meantime, super excited to try this for new projects in the future. I reckon it will get more and more usable from a “developer-consumer” point of view. After all, Unity’s ease of use is one of its big appeals. Some of the recent tutorials on DOTS have been heavily to-the-metal-tech focused, but in my projects I love to think about… you know… robots, unicorns, VR gestures, chat, and whatever else is important for the actual experience.

    Godspeed!

  2. can this be used on commercial projects ?

  3. Please, make it possible to supply Mesh with Native Arrays. Copying arrays around is very annoying.

  4. Firstly thanks for this and I hope more posts like this are done in Unity’s blog. Back then when Lucas was still a Unity customer, I was excited to read every post in unity’s blog and even the back then technical evalgelist of Unity’s posts were much more useful compared to some of what we see on the blog these days.
    He was evangelizing too and even back thens posts were being done which they had other goals than educating the reader about deep technical things but they had that nameless type of quality which is lost now.

    On ECS stuff, the FPS sample helped a lot and posts describing how regular things are done in ECS or maybe writing a 100 page PDF tutorial like what you guys did for lerpz for ECS samples probably would help many.

    For the FPS sample and ECS in general I put nights of my two weeks to watch all videos and read almost all code of the sample but not always you have such time and not everyone can learn like that.

  5. You are doing a terrible job of introducing a paradigm-shift

    1. Dude Fugges Duddette

      3월 15, 2019 11:01 오후

      You are doing a terrible job of expressing your criticism of this piece

  6. Thank you for the article! Now I understand ECS more. Clarity matters.

  7. So would you gays publish the whole MegaCity project?

  8. Setriakor Nyomi

    3월 9, 2019 7:53 오후

    Having been around to witness the growing pains that came with the IL2CPP transition, I have complete faith that you can pull this off. Although it feels a bit like comparing apples to oranges, I can draw some parallels especially around the time and resources it is going to take to have a mature version of the ECS family of technologies being similar IL2CPP’s.

    I for one cannot wait to start using pure ECS for everything, but I know that’s still a pretty long way off. I’m still excited to see all this progress though, and would like to see these posts once every week or so. Also like that parts of the engine are being ported to HPC# and will be open sourced. Keep up the good work!

  9. Unity 5.6.7 not support android 9 (API level 28) ?

  10. As a creative, not a coder, i feel a little bit better about ECS after this blog post. I just don’t want to see Unity go the way of Flash, which alienated artists and creatives with the “superior” but verbose AS3, and a structure that was more logical, but deterred fast artistic iteration. I hope Unity remains accessible to designers and artists without an engineering or professional coding background. That said, the idea behind ECS – building complexity from increasingly atomic components – seems like a good idea.

  11. Alejandro Castán

    3월 8, 2019 11:59 오후

    Hi Lucas. First of all, Great Post!!! .On the other hand, I am interested in learning ECS Puré and for it, I would like to learn with Proyect Tiny. Is this proyect made in ECS Puré? Finally and since I am very excited to get the Megacity demo, for ir, Do You think if it could be avalable in the next GDC? Thanks for your time. Alejandro

  12. Blog post blocks a certain user. WTF!

  13. Lucas Meijer, If you allow me to give you a suggestion here since a dedicated feedback solution is coming: Give you margins. In the car industry, we start designing and making a model with 10 years in advance. Perhaps Unity 2019 should have been called Unity 2021 to make it clearer to new users that it is a WIP software. So (as for example) this October 2019 you release Unity 2020LTS ready (ower Unity 2018LTS) and on January 2020, the Unity2020.4.18f1 LTS. In this way, the LTD arrives when the year starts (and not a year or two later). So that new beginners with no experience can look to a lot of tutorials in YouTube and do not experiment with a WIP software in the evolution phase creating noise on the forum. Experts feel comfortable creating the future. If you jump U2020 and 21 making U2022 for branding purpose we will miss the nice numbers 2020 but I think users can understand that. Is just a feeling. Thank you and your awesome team.

    1. While Lucas probably has no input on product naming, I agree with some of your points. It does feel like newcomers should be directed to an LTS release, and it would make more sense to name the software for the year they hit LTS.

      So, in my opinion, “Unity 2017 LTS” should be “Unity 2018” and “Unity 2019” should be “Unity 2020 Preview”. Experts, as you said, are often comfortable creating with the newest features, especially when it often takes over a year to develop even a modest game — Starting with 2020 Preview means you’ll likely ship after 2020 releases in its final, LTS state. Maybe they even drop “Preview” during the 4th quarter of 2019 — the key is having a pool of community knowledge on top of updated tutorials when this happens.

  14. Nice post! It is great to see that you are working on ECS! These kind of blog are a must read for me as a programmer thank you!

  15. Peter Mullins

    3월 8, 2019 2:01 오후

    The problem is that many time pass and the ECS system is not ready.

  16. A lot of marketing and great promises. Almost 2 years has been passed from initial presentations of ecs and your users still have nothing more than empty talking how great it could be.

    1. Wasn’t it announced in 2018?

      1. Nope. They showed massive battle scene in 2017. It was based on ECS and Burst compiler back then. It’s 2019, and we are still far away from production ready DOTS. We use burst in our shipped product, but there is a lot of missing API for NativeArrays. Only textures are possible to read/write to NA. Other parts of engine are still based on managed types. Under the hood, they call C++ functions with pointers to these arrays, so why we still can’t update meshes with NA? Why we can’t call Graphics API with NA? It seems like a small change in engine API with huge positive impact for ECS development. Maybe they create whole new API for ECS, and they don’t want people to base their games on older API, but engine is constantly updated with old C# managed style. Just look at SRP.

  17. > Update() has to use GetComponent() to go and find its Rigidbody. (It could be cached instead, but then you have to be careful about the Rigidbody component not being destroyed).

    In what instance would rigidbody not be destroyed? If you destroy the gameobject with the script, cached link, and rigidbody on you are not fine? Using GetComponent() has been a no-no in discussions for a long time

    1. He means not being destroyed externally I think.

  18. “Entity is just a 32-bit integer” => Entity is just two 32-bit integer

    To be precise :p

  19. Sergey Klimenko

    3월 8, 2019 10:17 오전

    Great as expected :)