플라네타리움 엔지니어링 스낵

Unity DOTS DynamicBuffer 사용하기

(English)

안녕하세요. 플라네타리움에서 나인 크로니클을 개발하고 있는 현승민입니다. 해당 프로젝트는 아직 Unity DOTS를 사용하고 있지 않지만, 차기작에 적용하기 위해 열심히 공부 중인데요. 앞으로 공부한 내용을 꾸준히 공유해보려고 해요.

이번에는 DynamicBuffer<T>에 대해서 알아 볼게요. 엔티티에 동적 버퍼를 설정하고 이를 사용하는 방법에 대한 것인데요. DOTS와 관련한 첫 번째 글 치고는 몇 단계를 넘어 오기는 했지만 그 양이 적으니 관련한 내용을 함께 보시면 바로 이해하실 수 있을 것이라 생각해요.

이 글은 Unity 공식 문서튜토리얼 영상을 참고했어요.

개발 환경

Unity
2019.3.12f1
com.unity.entities
0.10.0-preview.6

IBufferElementData 구현하기

엔티티에 더하는 컴포넌트가 IComponentData 인터페이스를 구현해야 하는 것과 마찬가지로, DynamicBuffer<T> 또한 IBufferElementData 인터페이스를 구현해야 해요.

EntityManager.AddBuffer<T>() 사용하기

엔티티에 컴포넌트를 더하는 방법과 같이 버퍼를 더할 때도 EntityManager를 활용해요. 아래에서는 게임 오브젝트에 더해서 사용할 PlayModeTest라는 컴포넌트를 작성하고 플레이 모드에서 Entity Debugger를 확인해 볼게요.

DynamicBuffer<T>.Reinterpret<U>() 사용하기

버퍼에 담긴 구조체가 포함하는 값을 직접 수정하는 방법을 알아 볼게요.

EntityManager.GetBuffer<T>() 사용하기

엔티티의 버퍼에 접근하는 방법도 필요하겠죠?

Authoring

GenerateAuthoringComponentAttribute를 적용하면 게임 오브젝트에 Authoring Component를 더해서 엔티티로 만들 수 있죠. IBufferElementData도 같은 방법을 사용할 수 있어요.

ComponentSystem에서 사용하기

ComponentSystem을 상속하는 시스템을 작성해서 UnitTag 컴포넌트를 포함하는 엔티티의 IntBufferElement DynamicBuffer에 접근해 볼게요.

JobComponentSystem에서 사용하기

JobComponentSystem을 상속하는 시스템을 작성해서 PlayerTag 컴포넌트를 포함하는 엔티티의 IntBufferElement DynamicBuffer에 접근해 볼게요.

팁들

InternalBufferCapacityAttribute

엔티티는 기본적으로 청크에 포함되는데, IBufferElementData를 구현하는 구조체에 InternalBufferCapacityAttribute를 적용하면 청크 내 존재할 수 있는 최대 요소 수를 지정할 수 있어요. 지정한 요소 수를 넘어서면 해당 버퍼는 힙 메모리로 이동해요. 물론 이때에도 이전과 같이 DynamicBuffer API로 해당 버퍼에 접근할 수 있어요.

implicit 연산자

편의를 위해서 이렇게 작성해서 사용할 수도 있겠죠?

using Unity.Entities;

namespace DOTS_DynamicBuffer
{
  // InternalBufferCapacity specifies how many elements a buffer can have before
  // the buffer storage is moved outside the chunk.
  [InternalBufferCapacity(2)]
  [GenerateAuthoringComponent]
  public struct IntBufferElement : IBufferElementData
  {
    public int Value;

    // The following implicit conversions are optional, but can be convenient.
    public static implicit operator int(IntBufferElement e)

    {
      return e.Value;
    }

    public static implicit operator IntBufferElement(int e)

    {
      return new IntBufferElement { Value = e };
    }
  }
}

마치며

IBufferElementDataDynamicBuffer<T>를 가볍게 알아 봤어요.

게임을 만들 때 오브젝트 풀링에 대해서 수도 없이 많이 들어 보셨을 거예요. 1회용 객체를 생성하는 것은 쓰레기를 만드는 것이기에 풀링해서 재사용하면 잦은 쓰레기 수집을 줄이고 인스턴스 생성 타이밍을 관리할 수 있어서 더욱 부드러운 게임을 만들 수 있죠.

다음에는 이 기능을 게임에 어떻게 적용하는지 알아보고, 적용하기 전과 후를 비교하면서 어느정도 효과를 얻을 수 있는지 확인해 볼게요.

플라네타리움은 게임에 특화된 오픈 소스 P2P 라이브러리 Libplanet과, 그 위에서 중앙 서버 없는 온라인 게임 〈나인 크로니클〉을 만들고 있습니다. 저희와 흥미로운 기술적 도전을 함께 하실 분들을 모시고 있습니다. 지금 인재 영입 페이지를 확인해주세요!