チカラの技術

電子工作やプログラミング

【VRChat】ワールドギミック作成TIPS【U#】

ギミック作成時のTIPSや不具合対策が溜まってきたのでまとめます。
VRChatの開発環境は絶えず更新されていくため情報が古くなっている可能性があることはご留意ください。
(ご指摘のコメントお待ちしています)

UdonSharp (U#)

Class Exposure Tree

U#で使用できるビルドインのC#クラスを確認できます。
ビルトインメソッドはU#よりも遥かに高速なのでパフォーマンスが向上します。


使用できるクラスは緑色、使用できないクラスは赤色で示されます。
]

公式ドキュメント
Class Exposure Tree | UdonSharp

不具合対策:同期変数の初期化待ち

2022年末頃からジョイン時の同期変数の初期同期が完了するまでstart()から10秒程掛かるようになっています。
その前に同期変数を操作するとギミックが壊れる可能性があるため、ギミックは同期待ち処理が必要です。

① 単純に10秒後に同期変数操作を呼ぶ場合:

        //Start
        void Start()
        {
            //------------------your process------------------

            //------------------start wait sync------------------
            SendCustomEventDelayedSeconds(nameof(ChangeSyncValue), 10.0f); //value is sync after 10sec. You can change sync value.
        }


② 定期的に同期完了をチェックする(初期操作に時間が掛かる場合):

        //stopwatch for waiting sync value
        private System.Diagnostics.Stopwatch syncWaitSw = new System.Diagnostics.Stopwatch();
        private const int WAIT_SYNC_VALUES_MSEC = 10000; //10sec wait

        //Start
        void Start()
        {
            //wait sync stopwatch start
            syncWaitSw.Restart();
            //------------------your process------------------

            //------------------start wait sync------------------
            SendCustomEventDelayedSeconds(nameof(WaitSyncValuesAndUnlock), 0.2f);
        }

        /// <summary>
        /// wait to sync
        /// </summary>
        public void WaitSyncValuesAndUnlock()
        {
            if (syncWaitSw.ElapsedMilliseconds >= WAIT_SYNC_VALUES_MSEC)
            {
                //value is synced. You can change sync value.
                syncWaitSw.Stop();
                ChangeSyncValue();
            }
            else
            {
                SendCustomEventDelayedSeconds(nameof(WaitSyncValuesAndUnlock), 0.2f);  //yet sync. call self
            }
        }


同期変数テンプレート

定型化した私の書き方です。

① 通常:

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class SyncTest : UdonSharpBehaviour
{
    [UdonSynced, FieldChangeCallback(nameof(SyncValue))] private int _syncValue;
    public int SyncValue
    {
        get => _syncValue;
        set
        {
            _syncValue = value;
            ApplySyncValue();
            Debug.Log("SyncValue is:" + _syncValue);
        }
    }

    private void ApplySyncValue() //"Apply" + property name
    {
        //process when sync value is changed. for all user.
    }

    public void SetSyncValue(int value) //"Set" + property name
    {
        TakeOwner();
        SyncValue = value;
        RequestSerialization();
    }

    /*----------------------------util--------------------------------------------*/
    private void TakeOwner()
    {
        if (!Networking.IsOwner(Networking.LocalPlayer, this.gameObject)) Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
    }
}


② 同値反映:
同期変数を設定するとき前回と同じ値でも全員に反映処理をさせたいときが稀にあります。
その場合はSetSyncValueとApplySyncValueを以下のように書き換えます。

    public void ApplySyncValue() //"Apply" + property name
    {
        //process when sync value is changed. for all user.
    }

    public void SetSyncValue(int value) //"Set" + property name
    {
        if (_syncValue != value)
        {
            TakeOwner();
            SyncValue = value;
            RequestSerialization();
        }
        else
        {
            SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All,nameof(ApplySyncValue));
        }
    }

ワールド全般

Whitelisted World Components

ワールドで利用できるコンポーネントのリストです。
docs.vrchat.com

記載されているが使えないコンポーネント

  • PuppetMaster (2021年からVRChat上で動かない。Canny

記載されていないが使えるコンポーネント

  • Cinemachine系(CinemachineVirtualCameraなど。タイムラインで使用)

uGUI

Textの文字崩れ問題

動画の通りTextが突然崩れました。(音声無し)
youtu.be

状況をまとめると以下の通りです。

・動的に文字列を変更する(した)Textが一度に壊れる。
・半角・全角は関係ない
・発生の要因になる再現性のある操作は見つからなかったが、3分操作した3割程度の人に発生した。
・動画後半のように文字化けが復旧することもあるが条件は不明。


対策:TextMeshProを使う
「Textの代わりにTextMeshProを利用する」ことで文字崩れは発生しなくなりました。

ただTextMeshProは標準では日本語に対応しておらず、日本語フォントを含めると15MB以上ワールド容量が増加するというデメリットがあります。ただこれはフォールバックフォントを利用して回避できます。
簡単にいうとワールドのアップロード時に空のフォントを設定しておくとVRChatのアプリ(クライアント)自身が持っている日本語フォントを使ってくれるという仕組みです。

2023/06/11現在のフォールバックフォントの見た目は以下の画像のようになります。
(文字は全てフォールバックフォント)


フォールバックフォントの設定方法(VRChat)
まず以下の空っぽのフォント(匿名提供)をダウンロードして全てのTextMeshProに設定します。

drive.google.com



以上でVRChat上でフォールバックフォントが適用されます。

フォールバックフォントの設定方法(Unity)
ただこのままだとUnity上(ClientSim)で文字化けしてしまうのでUnity上でのフォールバックフォントを別途設定します。
この例ではNotoSansJPフォントを作成して設定しています。(TextMeshProの日本語フォント作成方法は多くの他記事があるので各自調べてください)

以上Textの文字化け対策でした。

(フォールバックフォントについてはHaï~さんのツィートを元にさせて頂きました。https://twitter.com/vr_hai/status/1464343863708065794

その他

処理した地雷(?)達をtwitterから引用します。Editor拡張で解決して配布することも多いです。

デスクトップモードのugui視点対応


TextMeshProがフォールバックフォントになる

DeactiveなTextMeshProコンポーネントをActiveにすると解決します。
(配布ギミックを受け取った時など一度もActiveにされない場合に問題になる)


TextMeshProで日本語対応するときに使う文字だけ抜き出す

ワールド容量削減策です。


大量の名前重複オブジェクトがある場合の名前修正


大量のボタンイベントに対応するためのU#コード生成

SendCustomEventに引数が渡せない痛みを和らげるやつです。タグモードではコンポーネント側の処理も生成します。


png画像の縦横サイズを4の倍数にする。

クランチ圧縮の仕様に収めるためのものです。


まとめ

  • 色々やると色々問題も出てくるけど解決できると気持ちいいね。
  • 拡張エディタを書いてる間だけはU#から自由になるよ!
  • 頼むー!PuppetMaster使えるようにしてくれー!

それではワールド製作やっていきましょう!

更新履歴

順次内容を書き足していく予定です。

2023/03/24 初稿。