UDONメモ
UdonGraphはクソ
UdonSharp
最高。C#と同じ構文でUdonを記述できる。
GitHub - MerlinVR/UdonSharp: An experimental compiler for compiling C# to Udon assembly
インストール
上記サイトからUnitypackageをダウンロードし、VRChatSDK3をインポートしてからインポート。
スクリプトの作成
UdonBehaviour
コンポーネントを付けて、プルダウンから Udon C# Program Asset
を選択する。
そして New Program
でProgram Sourceを作成。Create Script
でスクリプトファイルを作成する。
あとはコーディング!
用語定義
ちょくちょく混同するのでここで定義します。
Master(マスター)
VRChat技術メモ帳ではInstance内にいる中で、ローディングが終了した順番が一番早い人。Instanceを出ると次に早い人に移行するとのこと。 Udon環境ではPlayerIDが取れるようになったため、ワールド内でPlayerIDが一番若い人がMasterという認識で多分間違っていない。
InstanceOwner(インスタンスオーナー)
Instanceを立てた人のこと(VRChat技術メモ帳では単にオーナーという)。FriendなどのInstanceの公開範囲に関わる。
OwnerShip(オーナーシップ)
オブジェクト一つ一つに設定されている、オブジェクトの同期を行う権限のこと。
ObjectOwner(オブジェクトオーナー)
OwnerShipを持っているプレイヤーについての用語がInstanceを立てた人のことであるOwnerと混同しやすいため、このサイトでは「そのオブジェクトのOwnerShipを持っているプレイヤー」のことをObjectOwnerと呼ぶことにする。(筆者の造語なので注意。 正しい言い方があったらTwitterで教えて・・・ (2020/04/09追記 Object Ownerと名付けました。))
同期変数
UdonSharpでは以下のように、UdonSynced属性がついた変数のことをいう。
1 2 |
|
インタラクト
IntaractはどうやらUdonBehaviourがついている物自体にコライダーがついてないと入っていない?
インタラクト時に呼ばれるコールバック関数はこのように定義する。
1 2 3 |
|
インタラクト時のヒント表示を変更する
InspectorをDebug表示にするとUdonBehaviourでInteract Textから変更できる。
(2020/04/11追記)Normal表示のUdonBehaviourで変更できるようになりました。
SetOwner
インタラクトした人にOwnerShipを渡す関数。 ただし反映されるのは次のインタラクション時。(近日中に修正されることが予定されている。)
1 2 3 4 |
|
Networking
GetOwner
そのオブジェクトのObjectOwnerの名前が取れる。
1 |
|
VRCPlayerApi
GetPlayerById
IDからプレイヤーを取得する。そのIDのプレイヤーがいない場合nullを返す。
1 |
|
GetAllPlayers
全てのプレイヤーの配列を取得する。 あらかじめ最大数の配列を用意して格納する使い方。
1 2 3 4 5 6 7 8 9 |
|
VRCPlayerAPI同士の比較
Networking.GetOwner
などで取れるVRCPlayerAPIは比較演算子を使ってNetworking.LocalPlayer
と比較は出来なかった。
こっちはダメ
1 |
|
こっちはOK
1 |
|
OnPlayerJoin
やOnPlayerLeft
の引数のplayer
はNetworking.LocalPlayer
と比較演算子(==)を使用しても正しく比較できるが、きちんと比較したい場合はplayerIDで比較するのがよいかもしれない。
PlayerIDの決定規則
プレイヤーIDは最初に入った人を1として、どんどん数字が増えていく
1 : user1
2 : user2
3 : user3
ここでuser2が抜けて、そのあとuser4が入ってきた場合は2が欠番となり、user4のplayerIDは4になる。
1 : user1
3 : user3
4 : user4
また、ここでuser2が戻ってきた場合はplayerIDは5になる
1 : user1
3 : user3
4 : user4
5 : user2
同期周りで陥りがちな問題・バグ
OnPlayerLeft時に同期変数が更新されない
OnPlayerLeftのタイミングで以下のように同期変数を修正する場合、このオブジェクトのObjectOwnerがワールドから移動する場合は値が更新されない。
1 2 3 4 5 6 7 8 9 10 |
|
ObjectOwnerが落ちるとそのワールドのMasterにOwnerShipが移動するが、Networking.SetOwner
と同様にOwnerShipの移動にラグがある。
憶測だが、OwnerShip移行時にOpPlayerLeftでIf分内が新しいObjectOwnerで動作するが、全体ではまだ新しいObjectOwnerがObjectOwnerではないため、値の同期は走らないのではないかと思われる。
少なくとも、OwnerShip移動と同期変数の更新は同タイミングで行わないほうがいい。
Synchronize Position がオンだと同期変数が同期しない
同期変数があるプログラムを Synchronize Position
をオンにしているUdonBehaviourにセットした場合、ObjectOwner以外のプレイヤーでは同期変数が同期されなくなる。
コンパイルが走らないとき
Udon C# Program AssetでSource Script
がセットされていない物があると以下のようなエラーが出てすべてのファイルのコンパイルが走らなくなる。
シーンで使っていなくても走らなくなるので削除する。
1 2 |
|
ちなみに、このエラーが出ている状況でも、Force Compile Script
を押せば単体でコンパイルできる。
Stringを使った同期リスト
現在(2020/04/09)同期変数で配列を使用することが出来ない。また、Listのような可変長の配列データも扱うことが出来ない。
しかし、string型の同期変数を使うことでこれらを疑似的に再現することは可能。
仕組みとしては、カンマ区切りで数列を持ちstring.Split
でそれぞれを分割して処理する。
1 2 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
string
から整数や浮動小数点数に変換したい場合は以下の関数を使う。
1 2 3 4 5 |
|
「Disable Station Exit」のついたVRC_Stationからプレイヤーをおろす方法
1 2 3 |
|
実はテレポートでもおろせる。(最初の方法に気づかず最近までこちらの方法を取っていたのは内緒)
1 |
|
オブジェクトの位置リセット
UdonSharpから直接オブジェクトをリスポーンさせる機能はなさそう?
オブジェクトの位置をリセットしたいとき位置同期をオンにしていると、オブジェクトのOwner以外ではリセットを実行された位置からリセット位置に補間されるように動くため、複数のオブジェクトを一か所に置いていると周りを吹っ飛ばしてしまう。
そこで位置をリセットするのではなくSDK2のようにリスポーンさせたいわけだが、直接オブジェクトをリスポーンさせる機能は存在しない。
そこで、オブジェクトをVRC_World
で設定するRespawn Height Y
より低い位置に動かすとリスポーンさせることができるため、周りを吹っ飛ばさずに安全に元の位置に戻すことができる。
(作例)同期スイッチ
オブジェクトのアクティブ、非アクティブを同期的に切り替えるUdon。 OwnerShipの移行は行わないため、常にインスタンスのMasterがOwnerShipを持つ。 切り替えの変数はMasterが保持するため、Master以外が高速で切り替えると値の更新が遅れて切り替わりがラグくなる時がある。 ただし、状態は間違いなく同期される。 後からワールドに入ってきた場合も同じ状態を保持する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
変数の同期
UdonSharpで変数の同期設定はアトリビュートでできる。
1 2 |
|
SyncモードはUdonSyncMode
で変更できる
モード | |
---|---|
UdonSyncMode.NotSynced | 同期しない |
UdonSyncMode.None | 補間なし |
UdonSyncMode.Linear | 線形補間 |
UdonSyncMode.Smooth | スムース補間 |
SendCustomNetworkEvent
ネットワーク越しでイベントを発行するのに使えるメソッド、オーナーシップとの兼ね合いはまだよくわからない。
ここに指定するメソッドはPublicじゃないといけない。
以下のコードでインタラクト時に、ワールドにいる全員に対してイベントを発火し、ExampleMethodを実行する。
1 2 3 4 5 6 7 8 9 |
|
VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner
にすることで、ObjectOwnerのみに発火させることができる。
この時、インタラクトした人は誰であっても問題ない。AllならObjectOwnerやObjectOwner以外がインタラクトした場合、全員にイベントが発火し、OwnerならObjectOwnerとObjectOwner以外がインタラクトするとObjectOwnerのみExampleMethodが実行される。
SendCustomEvent()
名前が似ているけれど、こちらはだた単純に名前で関数を実行できる関数だった。
(作例)PlayerModに代わるUdon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
UdonSharpBehaviourのトリガー関数
Interact()
VRC_Triggerでいうところの、OnInteract。 オブジェクトに対してアクションしたときに呼ばれる。
OnDrop()
Pickupオブジェクトを放したときに呼ばれる関数。
OnPickupUseDown()
Pickupオブジェクトを持った状態でUseするためにトリガーを押し込んだ瞬間1回だけ呼ばれる関数。
OnOwnershipTransferred()
OwnerShipを失う時に、失ったプレイヤーで実行される。 実行された段階でプレイヤーにはOwnerShipがないため、ここで同期変数を更新することはできない。
OnPickupUseUp()
Pickupオブジェクトを持った状態でトリガーの押し込みを解除した瞬間に1回だけ呼ばれる関数。
OnPlayerJoined(VRC.SDKBase.VRCPlayerApi player)
プレイヤーがインタンスに入った時に呼ばれる関数。引数のplayerにはそのプレイヤー情報が入る。 入室したインスタンスにすでにプレイヤーがいた場合、人数分実行される。
OnPlayerLeft(VRC.SDKBase.VRCPlayerApi player)
プレイヤーがインタンスから離れた時に呼ばれる関数。引数のplayerにはそのプレイヤー情報が入る。 (2020/10/31)ローカルテストでのマルチクライアントで実験したとき動作しない?
OnSpawn()
公式ドキュメント
このオブジェクトがローカルプレイヤーのためにスポーンしたときに発生します。バッファリングされていないので、遅れて入場したプレイヤーはこのイベントを取得しません。ネットワークインスタンス化でオブジェクトがスポーンされた場合にのみ発生します。オブジェクトがベースシーンに存在する場合は発生しません。
現在(2020/04/10)、 ネットワークインスタンス化を行う方法がないため発動条件を満たせない?
OnStationEnterd()
UseAttachedStation()
などで、VRC_Station
に座った時に実行される関数。
ただし、 現在(2020/04/10)は動作しない。
OnStationExited()
VRC_Station
から離れた時に実行される関数。
ただし、 現在(2020/04/10)は動作しない。
OnVideoEnd(),OnVideoPouse(),OnVideoPlay(),OnVideoState()
おそらくVRC_SyncVideoPlayer
用のトリガー関数。現在(2020/04/10)はVRC_SyncVideoPlayer
がSDK3に実装されていないため使用できない。Unity純正のVideoPlayer
というコンポーネントがあるが、こちらはUdonに対応していない。
OnPreSerialization(),OnDeserialization()
同期変数を送受信したときに呼ばれる関数。同期変数の値が変更されたときに呼ばれそうだが、 変更されていなくても毎度実行されている。 (無駄では・・・?)
つまり同期変数は変更されていようがなかろうが常に通信されている?
OnPreSerialization()
ObjectOwnerが同期変数を送信するときにObjectOwner側で呼ばれる関数。
OnDesrialization()
ObjectOwnerから同期変数を受信した時にObjectOwner以外で呼ばれる関数。
バグ
WheelCollider.GetWorldPose()は使用できない(2020/04/09現在)
WheelCollider.GetWorldPoseは関数としても、Udonのノードとしても存在するが動かない。
VRCInstantiateのあれこれ(2020/10/17現在)
現状ネットワーク同期にバグが多く、完全ローカルでない限りは使用しないほうがよさそう。
- 現在同期的にインスタンス化を行う方法がない?
インスタンス化したオブジェクトではUdonBehaviourが死ぬ。- シーン内のオブジェクトであればVRCInstantiateしてもUdonBehaviour、VRCPickupは死なない
- プレハブから直接VRCInstantiateした場合はコンポーネントが剥がれる。
- OwnerShipの同期にバグがあったり、プレイヤーごとにそれぞれ異なるオブジェクトが中途半端に同期する。
【仕様?】PickupオブジェクトのRespawn Height Yでのリスポーン(2020/11/13現在)
PickupオブジェクトにUdonBehaviour
コンポーネントがアタッチされており、かつSynchronize Position
が有効でないと、VRC_World
で設定しているRespawn Height Y
でリスポーンしない。
毎回すべてのUdonSharpがビルドされるのを止める。
Project Settings
からUdon Sharp
→Compile all scripts
のチェックを外す。
キー入力
VRChat inputs