Udon#/Unityで詰まった話

はじめに

私が VRC SDK3 でワールドを作るにあたり、 UdonSharp (U#) や Unity で詰まったところを書いていくページです。
ざっくり分類してるけど結構適当。
箇条書きだけど前後の項目と繋がってたり繋がってなかったり。
更新で変わったり間違ってたりするところがあるかもだけど許して。
順次追記します。

基本的な話

・using でライブラリから引っ張ってくる系の処理の多くは Udon サポート外
・簡単なものなら自分で実装するのが早い(ソートとか)
・Method not exposed to Udon とか出てたらそのメソッドは使えない

文法機能

・List とか Collection 系は全部使えないので、配列で上手くやるしかない
・自分で作ったメソッドで ref を受け取ることはできない
・Enum は使えない 使えるようになった
・継承は使えない 使えるようになった

例外処理

・Exception を catch できない
・Exception が発生すると、以降そのクラスの処理が全部止まるっぽい(Updateとかも含めて)
・事前チェックをして、例外を発生させない工夫が必要
・null になる可能性のある変数を参照する場合は、先に変数が null と等しくないことをチェックしてから処理する
・変数で割り算をする場合、0 で割り算をしないようにチェックしてから割り算を実行する
・Parse メソッドなどで文字列を数値に変換する場合、TryParse メソッドを使って、パースが可能なことをチェックしてからパースする

同期関連

・[UdonSynced] を付けたフィールドは同期する
・配列も同期されるようになったが、 string の配列はダメ
・Synchronization Method が Continuous だと、変数のサイズが大きい(100KB超えるくらいらしい)と同期しない
・float[100] は同期ダメだった
・Synchronization Method を Manual にすれば OK
・ただし、Manual の場合は RequestSerialization() を明示的に呼ばない限り同期されない
・Networking.SetOwner でオーナーを自分に設定後、すぐに同期変数を変更しても、変更処理が行われることは担保される
・しかし、同期変数の値を読んで、それを処理して同じ同期変数を更新する、という処理の場合は注意が必要(DBで言うところのコンフリクトが発生する)
・A と B が同時に同期している文字列変数に追記する処理を考えた場合、まず A がデータを読み込みそれに追記、続いて B がデータを読み込みそれに追記、次に A がデータに追記済みデータを書き込み、最後に B が追記済みデータを書き込み、という順番に処理される可能性があり、この場合 A が追記したデータは消失する
・[UdonSynced] を付けた配列に対して、配列自体を変更するような代入をすると、エラーにはならないが、すべての変数が同期しなくなる

public class SampleUdonClass : UdonSharpBehaviour
{
    [UdonSynced]
    private int[] a = new int[3];
    
    public void SetNG(int[] b)
    {
        if (!Networking.IsOwner(Networking.LocalPlayer, this.gameObject)) {
            Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
        }
        a = b; // C# の文法としては正しく、エラーにもならないが、実行後にこのクラスのすべての変数が同期しなくなる
        RequestSerialization();
    }
    
    public void SetOK(int[] b)
    {
        if (!Networking.IsOwner(Networking.LocalPlayer, this.gameObject)) {
            Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
        }
        // 要素を1つずつ代入するのはOKで、問題なく同期する
        // (余談)要素数が多い場合は for や foreach で処理した方が良い
        a[0] = b[0];
        a[1] = b[1];
        a[2] = b[2];
        RequestSerialization();
    }
}

・OnDeserialization は RequestSerialization を呼ばなくても、2人目以降のプレイヤーが Join してすぐのタイミングでも Join したプレイヤーの環境で呼ばれる

Ownership関連

・UdonBehaviour または VRC Object Sync がついたオブジェクトは、そのインスタンスにいる誰かが「オーナー」として設定される
・明示的にオーナーを設定していないオブジェクトのオーナーは、そのワールドに最初に入った人になる
・Networking.SetOwner を使って、オブジェクトのオーナーを変更できる
・[UdonSynced] な変数の値を変更できるのはオブジェクトのオーナーのみ
・VRC Object Sync のついたオブジェクトのTransformを操作できるのもオブジェクトのオーナーのみ

uGUI関連

・TextMeshPro(TMP) は使える
・ただし、 Udon から TMP の InputField にはアクセスできない
・回避策があるっぽいけど、上手くいかなかった
・Canvas に BoxCollider が付いていない場合、VRC が勝手に (1, 1, 1) スケールのコライダーを付ける
・このコライダーに当たって、 Ray (手から出る青いビーム) が Canvas の手前で不自然に消える
・自前で z が 0.01 くらいの BoxCollider を付けとくと良い感じ
・複数の Canvas を同一平面状に置くと重なり順が思ってたのと違う感じになることがある
・Canvas の OrderInLayer を設定すると思った通りにできる(参考: https://developer.aiming-inc.com/ux/ugui-layer/ )
・が、透過がおかしくなることがあったので、素直にCanvasをまとめた方がいい
・レイヤーが UI になっていると、メニューを開いている時だけ操作できるようになるので、誤って押すと困るボタンなどに使えそう
・逆に、普通に操作したいものは UI 以外(Defaultなど)のレイヤーに設定する

デバッグ関連

・[Edit] -> [Project Settings...] -> [Udon Sharp] から、UdonSharp の各種設定ができる
・デバッグに関する設定もいくつかある

この記事が気に入ったらサポートをしてみませんか?