본문 바로가기
개발/Unity

[Unity][DOTS] 계층 구조의 GameObject를 ECS Entity로 변환하기

by 가리봉맨 2019. 8. 23.
반응형

ECS 프레임웍은 기존 개발 방식의 GameObject를 ECS Entity로 변환해 주는 API를 제공한다. GameObjectConversionUtility 클래스의 ConvertGameObjectHierarchy()라는 이름의 메서드다. 자세한 스펙은 아래 문서에서 확인할 수 있다.

https://docs.unity3d.com/Packages/com.unity.entities@0.0/api/Unity.Entities.GameObjectConversionUtility.html

 

Class GameObjectConversionUtility | Package Manager UI website

Class GameObjectConversionUtility Inheritance System.Object GameObjectConversionUtility Assembly : solution.dll Syntax public static class GameObjectConversionUtility Methods ConvertGameObjectHierarchy(GameObject, World) Declaration public static Entity Co

docs.unity3d.com

이 메서드의 파라미터에 기존 GameObject를 넘겨주면 결과 값으로 ECS Entity를 리턴한다. 파라미터로 GameObject 뿐만 아니라 프리팹(prefab)도 넣을 수 있다. 일반적으로 프리팹은 부모 자식 관계를 갖는 다수의 GameObject들이 조합된 계층 구조를 갖는다. ConvertGameObjectHierarchy() 메서드는 계층 구조를 깨뜨리지 않고 그대로 변환해 준다. 따라서 기존 프리팹을 추가 작업 없이 사용하면서 ECS의 성능 향상 효과를 볼 수 있는 것이다.

다음 그림은 계층 구조의 프리팹의 예다. 실제 프로젝트에 사용하는 이미지를 공개할 수 없어서 간단하게 만들어봤다.

가운데 커다란 큐브(cube)가 부모 GameObject다. 위, 왼쪽, 오른쪽에 자식 GameObject들이 붙는 형태다. 이를 ECS로 변환하면 각 큐브에 붙어 있는 MeshRenderer 컴포넌트가 ECS Component, RenderMesh로 변환된다. 실행해보면 정상적으로 렌더링되고, 계층 구조가 깨지지 않은 것을 Entity Debugger에서 확인할 수 있다.

일반적인 경우라면 여기까지가 끝이다. 하지만 현재 진행 중인 프로젝트에서는 오브젝트 간 depth 문제로 RenderMesh를 사용할 수 없었다. 해결책은 두 가지다. 첫번째 방법은 기존 변환 API를 사용한 뒤, RenderMesh만 제거하는 것이다. 두 번째 방법은 변환 API를 직접 만드는 것이다. 힘들 것 같지만 프리팹에 붙은 컴포넌트의 종류가 적다면 불가능한 것도 아니다. 프로젝트에서 사용하는 프리팹에는 Transform, MeshRenderer 컴포넌트만 붙어있었다. 특정 위치에 오브젝트를 렌더링하는 기능만 있는 것이다. 그래서 두 번째 방법을 선택했다.

직접 작성한 변환 API는 Transform 컴포넌트를 Translation, Rotation, NonUniformScale로 나눠서 변환한다. MeshRenderer는 RenderMesh로 변환하지 않고, 아래 코드와 같이 Graphics::DrawMesh()를 사용해서 메쉬를 직접 렌더링했다.

public struct RotatingCubeRenderMesh : ISharedComponentData,  IEquatable<RotatingCubeRenderMesh>
{
       public Mesh mesh;
       public Material material;
       ...
}
public class RotatingCubeRenderSystem : ComponentSystem
{
       protected override void OnUpdate()
       {
              Entities.ForEach((RotatingCubeRenderMesh rendering, ref LocalToWorld  localToWorld, ref Rotation rotation, ref NonUniformScale scale) =>
              {
                      Matrix4x4 matrix = Matrix4x4.TRS(localToWorld.Position,  rotation.Value, new Vector3(scale.Value.x, scale.Value.y, scale.Value.z));
                      Graphics.DrawMesh(rendering.mesh, matrix, rendering.material,  0);
              });
       }
}

RotatingCubeRenderMesh은 Mesh와 Material을 멤버로 갖는 ECS Component다. RotatingCubeRenderSystem은 이 Component를 처리하는 ECS System이다.

다음으로 계층 구조를 직접 구현했다. 변환이 완료된 Entity에 다음 ECS Component를 추가했다. 부모 Entity에 LocalToWorld, 자식 Entity에는 LocalToWorld, Parent, LocalToParent를 추가했다. 그러면 각 Component를 처리하는 ECS System에 의해 계층 구조가 만들어진다. Entity에 Component를 추가하는 것만으로 어떻게 계층 구조가 만들어지는 아래 문서에 자세히 설명돼 있다.

https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/transform_system.html

 

TransformSystem | Package Manager UI website

TransformSystem Section 1: Non-hierarchical Transforms (Basic) LocalToWorld (float4x4) represents the transform from local space to world space. It is the canonical representation and is the only component and can be relied upon to communicate local space

docs.unity3d.com

아래 그림은 직접 작성한 변환 API로 변환한 Entity를 실행한 결과 화면이다.

정상적으로 화면에 렌더링된다.

끝.

반응형

댓글