ECS – Component concepts (2)

4. 여러 종류의 Component

(5) Dynamic buffer components

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer.html

1) Dynamic buffer 구성 요소 소개

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-introducing.html

Dynamic buffer components는 관리되지 않는 구조체들의 크기 조절이 가능한 배열처럼 작동하는 컴포넌트

예를 들어 Entity가 이동할 경로점(waypoint) 위치들과 같은 배열 데이터를 저장하는 데 사용할 수 있음

Dynamic buffer의 구성 요소

Dynamic buffer는 데이터와 함께 길이(Length), 용량(Capacity), 내부 포인터(pointer)를 저장

  1. 길이 (Length):
    buffer 에 저장된 요소의 개수, 처음에는 0이며, 값을 추가할 때마다 증가
  2. 용량 (Capacity):
    buffer 에 저장할 수 있는 요소의 최대 개수
    처음에는 내부 버퍼 용량과 동일하게 시작하고, 용량을 설정하여 버퍼 크기를 조정
  3. 포인터 (Pointer):
    Dynamic buffer 데이터의 위치를 나타냄
    처음에는 데이터가 Entity의 Chunk 내에 있을 때 null로 설정되며,
    Unity가 데이터를 Chunk 밖으로 이동시키면 포인터가 새 배열을 가리키도록 설정됩니다.
Dynamic buffer 용량

Dynamic buffer의 초기 용량은 버퍼가 저장하는 타입에 의해 정의됩니다.

기본적으로 용량은 128바이트에 맞게 설정됩니다.

만약 더 큰 용량이 필요하면 InternalBufferCapacity 속성을 사용하여 커스터마이즈할 수 있습니다.

구조적 변경(Structural Changes)

구조적 변경(Structural Changes) : Entity의 생성, 삭제, 또는 컴포넌트 추가/제거와 같은 작업이 수행될 때 Unity의 ECS 시스템 내부에서 구조적 변화가 발생함

Structural ChangesDynamic buffer가 참조하는 배열을 파괴하거나 이동시킬 수 있기 때문에, 구조적 변화 후에는 Dynamic buffer에 대한 Handle이 무효화됩니다.

Structural Changes 후에는 Dynamic buffer를 다시 가져와야 합니다.

예를 들어:

public void DynamicBufferExample(Entity e)
{
    // MyElement 타입의 동적 버퍼를 가져옵니다.
    DynamicBuffer<MyElement> myBuff = EntityManager.GetBuffer<MyElement>(e);

    // 이 Structural Changes는 이전에 얻은 DynamicBuffer를 무효화시킵니다.
    EntityManager.CreateEntity();

    // 안전 검사로 인해 버퍼에 대한 읽기/쓰기 작업에서 예외가 발생합니다.
    var x = myBuff[0];

    // Structural Changes 후 동적 버퍼를 다시 가져옵니다.
    myBuff = EntityManager.GetBuffer<MyElement>(e);
    var y = myBuff[0];
}
네이티브 컨테이너와의 비교

Dynamic buffer는 네이티브 컨테이너가 컴포넌트에서 사용하는 작업 스케줄링 제한이 없으므로, 가능한 경우 코드에서 Dynamic buffer를 사용하는 것이 더 나은 선택입니다.

또한, Dynamic buffer는 Chunk 내에 인라인으로 저장될 수 있어 메모리 대역폭 사용을 줄이는 데 도움을 줍니다.

일반적으로, 여러 Entity가 동일한 컬렉션을 필요로 한다면 Dynamic buffer를 사용하는 것이 좋습니다.

만약 하나의 Entity만 필요하다면 네이티브 컨테이너를 가진 싱글톤 컴포넌트로 처리하는 것이 좋을 수 있습니다.

  • Dynamic buffer작업 스케줄링 제한이 없고, 메모리 사용 측면에서도 효율적
비교 항목동적 버퍼 (DynamicBuffer<T>)NativeContainer (NativeArray<T>, NativeList<T>)
ECS와의 연동✅ Entity에 직접 부착 가능❌ ECS Entity와 직접 연결 불가
Job 시스템 사용✅ 가능 (ReadOnly 또는 ParallelForEach 가능)✅ 가능 ([NativeContainer] 필요)
메모리 위치Chunk 내 저장 (최적화됨) → 크기 초과 시 자동 이동Chunk 외부 할당
구조적 변화 영향🚨 구조적 변화 시 무효화됨 → 재할당 필요✅ 영향 없음
  • 여러 개의 엔티티가 배열 데이터를 가질 필요가 있다면 DynamicBuffer<T>를 사용하는 것이 좋음
  • 싱글톤 데이터처럼 전역적으로 관리할 경우 NativeContainer를 사용하는 것이 유리함

2) Dynamic buffer 컴포넌트 생성 및 사용 방법

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-create.html

IBufferElementData를 상속한 구조체 생성

Dynamic buffer의 개별 요소를 정의하는 구조체를 만들고, IBufferElementData를 상속받아야 합니다.

using Unity.Entities;

// Dynamic buffer의 요소가 되는 구조체 정의
// 기본적으로 16개의 요소를 저장할 수 있도록 설정
// 만약 16개를 초과하면 Unity가 자동으로 Chunk 외부 메모리로 확장
[InternalBufferCapacity(16)]
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value; // 버퍼 요소 값
}
Entity에 Dynamic buffer 추가하기

DynamicBuffer<T>를 사용하여 엔티티에 동적 버퍼를 추가하고, 값을 삽입

Entity entity = EntityManager.CreateEntity();
DynamicBuffer<ExampleBufferComponent> buffer = EntityManager.AddBuffer<ExampleBufferComponent>(entity);

// 데이터 추가
buffer.Add(new ExampleBufferComponent { Value = 10 });
buffer.Add(new ExampleBufferComponent { Value = 20 });

// 현재 길이 확인 (2개 요소가 들어있음)
UnityEngine.Debug.Log("Buffer Length: " + buffer.Length);
Dynamic buffer 읽기

Dynamic buffer는 배열처럼 접근할 수 있습니다.

DynamicBuffer<ExampleBufferComponent> buffer = EntityManager.GetBuffer<ExampleBufferComponent>(entity);

for (int i = 0; i < buffer.Length; i++)
{
    UnityEngine.Debug.Log("Buffer Element " + i + ": " + buffer[i].Value);
}
동적 버퍼에서 요소 제거

요소를 제거할 때는 RemoveAt() 또는 Clear()를 사용할 수 있습니다.

buffer.RemoveAt(0); // 첫 번째 요소 제거
buffer.Clear(); // 모든 요소 제거
동적 버퍼의 사용 예시
  • 웨이포인트(Waypoints) 저장하기
using Unity.Entities;
using Unity.Mathematics;

// 웨이포인트를 저장하는 동적 버퍼
[InternalBufferCapacity(4)]
public struct WaypointBuffer : IBufferElementData
{
    public float3 Position; // 좌표 정보
}
// 엔티티에 웨이포인트 버퍼 추가
Entity entity = EntityManager.CreateEntity();
DynamicBuffer<WaypointBuffer> waypoints = EntityManager.AddBuffer<WaypointBuffer>(entity);

// 웨이포인트 추가
waypoints.Add(new WaypointBuffer { Position = new float3(0, 0, 0) });
waypoints.Add(new WaypointBuffer { Position = new float3(1, 0, 0) });
waypoints.Add(new WaypointBuffer { Position = new float3(2, 0, 0) });
  • 시스템에서 동적 버퍼 활용

ISystem을 활용하여 동적 버퍼를 검색하고 처리하는 시스템을 구현

using Unity.Entities;
using Unity.Burst;
using Unity.Collections;
using Unity.Mathematics;

[BurstCompile]
public partial struct WaypointSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (waypoints, entity) in SystemAPI.Query<DynamicBuffer<WaypointBuffer>>().WithEntityAccess())
        {
            foreach (var waypoint in waypoints)
            {
                UnityEngine.Debug.Log("Waypoint Position: " + waypoint.Position);
            }
        }
    }
}

3) Dynamic Buffer 용량 설정

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-set-capacity.html

Dynamic Buffer의 초기 용량 (Default Capacity)
  • Dynamic Buffer는 초기 용량을 가지고 있으며, 기본적으로 128바이트 크기로 설정
  • 128바이트는 TypeManager.DefaultBufferCapacityNumerator를 통해 결정
  • 예를 들어, int(4바이트) 값을 저장하는 버퍼라면 기본적으로 32개의 요소를 저장할 수 있음
Dynamic Buffer의 메모리 저장 방식

초기 상태

  • Entity의 아키타입 청크(Archetype Chunk) 내부에 저장됨.
  • 일반적인 IComponentData처럼 빠르게 접근 가능.

버퍼 크기가 초기 용량을 초과할 경우

  • Chunk 외부에 새로운 메모리 공간을 할당하고 데이터를 복사함.
  • 이후 해당 버퍼는 항상 Chunk 외부 메모리를 사용하게 됨.
  • 단점:
    • 메모리 할당 비용 증가
    • CPU 캐시 효율 저하 (Chunk 내부에 있던 데이터가 캐시에서 빠짐)
    • Chunk 내부에 빈 공간(fragmentation)이 생겨서 메모리 낭비 발생
Dynamic Buffer의 내부 용량 설정 (InternalBufferCapacity)

초기 용량을 커스텀 설정하려면 InternalBufferCapacity 속성을 사용

// 청크 내부에 최대 42개의 요소를 저장 가능
// 43개 이상 추가 시, 자동으로 청크 외부 메모리로 이동됨
[InternalBufferCapacity(42)]
public struct MyBufferElement : IBufferElementData
{
    public int Value;
}

초기 용량을 0으로 설정하면?

  • 버퍼의 모든 데이터가 청크 외부 메모리에 저장됨.
  • 버퍼 크기가 계속 변하는 경우 유리 (즉, 초기에 작은 용량을 두지 않음).
[InternalBufferCapacity(0)]
public struct MyBufferElement : IBufferElementData
{
    public int Value;
}

최적화

  • 버퍼 크기를 예상할 수 있다면 InternalBufferCapacity를 적절히 설정
  • 버퍼 크기가 자주 변경되면 InternalBufferCapacity(0)을 사용하여 메모리 재할당 비용을 줄임
동적으로 용량 조절 (EnsureCapacity & TrimExcess)
  • 실행 중에 버퍼 용량을 설정하려면 EnsureCapacity() 사용
DynamicBuffer<MyBufferElement> buffer = entityManager.GetBuffer<MyBufferElement>(entity);
buffer.EnsureCapacity(100); // 100개 요소를 저장할 수 있도록 미리 용량 할당
  • 불필요한 메모리를 줄이려면 TrimExcess() 사용
buffer.TrimExcess(); // 현재 길이에 맞게 불필요한 메모리 정리

최적화

  • 자주 Add()로 데이터를 추가하는 경우 EnsureCapacity()를 미리 호출하면 성능 향상
  • 더 이상 사용하지 않는 메모리를 줄이려면 TrimExcess() 사용
Dynamic Buffer보다 더 나은 대안?
  • Dynamic Buffer의 용량 제한이 문제라면, 다음과 같은 대안 고려 가능
    1. Blob Assets
      • 불변(읽기 전용) 데이터를 저장하는 데 적합
      • 여러 엔티티가 공유 가능
      • 멀티스레딩 접근 가능
    2. NativeContainer + IComponentData 조합
      • NativeArray<T> 또는 NativeList<T> 등을 사용하여 별도로 관리 가능

4) Chunk 내 Dynamic Buffer 접근 방법

모든 동적 버퍼를 한 번에 가져오려면 ArchetypeChunk.GetBufferAccessor() 사용

GetBufferAccessor<T>()BufferAccessor<T>를 반환하며, 이를 통해 Chunk 내 모든 Entity의 버퍼에 접근 가능

필요한 요소

  1. EntityQueryBuilder로 Entity 쿼리 생성
  2. EntityQuery에서 Chunk 배열(NativeArray<ArchetypeChunk>) 가져오기
  3. BufferTypeHandle<T>을 사용하여 Dynamic Buffer 핸들 생성
  4. GetBufferAccessor<T>()Chunk 내 모든 버퍼 가져오기
  5. 반복문을 통해 버퍼 데이터 처리

Dynamic Buffer 접근 시스템 (SystemBase 기반)

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;

[InternalBufferCapacity(16)] // 내부 용량을 16개 요소로 설정 (초과 시 Chunk 외부 메모리로 이동)
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value;
}


public partial class ExampleSystem : SystemBase
{
    protected override void OnUpdate()
    {
        // EntityQuery를 생성하여 ExampleBufferComponent를 가진 Entity 필터링
        var query = new EntityQueryBuilder(Allocator.Temp)
            .WithAllRW<ExampleBufferComponent>() // 읽기/쓰기 가능한 버퍼 포함
            .Build(EntityManager);

        // 쿼리를 통해 ArchetypeChunk 배열 가져오기
        NativeArray<ArchetypeChunk> chunks = query.ToArchetypeChunkArray(Allocator.Temp);

        // Chunk 순회하면서 처리
        for (int i = 0; i < chunks.Length; i++)
        {
            UpdateChunk(chunks[i]);
        }
        
        // 메모리 해제
        chunks.Dispose();
    }

    private void UpdateChunk(ArchetypeChunk chunk)
    {
        // ExampleBufferComponent에 대한 BufferTypeHandle 가져오기
        BufferTypeHandle<ExampleBufferComponent> bufferTypeHandle = GetBufferTypeHandle<ExampleBufferComponent>();

        // Chunk에서 BufferAccessor 가져오기
        BufferAccessor<ExampleBufferComponent> buffers = chunk.GetBufferAccessor(ref bufferTypeHandle);

        //  Chunk 내 모든 Entity 의 Dynamic Buffer 접근
        for (int i = 0, chunkEntityCount = chunk.Count; i < chunkEntityCount; i++)
        {
            DynamicBuffer<ExampleBufferComponent> buffer = buffers[i];

            // 각 Entity의 동적 버퍼 요소 접근
            for (int j = 0; j < buffer.Length; j++)
            {
                UnityEngine.Debug.Log($"Buffer Element {j}: {buffer[j].Value}");
            }
        }
    }
}

Dynamic Buffer 접근 시스템 (ISystem 기반)

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;

[InternalBufferCapacity(16)]
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value;
}

[BurstCompile] // 성능 최적화
public partial struct ExampleSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        // EntityQuery를 생성하여 ExampleBufferComponent를 가진 Entity 필터링
        EntityQuery query = SystemAPI.QueryBuilder()
            .WithAllRW<ExampleBufferComponent>() // 읽기/쓰기 가능한 버퍼 포함
            .Build();

        // 쿼리를 통해 ArchetypeChunk 배열 가져오기
        NativeArray<ArchetypeChunk> chunks = query.ToArchetypeChunkArray(Allocator.Temp);

        // BufferTypeHandle 가져오기
        BufferTypeHandle<ExampleBufferComponent> bufferTypeHandle = state.GetBufferTypeHandle<ExampleBufferComponent>();

        // 모든 Chunk 순회하면서 Dynamic Buffer 처리
        foreach (var chunk in chunks)
        {
            UpdateChunk(chunk, ref bufferTypeHandle);
        }

        // NativeArray 해제
        chunks.Dispose();
    }

    private void UpdateChunk(ArchetypeChunk chunk, ref BufferTypeHandle<ExampleBufferComponent> bufferTypeHandle)
    {
        // Chunk에서 BufferAccessor 가져오기
        BufferAccessor<ExampleBufferComponent> buffers = chunk.GetBufferAccessor(ref bufferTypeHandle);

        // Chunk 내 모든 Entity의 Dynamic Buffer 접근
        for (int i = 0; i < chunk.Count; i++)
        {
            DynamicBuffer<ExampleBufferComponent> buffer = buffers[i];

            // 각 Entity의 Dynamic Buffer 요소 접근
            for (int j = 0; j < buffer.Length; j++)
            {
                UnityEngine.Debug.Log($"Buffer Element {j}: {buffer[j].Value}");
            }
        }
    }
}

5) 여러 Entity에서 동일한 Dynamic Buffer 재사용

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-reuse.html

여러 Entity에서 동일한 동적 버퍼 사용하기

  • IJobEntity 내에서 같은 DynamicBuffer를 공유하는 방법
  • 동일한 동적 버퍼를 여러 엔티티에 적용하는 경우, 메인 스레드에서 먼저 버퍼를 가져온 후 Job에 전달

SystemBase 기반 예제

using Unity.Entities;
using Unity.Burst;
using Unity.Jobs;

public struct MyElement : IBufferElementData
{
    public int Value;
}

public struct OtherComponent : IComponentData {}

public partial class ExampleSystem : SystemBase
{
    protected override void OnUpdate()
    {
        // 특정 엔티티에서 DynamicBuffer 가져오기
        Entity e = GetSingletonEntity<MyElement>();
        DynamicBuffer<MyElement> myBuffer = SystemAPI.GetBuffer<MyElement>(e);

        // Job에 버퍼 전달하여 여러 엔티티에서 사용
        new MyJobEntity { MyBuffer = myBuffer }.Schedule();
    }

    [BurstCompile]
    public partial struct MyJobEntity : IJobEntity
    {
        public DynamicBuffer<MyElement> MyBuffer;

        public void Execute(in OtherComponent other)
        {
            // MyBuffer를 모든 엔티티에서 공유
            for (int i = 0; i < MyBuffer.Length; i++)
            {
                UnityEngine.Debug.Log($"Shared Buffer Element: {MyBuffer[i].Value}");
            }
        }
    }
}

ScheduleParallel을 사용할 때 주의할 점

  • ScheduleParallel을 사용하면 다중 스레드 환경에서 DynamicBuffer를 직접 수정할 수 없음
  • EntityCommandBuffer.ParallelWriter를 활용해야 함
  • 그러나 구조적 변경(Structural Change)이 발생하면 버퍼가 무효화됨

ScheduleParallel을 사용할 때 EntityCommandBuffer 활용 예제

protected override void OnUpdate()
{
    Entity e = GetSingletonEntity<MyElement>();
    DynamicBuffer<MyElement> myBuffer = SystemAPI.GetBuffer<MyElement>(e);

    EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob);
    EntityCommandBuffer.ParallelWriter ecbParallel = ecb.AsParallelWriter();

    Entities
        .WithAll<OtherComponent>()
        .ForEach((Entity entity, int entityInQueryIndex) =>
        {
            // EntityCommandBuffer를 사용하여 동적 버퍼에 변경 적용
            var buffer = ecbParallel.AddBuffer<MyElement>(entityInQueryIndex, entity);
            buffer.CopyFrom(myBuffer);
        }).ScheduleParallel();

    Dependency.Complete();
    ecb.Playback(EntityManager);
    ecb.Dispose();
}

최적화

  • 같은 버퍼를 여러 Entity에서 사용하면 메모리 절약 가능
  • ScheduleParallel을 사용할 때는 EntityCommandBuffer.ParallelWriter 필요
  • Dynamic Buffer 변경 시 구조적 변화(Structural Changes) 발생 가능 → 버퍼 재획득 필요
  • 캐시 효율을 고려하여 버퍼 크기 최적화 (InternalBufferCapacity)

동일한 Dynamic Buffer를 재사용하면 성능과 메모리 사용량을 최적화

6) Job에서 DynamicBuffer 접근 및 활용

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-jobs.html

Job에서 DynamicBuffer 접근

  • IJobEntity에서 여러 엔티티의 Dynamic Buffer를 조회하려면 BufferLookup<T>를 사용해야 함
  • BufferLookup<T>EntityManager에서 Dynamic Buffer를 효율적으로 검색할 수 있도록 하는 Lookup Table

BufferLookup<T>를 활용한 Job 예제

using Unity.Entities;
using Unity.Burst;
using Unity.Collections;

[InternalBufferCapacity(16)]
public struct ExampleBufferComponent : IBufferElementData
{
    public int Value;
}

[BurstCompile]
public partial struct AccessDynamicBufferJob : IJobEntity
{
    // ReadOnly BufferLookup 멤버 변수를 추가
    [ReadOnly] public BufferLookup<ExampleBufferComponent> BufferLookup;

    public void Execute(Entity entity)
    {
        if (BufferLookup.HasBuffer(entity))
        {
            DynamicBuffer<ExampleBufferComponent> buffer = BufferLookup[entity];
            for (int i = 0; i < buffer.Length; i++)
            {
                UnityEngine.Debug.Log($"Entity {entity.Index} - Buffer[{i}] = {buffer[i].Value}");
            }
        }
    }
}

System에서 BufferLookup<T> 관리하기 (ISystem 버전)

public partial struct AccessDynamicBufferFromJobSystem : ISystem
{
    private BufferLookup<ExampleBufferComponent> _bufferLookup;

    public void OnCreate(ref SystemState state)
    {
        _bufferLookup = state.GetBufferLookup<ExampleBufferComponent>(true);
    }

    public void OnUpdate(ref SystemState state)
    {
        _bufferLookup.Update(ref state);

        var job = new AccessDynamicBufferJob { BufferLookup = _bufferLookup };
        job.ScheduleParallel();
    }
}

BufferLookup<T>는 매 프레임 Update()를 호출해야 최신 데이터를 유지할 수 있음
ScheduleParallel()을 사용하면 여러 Entity에서 병렬 처리 가능

7) EntityCommandBuffer(ECB)에서 DynamicBuffer 수정

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-command-buffer.html

ECB(EntityCommandBuffer)미래에 실행될 변경 사항을 기록하는 역할을 하며, DynamicBuffer에 대한 특별한 API를 제공

using Unity.Entities;
using Unity.Collections;

public struct MyElement : IBufferElementData
{
    public int Value;
}

public partial struct ExampleECBSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        var ecb = new EntityCommandBuffer(Allocator.TempJob);
        
        Entity entity = SystemAPI.GetSingletonEntity<MyElement>(); 
        Entity otherEntity = SystemAPI.GetSingletonEntity<MyElement>();

        // 버퍼 삭제
        ecb.RemoveComponent<MyElement>(entity);

        // 새 버퍼 추가 및 초기화
        DynamicBuffer<MyElement> myBuff = ecb.AddBuffer<MyElement>(entity);
        myBuff.Add(new MyElement { Value = 5 });
        myBuff.Add(new MyElement { Value = -9 });

        // 기존 버퍼 덮어쓰기 (없으면 예외 발생)
        DynamicBuffer<MyElement> otherBuf = ecb.SetBuffer<MyElement>(otherEntity);
        otherBuf.Add(new MyElement { Value = 10 });

        // 버퍼에 요소 추가 (버퍼가 없으면 예외 발생)
        ecb.AppendToBuffer(otherEntity, new MyElement { Value = 12 });

        ecb.Playback(state.EntityManager);//  Playback()이 호출될 때 실제 변경이 적용됨
        ecb.Dispose();
    }
}
  • ECB는 Playback()이 호출될 때 실제 변경이 적용됨
  • SetBuffer<T>() → 기존 버퍼가 없으면 예외 발생
  • AppendToBuffer<T>() → 버퍼가 없으면 예외 발생 (안전하게 사용하려면 AddComponent<T>() 선행)
  • AddBuffer<T>() → 없으면 추가하고, 있으면 기존 내용을 덮어씀

8) Reinterpret()을 이용한 버퍼 변환

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-buffer-reinterpret.html

Reinterpret<T>()을 사용하면 같은 크기의 다른 타입으로 변환 가능

단, 메모리 크기가 같은 경우에만 가능 (int ↔ float, struct ↔ int 등)

public partial class ExampleSystem : SystemBase
{
    private void ReinterpretEntitysChunk(Entity e)
    {
        DynamicBuffer<MyElement> myBuff = EntityManager.GetBuffer<MyElement>(e);

        // MyElement의 Value 필드가 int이므로 int 버퍼로 변환 가능
        DynamicBuffer<int> intBuffer = myBuff.Reinterpret<int>();

        intBuffer[2] = 6;  // 동일한 메모리를 참조하므로 myBuff[2]도 변경됨

        MyElement myElement = myBuff[2];
        Debug.Log(myElement.Value);    // 6
    }
}

Reinterpret<T>()메모리를 공유하므로, 한쪽을 수정하면 다른 쪽도 변경됨
안전성 검토 필수 (struct 내부 메모리 크기를 고려해야 함)

Reinterpret<T>()를 사용하는 주요 이유

  1. 데이터 변환 효율성
  • 실제 메모리 복사 없이 같은 메모리를 다른 타입으로 해석할 수 있음
  • 큰 데이터 셋을 다룰 때 성능상 이점이 큼
  • 메모리 할당 오버헤드 방지
  1. 데이터 호환성
// 예: 네트워크나 파일에서 받은 바이트 데이터를 직접 구조체로 해석
DynamicBuffer<byte> rawData = ...;
DynamicBuffer<MyStruct> structData = rawData.Reinterpret<MyStruct>();
  1. 저수준 최적화
  • 예를 들어 float 배열을 int 비트 패턴으로 직접 조작해야 할 때
  • SIMD 연산이나 특수한 비트 연산을 수행할 때 유용
  1. 다양한 데이터 뷰 제공
// 같은 메모리를 다른 관점에서 접근 가능
DynamicBuffer<Vector3> positions = ...;
DynamicBuffer<float> rawFloats = positions.Reinterpret<float>();

주의사항:

  • 메모리 크기가 정확히 일치해야 함
  • 타입 안전성이 깨질 수 있으므로 신중히 사용
  • 데이터 정렬(alignment) 고려 필요

“ECS – Component concepts (2)”에 대한 1개의 생각

  1. 핑백: ECS – Component concepts (3) - 어제와 내일의 나 그 사이의 이야기

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤