Unity 사용자 매뉴얼 2022.3 (LTS)
스크립팅
이 문서에서는 Unity의 커스텀 NativeContainer
예제를 다룹니다. 이 자료는 자주 사용할 수 있는 팁과 코드를 포함하고 있습니다.
1. 커스텀 NativeContainer 예제
NativeContainer
는 Unity의 잡 시스템에서 사용되는 데이터 구조입니다. 기본적으로, 이 구조는 ATOMIC 세이프티 핸들을 포함해야 하며, 스레드 안전성을 보장합니다. 아래 코드는 안전한 읽기 및 쓰기 작업을 위한 기본 구조를 정의한 것입니다.
코드 스니펫
using System;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Collections;
// 구조체를 NativeContainer로 표시합니다.
[NativeContainer]
public unsafe struct NativeAppendOnlyList<T> : IDisposable where T : unmanaged
{
[NativeDisableUnsafePtrRestriction]
internal void* m_Buffer;
internal int m_Length;
internal Allocator m_AllocatorLabel;
// AtomicSafetyHandle 필드를 반드시 'm_Safety'라는 이름으로 지정합니다.
# if ENABLE_UNITY_COLLECTIONS_CHECKS
internal AtomicSafetyHandle m_Safety;
internal static readonly int s_staticSafetyId = AtomicSafetyHandle.NewStaticSafetyId<NativeAppendOnlyList<T>>();
# endif
public NativeAppendOnlyList(Allocator allocator, params T[] initialItems)
{
m_Length = initialItems.Length;
m_AllocatorLabel = allocator;
int totalSize = UnsafeUtility.SizeOf<T>() * m_Length;
m_Buffer = UnsafeUtility.MallocTracked(totalSize, UnsafeUtility.AlignOf<T>(), m_AllocatorLabel, 1);
var handle = GCHandle.Alloc(initialItems, GCHandleType.Pinned);
try
{
UnsafeUtility.MemCpy(m_Buffer, handle.AddrOfPinnedObject().ToPointer(), totalSize);
}
finally
{
handle.Free();
}
# if ENABLE_UNITY_COLLECTIONS_CHECKS
m_Safety = AtomicSafetyHandle.Create();
AtomicSafetyHandle.SetStaticSafetyId(ref m_Safety, s_staticSafetyId);
AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
if (UnsafeUtility.IsNativeContainerType<T>())
AtomicSafetyHandle.SetNestedContainer(m_Safety, true);
# endif
}
// 길이 속성
public int Length
{
get
{
# if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
# endif
return m_Length;
}
}
// 인덱서
public T this[int index]
{
get
{
# if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
# endif
return UnsafeUtility.ReadArrayElement<T>(m_Buffer, index);
}
set
{
# if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
# endif
UnsafeUtility.WriteArrayElement(m_Buffer, index, value);
}
}
// 값 추가 메서드
public void Add(T value)
{
# if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
# endif
int newTotalSize = (m_Length + 1) * UnsafeUtility.SizeOf<T>();
void* newBuffer = UnsafeUtility.MallocTracked(newTotalSize, UnsafeUtility.AlignOf<T>(), m_AllocatorLabel, 1);
UnsafeUtility.MemCpy(newBuffer, m_Buffer, m_Length * UnsafeUtility.SizeOf<T>());
UnsafeUtility.FreeTracked(m_Buffer, m_AllocatorLabel);
m_Buffer = newBuffer;
UnsafeUtility.WriteArrayElement(m_Buffer, m_Length++, value);
}
public NativeArray<T> AsArray()
{
# if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Safety);
AtomicSafetyHandle handleForArray = m_Safety;
AtomicSafetyHandle.UseSecondaryVersion(ref handleForArray);
# endif
var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(m_Buffer, m_Length, Allocator.None);
# if ENABLE_UNITY_COLLECTIONS_CHECKS
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, handleForArray);
# endif
return array;
}
public void Dispose()
{
# if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckDeallocateAndThrow(m_Safety);
AtomicSafetyHandle.Release(m_Safety);
# endif
UnsafeUtility.FreeTracked(m_Buffer, m_AllocatorLabel);
m_Buffer = null;
m_Length = 0;
}
}
2. 활용 및 응용 예제
- 리스트 생성:
NativeAppendOnlyList<int> list = new NativeAppendOnlyList<int>(Allocator.Persistent);
- 값 추가:
list.Add(10);
- 값 읽기:
int value = list[0];
- 길이 확인:
int length = list.Length;
- 배열로 변환:
NativeArray<int> array = list.AsArray();
- 메모리 해제:
list.Dispose();
3. 주의사항
- 안전 체크를 위해
ENABLE_UNITY_COLLECTIONS_CHECKS
가 활성화되어 있어야 합니다. - 프로젝트의 최종 빌드에서는 성능상의 이유로 안전 시스템이 비활성화됩니다.
이 문서를 통해 Unity의 커스텀 NativeContainer
를 효과적으로 활용할 수 있습니다. 추가적인 질문이나 도움이 필요하시면 커뮤니티 포럼이나 튜토리얼을 참조하시기 바랍니다.