Programming in VRChat

VRChat でのプログラミングについて調べたことの書き溜め

VRC_AudioBank

AudioClip のリストを保持し、それらを指定された方法で AudioSource で鳴らすコンポーネント

参考:Unity マニュアル AudioClipAudioSource

f:id:naqtn:20181002081233p:plain


使用方法

公式ドキュメント を参照。

VRChat 技術メモ帳 | VRC_AudioBank に日本語での基本的な設定手順もあります。

設定項目の大まかな意味

Conditions

Playback Order

クリップのリストから次に再生するクリップをどうのように決めるかを指定する。

  • InOrder : クリップを登録した順番通りに鳴らす
  • InOrderReversing : 実装未完
  • Shuffle : クリップの順をシャッフルしてから再生する
  • Random : 次に再生するクリップをランダムに選択する

Playback Style

再生の進め方の指定。

  • OneShot : 一つのクリップだけを鳴らす。
  • Continuous : 連続して再生する。クリップの演奏が終わったら、次のクリップを自動的に再生し始める

Source

再生に用いる AudioSource

Pitch Range

Min Pitch Range を下限 Max Pitch Range を上限として、再生するたびにランダムに pitch が選択される (おそらく AudioSource の pitch の指定)

1 の場合にクリップの元の速度になる。(変えたくない場合は Min, Max 両方を 1 にする)

Clips

再生対象 AudioClip

Triggers

VRC_AudioBank が動作に伴って呼び出す Custom trigger を設定する。

  • OnPlay : 再生を開始する際に呼ぶ
  • OnStop : 再生が停止されると呼ぶ
  • OnChange : 再生するクリップを VRC_AudioBank が変更すると呼ぶ

RPCs

  • Play : 指定したクリップを再生し始める
  • Stop : 再生をやめる
  • PlayNext : 次のクリップを再生する
  • Shuffle : 再生リストの項目をランダムに入れ替える

詳細仕様(分析結果)

公式ドキュメントの説明は詳細についてきわめて不足している。 実際の動作を観察して、実態を書き出した。

(動作確認環境 Client 2018.3.1 build 625, SDK 2018.08.28.13.02)

データ

公式文書には存在しないが、 説明をしやすくするために以下のデータに名前を付ける。

ClipList

  • VRC_AudioBank に登録した Clip のリスト
  • この内容は変化することなく保持され続ける

Playlist

  • シャッフル操作が要素を入れ替えるリスト
  • 初期状態では PlaylistClipList と同じ並びになっている

CurrentIndex

  • 現在演奏している(あるいは演奏が終わった)クリップを Playlist で何番目であるかを指し示す。
  • RCP PlayNext が次に鳴らすクリップを特定するのに、この CurrentIndex が用いられる
  • 初期状態ではゼロになっている
    • これは奇妙。初期状態から PlayNext をすると二番目を鳴らす事になってしまう。 不具合とみなすべきだろう。特例的に -1 とした方が自然だ。

操作・振る舞い

RPC を実行した時の振る舞いを述べる。 設定によって異なる部分があるので、理解しやいと思われる順番に述べる。

RPC: Play

  • 指定したクリップを再生する
  • クリップの指定は、ClipList でのゼロ始まりのインデックスでおこなう。
  • 再生に先立ち OnStop を実行する
    • 実際に再生していなくとも実行する
  • 再生を開始するとともに OnPlay を実行する
    • 無効なインデックスで再生できなかった場合には実行しない
  • CurrentIndex が変更される。
    • 少し奇妙な仕様だが次のようになっている。
    • Play で指定したクリップが Playlist 中で位置しているインデックスの値になる。
    • 例えば ClipList にクリップが [a, b, c] と入っていて Playlist はシャッフルされていて [c, a, b] となっている時に Play(0) は clip a を再生し、CurrentIndex は 1 になる。

Trigger: OnStop

  • クリップの再生が最後に達して終了すると OnStop を実行する
  • (補足:他のタイミングでも OnStop は呼ばれる)

RPC: Stop

  • 再生を停止する
  • OnStop を実行する。実際に再生していないくても実行する。
    • 例えば再生中に RPC Stop が二回連続して実行されると(二回目には既に再生はしていないが) OnStop を二回実行する。

Playback Style

再生がクリップの最後に達した時の振る舞いが Playback Style の設定により変化する。 (RPC Stop ではなく、再生して最後に達した場合の扱い)

  • OneShot
    • 何もしない
    • (既述のように OnStop は実行される)
  • Continuous
    • RPC PlayNext が呼ばれたかのように動作する

RPC: PlayNext

  • 次のクリップを再生しようとする
  • (補足:前述のように Playback StyleContinuous の場合でクリップの最後に達した時、ここで記述する動作が起きる)
  • クリップの選択(何が再生されるか、あるいはされないか)は Playback Order の設定に依存する。別記のそれぞれの項目を参照。
  • 再生中か否かにかかわらず再生に先立ち OnStop が実行される。
  • OnChange を実行する。
  • 再生された場合 OnPlay を実行する。

RPC: Shuffle

  • Playlist の内容をシャッフルする
  • ShuffleCurrentIndex を変えない
  • Playlist をシャッフルされていない初期状態にリセットする手段は用意されていない
  • シャッフル結果は、ワールドに置いてある同じ個数の clip を収めた VRC_AudioBank で同じになる。
    • 疑似乱数のシード値が同じになるのだろう。

Playback Order: InOrder

  • Playlist を順番に再生する基本となる動作モード。
    • 初期状態で Playlist は ClipList の順と等しいので、シャッフルしていない場合、クリップを登録した順番に再生する
  • InOrder での PlayNext の振る舞い
    • CurrentIndex を先にひとつ進めて、進めたインデックスで Playlist に格納されているクリップを再生する
      • 直前のクリップ再生停止時にクリップの最後まで再生したかは問わずに、次のクリップが再生対象になる。
      • RPC Stop により再生停止したクリップから再び再生する直接的な方法は無い。
    • CurrentIndex が既に最後のクリップを指していた場合、再生は行われない。 先頭に戻るループ動作はしない。
      • OnStop, OnChange は実行される。
      • もう一度 PlayNext を実行すると Playlist の先頭を再生する。CurrentIndex は 0 になる。

最後での PlayNext の振る舞い

ログを見ると例外が発生している。意図した実装とは思えない。 (InOrder_OneShot_AudioBank は GameObject に付けた名前)

Error      -  User code has thrown an exception, when calling PlayNext on InOrder_OneShot_AudioBank
Error      -  Exception has been thrown by the target of an invocation.
Exception  -  Array index is out of range.

Playback Order: InOrderReversing

  • 実装されていない

Playback Order: Shuffle

  • Playlist をあらかじめシャッフルする。他の振る舞いは InOrder と同じ。
  • CurrentIndex の初期値は、シャッフルの結果 Playlist 中で clip 0 が置かれているインデックスになる
    • 言い方を変えると初期状態が RPC を Shuffle, Play(0), Stop と連続して呼び出しした後の状態と同じになっている
    • (この振る舞いは OneShot, Continuous で共通。)
  • Playback StyleContinuous の場合連続再生になるのだが、 CurrentIndex の扱いがこのようになっているために PlayNext で全てのクリップが再生されることは無い。 (特に、clip 0 は再生されることは無い)
    • 不自然なので不具合だと言っていいだろう。

Playback Order: Random

  • PlayNext が呼ばれると、 VRC_AudioBank は新たに再生するクリップをランダムに選択し、再生する
  • 補足:これまでの記述で必要なことは全て述べているが、実用性を踏まえたリファレンスとして補足説明をする
    • PlayPlayNext で再生を開始する。
      • Play の場合まず再生されるものは指定したクリップである。(これはランダム対象ではない)
      • PlayNext の場合はランダムに選択される
    • Playback StyleContinuous の場合は、クリップの再生が終わり次第、次のクリップがランダムに選択され、再生が継続する。

参考: Trigger まとめ

  • OnPlay
    • 実際に再生開始できた時に実行する
    • きっかけには次がある
      • RPC Play の呼び出し
      • RPC PlayNext の呼び出し
      • Playback StyleContinuous であることによる連続再生
  • OnStop
    • 停止したあるいは停止しようとした時に実行する
    • 実際には再生していなくても実行する
    • 従って次の状況がありうる
      • クリップの最後に達して自動的に実行
      • RPC Stop の呼び出しによって実行
      • RPC Play および RPC PlayNext による再生に先立って実行
  • OnChange
    • RPC PlayNext によってクリップを変更した時に実行する
    • RPC Play でクリップが変わる時には(意味が不明だが)呼ばれない

準正常系 および 異常系

RPC Play の指すインデックスが範囲外

  • OnStop は実行される
  • (ログに例外発生が報告される)
  • 範囲外の Play 後に範囲内の Play が呼ばれれば、正しく動作する。
  • ゆえに、範囲外の Play を呼び次に PlayNext を呼ぶと、Playlist の先頭を再生できる。
    • これで任意の状態から Shuffle 後に Playlist の先頭からの再生することが可能になる。意図している動作かは不明だが。

VRC_AudioBank コンポーネントが disenabled の場合

  • 通常どおり動作する

GameObject が inactive の場合

  • 再生されない
  • OnPlay は実行されない
  • OnStop, OnChange は実行される

WTF : 要調査

  • OnInteract Local から RPC target All で、全員の元で再生されないだと?
    • Local で再生されるにとどまる。
    • 以前は鳴っていたように思うのだが??
  • シャッフルはそれぞれのマシンで行う(ようだ)。