UnsafeUtilityについて

UnsafeUtility.Mallocとは

Unmanaged memoryを確保することができる。

// 確保するサイズ
var size = UnsafeUtility.SizeOf<int>();
// メモリアライメント
var alignment = UnsafeUtility.AlignOf<int>();
// アンマネージドメモリの確保
void* ptr = UnsafeUtility.Malloc(size, alignment, Allocator.Persistent);

UnsafeUtility.Freeとは

確保したUnmanaged memoryを解放する。

// 確保したメモリの解放
UnsafeUtility.Free(ptr, Allocator.Persistent);

使用例

// 弾の共通データ
public struct SharedBulletParam
{
    public float Speed;
    public float Damage;
}

// 個別に持つ弾データ(NativeArrayとして持つのでBlittable型である必要)
public unsafe struct Bullet
{
    public SharedBulletParam* SharedBulletParamPtr;
    public float Angle;        // 角度
    public float Lifespan;     // 生存時間
}

void Initialize()
{
    // 確保するメモリサイズ
    var size = UnsafeUtility.SizeOf<SharedBulletParam>();
    // メモリアライメント
    var alignment = UnsafeUtility.AlignOf<SharedBulletParam>();
    // アンマネージドメモリの確保
    var sharedBulletPtr = (SharedBulletParam*)UnsafeUtility.Malloc(
        size, alignment, Allocator.Persistent);

    const int BulletCount = 1000;
    var bullets = new NativeArray<Bullet>(BulletCount, Allocator.Persistent);
    for (int i = 0; i < BulletCount; i++)
    {
        bullets[i] = new Bullet
        {
            // 各インスタンスに共通データのポインタを持たせる
            SharedBulletParamPtr = sharedBulletPtr,
        };
    }

    // ※使い終わったらNativeArrayとMallocしたポインタを解放すること
}

使用例2-1

struct SampleStr
{
    public int Param1;
    public float Param2;
    public SampleStr(int index)
    {
        Param1 = index;
        Param2 = index + index / 10f;
    }
    public override string ToString()
    {
        return $"{Param1}, {Param2}";
    }
}

void Start()
{
    // 確保するメモリサイズ
    var size = UnsafeUtility.SizeOf<SampleStr>();
    // メモリアライメント
    var alignment = UnsafeUtility.AlignOf<SampleStr>();
    // アンマネージドメモリの確保
    var ptr = (SampleStr*)UnsafeUtility.Malloc(size, alignment, Allocator.Persistent);

    // ----------------------------------------
    // 構造体の値をポインタにコピー
    SampleStr sample = new SampleStr(8);
    UnsafeUtility.CopyStructureToPtr(ref sample, ptr);

    // > 8, 8.88
    Debug.Log(*ptr);


    // ----------------------------------------
    // ポインタが指す値を構造体にコピー
    var dest = new SampleStr();
    UnsafeUtility.CopyPtrToStructure(ptr, out dest);

    // > 8, 8.88
    Debug.Log(dest);


    UnsafeUtility.Free(ptr, Allocator.Persistent);
}

使用例2-2

大変分かりやすい

struct SampleStr
{
    public int Param1;
    public float Param2;
    public SampleStr(int index)
    {
        Param1 = index;
        Param2 = index + index / 10f;
    }
    public override string ToString()
    {
        return $"{Param1}, {Param2}";
    }
}

void Start()
{
    // SampleStrを8個生成
    var nativeArray = new NativeArray<SampleStr>(8, Allocator.Persistent);
    for (var i = 0; i < nativeArray.Length; i++)
    {
        nativeArray[i] = new SampleStr(i);
    }

    // 拡張メソッドとして実装されているのでこれで取得可能
    var ptr = (SampleStr*) nativeArray.GetUnsafePtr();

    // > "0, 0"
    // ※"Debug.Log(nativeArray[0])"と同等
    Debug.Log(*(ptr));

    // > "2, 2.2"
    // ※"Debug.Log(nativeArray[2])"と同等
    Debug.Log(*(ptr + 2));

    // > "3.3"
    // ※アロー演算子(->)でフィールドにアクセスできる
    // ※"Debug.Log(nativeArray[3].Param2)"と同等
    Debug.Log((ptr + 3)->Param2);

    nativeArray.Dispose();
}

参考