[UNITY],[C#]/TIL : DOTS

[ECS/DOTS] Unity ECS

네,가능합니다 2024. 11. 17. 17:02

유니티에서 제공하는 자료를 보며, 정리한 글입니다.

( https://create.unity.com/Best-tips-and-tricks-by-japan)

 

유니티에서 DOTS를 다루는 방법을 정리해보자

 

기존의 '표준적인' 유니티에서는 MeshFilter나 MeshRenderer, MonoBehaviour같은 컴포넌트가 사용되었었다.

 

하지만 ECS에서는 새롭게 추가된 컴포넌트를 이용할 수 있다.

 

하지만 ECS를 유니티 에디터에서 제어할때는 기존의 구조처럼 사용할 수 있어야 편리할것이다.

(기존 GameObject를 다루듯)

 

그래서 기존의 컴포넌트를 사용해서 똑같이 작업할 수 있도록 구현이 되어있다.

 

그리고 그걸 보관하는 곳이 바로 subscene이다.

 

subscene의 내용은 실행할 때 혹은 빌드할 때 ECS의 새로운 컴포넌트 데이터로 변환이 되는데, 이러한 현상를 Bake 라고 한다.(유니티에서 여러모로 많이 쓰이는 단어 '굽는다' 라고 많이 사용한다.)

 

또한 해당 오브젝트를 클릭하면 인스펙터 하단에 어떤 컴포넌트들로 변환되는지 확인할 수 있다.

 

샘플자료에 있는 춤을 추고 있는 Dancer에 달린 DancerAuthoring스크립트를 살펴보자.

 

스크립트의 상단에는 Dancer struct가 구현되어있다.

 

public struct Dancer : IComponentData
{
	public float Speed;
}

 

위 코드는 ECS에서 컴포넌트별 데이터를 정의하는 방법이다.

 

위 코드 내용을 그대로 읽어보면 IComponentData가 구현된 Dancer라는 struct(구조체)이다.

 

그 아래의 내용도 살펴보자

public class DancerAuthoring : MonoBehaviour
{ 
	public float _speed = 1;
    
    class Baker : Baker<DancerAuthoring>
    {
    	public override void Bake(DancerAuthoring src)
        {
        	var data = new Dancer(){ Speed = src._speed };
            AddComponent(GetEntity(TransformUsageFlags.Dynamic), data);
        }
    }
}

 

DancerAuthoring 클래스가 정의되어있다.

 

그리고 그 아래는 변환 처리를 하기 위한 Baker클래스가 정의되어있다.

 

기존의 유니티에서는 MonoBehaviour을 상속받는 클래스 안에서 게임로직을 구현했었는데

 

유니티 에디터에서 설정된 값을 Dancer 컴포넌트로 변환하는 처리만 작성되어있다.

 

그렇다면 실제 오브젝트를 움직이기 위한 처리는 어디에 작성되어있을까 ?

 

이게 바로 ECS의 특정이다.

 

ECS에서는 데이터와 로직이 완전히 분리된다.

 

이 로직부분을 'System'이라 불리며 다른 위치에 구현을 한다.

 

이 샘플에서는 DancerSystem.cs가 여기(System)에 해당한다.

 

 

코드를 살펴보자.

 

ISystem이 구현된 struct로서 DancerSystem이 정의되어있다. 여기서 확인해볼것은 struct에 partial 키워드가 포함된다는것이다.

 

ECS에서는 Source Generator로 다양한 보조코드를 자동으로 생성하는데, 이 자동생성된 코드와 함께 사용하기 위해서 이  partial키워드가 필요한것이다.

 

OnUpdate는 기존의 Update와 비슷한 개념이다.

 

var elapsed = (float)SystemAPI.Time.ElapsedTime; 는 현재 경과 시간을 SystemAPI에서 가져와서 변수에 저장하는 부분이다.

 

아래는 SystemAPI의 Query를 사용해서 Dancer와 LocalTransform 두가지 모두를 갖는 엔티티를 열거한다.

 

이 Query 방식에서는 RefRO와 RefRW라는 접근자로 컴포넌트 데이터를 참조할 수 있다.

여기서 값을 수정하지 않고 참조할 경우는 'Read Only' 를 뜻하는 RefRO를 사용하고,

값을 수정할 경우에는 'Read Write'를 뜻하는 RefRW를 사용한다.

 

그리고 foreach안에 들어있는 내용은 컴포넌트별 설정값 및 경과시간을 이용하여 오브젝트의 움직임을 계산하고 적용하는 로직이 구현되어있다.

 

코드는 여기까지이다.

 

이제 유니티에디터에서 적용하려면 어떻게 해야할까?

 

subscene에 빈 오브젝트를 만들고 MeshFilter, MeshRenderer, DancerAuthoring를 추가하자.

 

아까 작성한 DancerSystem은 적용할 필요가 없다. 자동으로 실행되어서 처리한다.

 

그리고 이상태에서 play버튼을 누르면 자동으로 오브젝트가 춤을 출것이다.

 

이제 여기서 위와 같은 방식으로 WalkerAuthoring과 WalkerSystem을 구현했다고 가정해보자.

 

구현은 특정 구역을 기점으로 빙글빙글 돌고있는 로직이라고 가정하고

 

오브젝트에 MeshFilter, MeshRenderer, DancerAuthoring, WalkerAuthoring 이렇게 4개의 컴포넌트를 붙히면 어떻게 될까 ?

 

춤을 추면서 이동을 하는 오브젝트가 완성이 된다.

 

그리고 여기서 춤을 추는 기능과 이동을 하는 기능이 모두 구현된 오브젝트는 스케일을 조정하는 System만 만들어두었다고 치자.

 

그리고 A오브젝트는 춤을 추는 컴포넌트, B는 걷는 컴포넌트, C는 두가지 모두를 붙혀주면

 

위에서만든 스케일을 조정하는 System은 구현만으로 C의 스케일을 조정한다.

 

이렇게 컴포넌트를 조합하기만 하면 오브젝트의 동작을 만들 수 있다.

 

나는 이부분이 너무 매력적으로 느껴진다.

 

ECS는 성능도 물론 아주 매력적이다.

 

하지만 OOP의 단점을 극복하는것이 너무 매력적이다. 예를 들어 체와 B객체가 있다면 두 객체를 연결할때

 

'상호작용' 을 구현해야한다. 이로 인해 아무리 노력한다 해도 결국 A객체가 바뀌면 '상호작용'은 대부분의 경우 만져줘야한다.

 

이런부분에서 ECS가 너무 매력적으로 느껴진다.

 

처음 OOP를 공부할때 느꼈던 왜 이렇게까지 하는거지 ? 를 해결해주는 기술인것같다.