この記事は、UniMagic Advent Calendar 2024 9日目の記事です。
まあ厳密には関係者じゃない気がするけど、細かいことは気にしたら負け。
UdonSharpで数~数十MBくらいのサイズの数値配列を扱いたい時のメモ。
サイズが小さい配列であれば、普通に定義すればよい。
しかし、配列のサイズが大きくなってくると、次第にコンパイルに時間がかかり、メモリも必要になってくる。
(こんな感じに100MB越えのcsファイルを作ると、コンパイル時にメモリ不足になる。)
VRCJsonを使う手もあるが、文字列として扱うのでサイズが大きくなりがちである。
また、数値が内部的にDoubleとして扱われるので、そこまでの精度が必要ない時はリソースの無駄になってしまう。
System.Buffer.BlockCopy
ここで使えるのが、System.Buffer.BlockCopyである。
これを使うと、Byteの配列から他の数値型の配列に値をコピーすることができる。
コピー元となるByteの配列だが、ワールドデータに含めるのであればTextAssetを使うのがよいと思う。
拡張子を.bytes
にして保存すれば、Unityがバイナリデータであると認識してくれる。
using System;
using UnityEngine;
using UdonSharp;
public class ExampleClass : UdonSharpBehaviour
{
[SerializeField] public TextAsset bigArrayData;
private uint[] _uintArray;
void Start()
{
// uintは4byteなので、4で割っている
// 型に合わせて割る数を変える
_uintArray = new uint[bigArrayData.bytes.Length / 4];
Buffer.BlockCopy(bigArrayData.bytes, 0, _uintArray, 0, bigArrayData.bytes.Length);
}
}
また、ワールドデータに含めず、インターネット経由にしたい場合は、VRCStringDownloaderのResultBytes
を使えばいいと思われる。(自分では未確認)
なお、元となるByte配列のデータは、pythonで用意した。
structを用いればいい感じのバイナリデータを作成できる。
import struct
data = [1, 2, 3, 4, 5]
with open("bigArrayData.bytes", "wb") as f:
# "<": リトルエンディアン
# "I": uint(ここの文字は数値の型によって変える)
f.write(struct.pack("<"+"I"*len(data), *data))
余談
書き終わってから気づいたが、VRC公式ドキュメントにもBlockCopyの記載があった。
(コードだけの分かりにくいものだったので、この記事を書いた意味はあったかな…?)