Skip to content

Repository files navigation

VRCDollyPivotManager

VR 内で床に印を付けると、その点を中心に回る Camera Dolly のパスを作ります。

アバターに仕込んだレイで印までの距離を3方向から測り、三辺測量で位置を求めて、そこを軸にした旋回パスを書き出します。生成したパスは OSC で VRChat へ自動的に読み込ませます。

コンソールを持たないタスクトレイ常駐ツールです。アバター側は別途セットアップが必要です。

まず読むものこれは POC です / 前提 / ライセンス

導入する入手アバター側の導入config.json

困ったらうまくいかないとき

仕組みを知る動作 / 出力 / 計算 / アバターと一致必須の値

これは POC です

思い付きを形にして確かめるための試作です。正しく動かない可能性があります。

作者の環境(フルトラッキング + VR)でしか確かめていません。それ以外では測位がずれたり、そもそも動かなかったりします。想定していない使い方はもちろん、想定した使い方でも壊れます。

未検証・未解決の項目は「未実装・未確認」にまとめてあります。実機で確かめた項目だけを「実機で確認済み」に挙げています。そこに載っていないことは確かめていないと考えてください。

アバターに手を入れるツールです。試す前にプロジェクトのバックアップを取ってください。

前提

生成される座標は IsLocal(Import 時のプレイヤー位置・向きが基準)です。自動読み込みを使わない場合は、確定した場所・向きから動かずに Import する必要があります。

トラッキングモードで精度が変わります

フルトラッキング推奨。3点トラッキングでは Confirm の瞬間に正面を向いてください。

プローブ(アバター側)とドリー座標系(VRChat 側)は、追従する対象が違います。

プローブ座標系の基準 ドリー IsLocal の基準
フルトラッキング プレイスペース
3点トラッキング プレイスペース

3点ではアバタールートが頭に追従するため、Confirm した瞬間の「首の向き − 体の向き」がそのまま方位誤差になります。 誤差は中心までの距離に比例するので、遠くを狙うほど大きく開きます。

同じマーカーに対して首だけを左右45度振り、3回生成した実測です(2026-08-08)。

プローブが見た方位の振れ幅 ワールド上のパスのずれ
フルトラッキング 0.033° 0.016 m
3点トラッキング 102.55° 11.85 m

フルトラでは首を振ってもプローブ座標系が回らないため、両者が一致し続けます。ドリー座標系の向きは3回とも小数5桁まで同一でした。

3点で運用する場合は、Confirm の直前に一呼吸おいて正面を向いてください。体は首の向きに遅れて追従するので、止まっていれば体が首に追いつきます。首と体をそろえた状態なら残差は 1.4〜1.9°(12.5 m 先で 0.3〜0.4 m)に収まります。

ツール側からトラッキングモードは判別できないため、警告は出ません。

ライセンス

MIT License です。全文は LICENSE にあります。

無保証です。POC である点と併せてご理解ください。

同梱していないもの

FloorPointer(PAF|レーザーで指定した場所にワールド固定)は含まれていません。 本ツールは、利用者が別途入手したそれに対してガイドと測距レイを取り付けるアドオンです。コードを取り込んでもいません。参照しているのは Hierarchy 上のオブジェクト名(FloorPointerObject_1Object_5)だけです。

したがって FloorPointer の利用条件は、本ツールの MIT ではなく配布元の定めるものが適用されます。

VRChat SDK3 - Avatars と Modular Avatar も同様に同梱しておらず、それぞれの配布元の条件に従います。

入手

ローカルビルドはしません。すべて GitHub Actions 上でビルドします。

main への push ごとにリリースを作ります。 バージョンは v0.0.1 から始まり、ビルドのたびにパッチ番号が 1 つ上がります。タグはワークフローが自動で打つので、手で付ける必要はありません。

用途 入手先 ファイル名
通常 Releases の最新 VRCDollyPivotManager_v0.0.N.zip
過去のビルド Releases の一覧、または Actions タブの Artifacts 同上

zip の中身です。

用途
VRCDollyPivotManager.exe PC 側のツール本体
config.json 設定。exe と同じフォルダに置いてください
VRCDollyPivotManager.unitypackage アバター側のエディタ拡張
README.md このファイル
LICENSE ライセンス(MIT)

Actions の Artifacts は GitHub 側でさらに zip に包まれるため、ダウンロードすると zip の中に zip が入った状態になります。Releases から取れば zip がそのままです。

任意のバージョンを指定したい場合は、自分でタグを打てばその名前が使われます。

git tag v0.1.0
git push origin v0.1.0

アバター側の導入

PC 側の exe だけでは動きません。 アバターに測距用のレイとメニューを仕込む必要があります。

必要なもの

用途
VRChat SDK3 - Avatars 必須
Modular Avatar 必須。パラメータとメニューの合成に使います
FloorPointer **必須。**別途購入・導入してください(下記)

FloorPointer について

本文では短く FloorPointer と書いていますが、これは Hierarchy 上のオブジェクト名です。製品としての正式名称と入手元は次の通りです。

正式名称 PAF|レーザーで指定した場所にワールド固定
入手元 https://booth.pm/ja/items/6073186

**同梱していません。**本ツールとは無関係の第三者による作品です。上記から各自で入手してください。

このツールは FloorPointer が作る固定点(Object_1Object_5)にガイドと測距レイを取り付けるアドオンです。アバター直下に FloorPointer が無い状態でセットアップを実行すると、警告を出して中止します。

手順

  1. 対象のプロジェクトを開き、Assets > Import Package > Custom Package... から VRCDollyPivotManager.unitypackage を選ぶ

    Project ウィンドウへドラッグ&ドロップでも入ります。ダブルクリックは避けてください。.unitypackage の関連付けが Unity に向いていないと、何も起こらずログにも何も出ません。

  2. Hierarchy でアバター(VRCAvatarDescriptor を持つオブジェクト)を選ぶ

  3. Tools > CamDrone > Setup Player Probe (Object_5 Only) を実行

  4. Tools > CamDrone > Setup Orbit Guide (Object_5 Only) を実行

  5. アバターをアップロード

アニメーション・アニメーター・メニュー・マテリアルはセットアップ実行時に自動生成されます。 unitypackage に入っているのはエディタ拡張のスクリプト2つとテクスチャ4枚だけです。

unitypackage は Unity から書き出したものをリポジトリルートに置いています。**CI では組み立てません。**Unity 無しで正しい形にするのが割に合わなかったためです。エディタ拡張を変更したときは、書き出し直してコミットする必要があります。

取り外すときは Tools > CamDrone > Remove Orbit GuideRemove Player Probe を実行してください。

ガイドは既定で非表示です

**アバターを読み込んだ直後、ガイドは出ません。**設定値のどれかを初期値から動かすと自動で出ます。メニューの Guide で手動の切り替えもできます。

既定を表示にしていない理由です。**設定値は保存しないので、ワールド移動などでアバターが読み込み直されると初期値へ戻ります。**そのときガイドだけ残ると、何も指定していないのに前の円が出ている状態になります。

一度自動で出したあとは、繰り返し出しません。手で消したものが戻らないようにするためです。設定値がすべて初期値へ戻れば、また自動表示の待機に入ります。

単独スロット版と5スロット版

通常は (Object_5 Only) の付いたほうを使ってください。 同時に読み込ませられるパスは1本なので、固定点を5つぶん持っても1つしか使えません。

(1-5 All) の付いた版は、5つの固定点すべてにガイドと測距レイを付けます。性能の差は小さくありません。

5スロット版 Object_5 のみ
VRCRaycast 15(Poor 判定の上限ちょうど) 3
ParticleSystem 15 3
MeshRenderer 10 2
ポリゴン 2,560 512
パラメータ 100 20

単独スロット版では次のようになります。

  • **Object_1Object_4 は FloorPointer 本来の用途に残ります。**旋回の中心には Object_5 を使ってください
  • メニューが1段浅くなります(Pivot を選ぶ階層が消え、Pivot の下がそのまま設定です)
  • **PathIndex は 0 固定です。**Multi ストリーミング(同時4パス)は使えません
  • 対象外の固定点に前回のガイドやレイが残っていれば、実行時に自動で消します

PC 側のツールは共通です。CamDrone/Obj{N}/... を 1〜5 まで一律に待ち受ける作りなので、どちらの版でも設定を変える必要はありません。

更新するとき

新しいバージョンの unitypackage をインポートして上書きし、セットアップを再実行してください。生成物は毎回作り直されます。

GUID は据え置きなので、上書きしても既存の参照は切れません。

exe とアバターは同じバージョンで揃えてください。 片方だけ新しいと、後述の「アバターと一致必須の値」がずれて生成結果が狂います。

現在値が揃わないとき

通常は何もしなくて構いません。 ツールは起動直後に VRChat へ現在値を問い合わせ、全パラメータを取り込みます。使う直前に起動しても大丈夫です。

なぜ問い合わせが要るかというと、VRChat は値が変化したときにしか OSC を送らないからです。ツールを起動する前に設定していたスライダーの値は、その後動かさない限り一度も届きません。この状態で Confirm すると、届いている値だけが使われ、残りは既定値になります。中心がずれた小さい円ができるのはこれが原因です。

問い合わせに失敗したときは、アバターを一度読み込み直してください

ロード時には全パラメータが一度送られるので、それで現在値が揃います。

失敗するのは zeroconf が無い、mDNS が通らない、VRChat がまだ起動していない、といった場合です。そのときはログに出ます。

現在値を取得できませんでした。アバターの読み込み直しで代用してください

トレイの「VRChat から現在値を取得(OSCQuery)」から手動で再試行もできます。

アバターを切り替えると、こちらも値を捨てます

アバター側のパラメータは読み込みで既定値に戻ります。ツールが前の値を持ち続けると、ガイドが示す円と生成されるパスが食い違います。 半径をガイドが 2 m と示しているのに、7 m のパスが出るような状態です。

/avatar/change を受けた時点で、メニュー値も測距値も全部捨てて既定値へ戻します。直後に VRChat が全パラメータを送ってきますが、取りこぼしても既定値のままなので**アバター側の表示と食い違いません。**あわせて OSCQuery でも取りに行きます。

受信値をファイルに保存して次回起動時に復元することはしません。 ツールを止めている間に設定を変えていると前回の値が黙って使われ、正常に動いているように見えたまま誤った出力が出るためです。実際にそれで原因の切り分けに時間を要しました。

問い合わせるか読み込み直すかすれば全パラメータが新しく届くので、そちらのほうが確実です。

Confirm 時のログでは値の出どころが分かります。

表示 意味
[受信] 今回のセッションで受信した
[初期] 一度も受信しておらず初期値を使った

[初期] が並んでいたら、アバターを読み込み直してください。

値はパペットの表示と同じ % でも出るので、VR 内で見えている数字とそのまま突き合わせられます。

  Radius       パペット  15.2%  ->      4.64 m   [受信]

config.json

exe と同じフォルダに置きます。カレントディレクトリではなく、exe が置かれた場所を見ます。

{
  "connection": "oscquery",
  "fallback": true,
  "receive_port": 9001,
  "send_port": 9000,
  "output_dir": "%USERPROFILE%\\Documents\\VRChat\\CameraPaths",
  "auto_import": true,
  "auto_play_delay": -1,
  "write_at_press": false,
  "rho": 0.2,
  "reference_eye_height": 1.643425,
  "jitter_vertical_ratio": 0.2,
  "debug": false
}

この12個が設定できるすべてです。 ここに無い値はプログラム定数として固定してあります。

キー 既定 説明
connection "oscquery" "oscquery" または "osc"
fallback true 優先した方が失敗したときもう一方を試す
receive_port 9001 OSC 受信待ちポート
send_port 9000 OSC 送信ポート
output_dir Documents 配下 軌道計算結果の出力先
auto_import true 生成直後に VRChat へ読み込ませる
auto_play_delay -1 読み込み後の再生。負の値と省略で再生しない、0 で即時、正の値でその秒数後
write_at_press false Confirm を押した時点の値でも1本出力する(読み込ませない)
rho 0.2 プレイヤーコライダー表面から中心までのオフセット(m)。校正値
reference_eye_height 1.643425 水平座標の正規化の基準となる高さ(m)。校正値
jitter_vertical_ratio 0.2 上下の揺らぎを水平の何倍にするか。0 で上下の揺らぎ無し
debug false true にすると受信したデータを逐次ログへ出す

接続方法

どちらの方式でも UDP で受信する点は同じで、違いはVRChat に送信先をどう知らせるかです。

方式 動作
oscquery mDNS で自分を広告し、VRChat に見つけてもらう。加えて VRChat へ現在値を問い合わせられる
osc receive_port を固定で使う。VRChat 側の送信先を手で合わせる必要がある

oscquery が失敗する条件(zeroconf 不在、mDNS が通らない、ポート衝突)ではログに理由を出し、fallback が true なら osc に切り替えます。切り替わったときに初めて receive_port が使われます。

待ち受けポートを方式ごとに分けています

方式 待ち受け
oscquery 空きポート。番号は mDNS で VRChat へ伝えるので、設定値である必要がありません
osc(フォールバック含む) receive_port

分けているのは、UDP リピーターで VRChat の OSC を複数のツールへ配っている場合に、同じ配信を二重に受け取らないためです。リピーター経由の分と、OSCQuery を見た VRChat が直接送ってくる分が重なります。

Confirm は立ち上がりで検出しているので同時に2つ届く分には耐えますが、2系統がずれて届くと立ち上がりが2回成立し、1回の押下で2本生成されます。

空きポートならリピーターの配布先と重なりません。receive_port はフォールバック専用として空けておけます。

mDNS ではループバックを広告します

VRChat は localhost からの通信しか受け付けません。 LAN の IP を広告すると、ツリーを読んでもらえても VRChat 側から送ってきません。

以前は外向きのソケットから LAN の IP を取って広告しており、OSCQuery 経由の受信が成立していませんでした。 届いていた値はすべてリピーターか既定ポート経由です。

OSCQuery で現在値を取得できます

素の OSC は値が変化したときにしか届きません。 ツールを起動する前に設定していた値は、その後動かさない限り一度も来ません。

OSCQuery なら VRChat 側も HTTP でパラメータツリーを公開しているので、いつでも全パラメータの現在値をまとめて取得できます。 起動時に自動で取得し、トレイの「VRChat から現在値を取得(OSCQuery)」からいつでも再取得できます。

これが効いていれば、アバターを読み込み直す必要はありません。

取り込むのはメニュー値と測距値の両方です。素の OSC は値が変化したときしか届かないため、ツール起動前から当たり続けているレイの _Hit は一度も来ません。これを取り込まないと「当たっているか分からない」まま計算することになります。

Confirm だけは取り込みません。押しボタンなので、取り込むと意図せず生成が走るためです。

取得は /avatar 以下をまとめて読みます。/avatar/parameters だけでは目線の高さ(/avatar/eyeheight)が取れず、座標の正規化ができないためです。

VRChat から送られてきているかの確認

ツールチップの「接続: OSCQuery」はこちらが広告に成功したことを示すだけで、VRChat が実際にそれを見つけて送ってきているかまでは保証しません。従来どおり固定ポートで届いているだけの可能性もあります。

区別するには receive_port を VRChat 側が知らない番号に変えて再起動してください。それでも受信件数が増えるなら、OSCQuery で発見されています。

output_dir環境変数と ~ を展開します。上記の既定値はどのユーザー環境でもそのまま使えます。%USERPROFILE%\Documents\VRChat\CameraPaths は VRChat の Camera Dolly が既定で参照する場所です。

JSON では \ をエスケープする必要があるため \\ と書くか、/ 区切りで書いてください。展開できなかった場合は exe と同じフォルダの output へ書き出し、その旨をログに残します。

ポート番号は要確認です。 VRChat は既定で 9001 へ送信し 9000 で受信するため、受信側は 9001 を既定にしています。環境が違う場合はここを合わせてください。config.json が無い場合は既定値で起動し、その旨をログに残します。

校正で決まる値

reference_eye_heightrhoVRChat 側の性質を実測して求めた値です。アバターを変えても同じ値が使えます。合わせ方はそれぞれ下記を参照してください。

アバターと同じ値を持たなければならない項目は config にありません。 下記のとおりプログラム定数として固定しています。

追加で書けるキー

上の12個のほかに template を書くと、Hue Saturation Exposure など計算で決まらない値を上書きできます。既定値は下記の「出力」を参照してください。

カメラ設定の5項目(Zoom FocalDistance Aperture Duration Speed)と PathIndex も書けますが、アバターから届いた値と Pivot ごとの割り当てが優先されます。

タスクトレイ

アイコンにマウスを乗せると次を表示します。

VRCDollyPivotManager
接続: OSCQuery
受信 127.0.0.1:9001 / 送信 127.0.0.1:9000
状態: 受信中  受信 12345 件 / 生成 3 件

「接続」の行で、どちらの方式で確立したかが分かります。

状態は直近5秒以内に OSC を受け取っていれば「受信中」、なければ「待機中」です。UDP に接続の概念が無いため、実際に値が届いているかで判定しています。アイコンの色も連動します。

右クリックメニューから次の操作ができます。

項目 内容
直前のパスをもう一度読み込ませる 最後に生成したファイルを VRChat へ送り直す
VRChat から現在値を取得(OSCQuery) 全パラメータの現在値をまとめて取り込む
高さを正確に設定(OSC送信) 注視点と円の高さを、指定したメートル値ちょうどに設定する
水平倍率の微調整(校正用) 水平倍率を ±1/2/5% ずつ動かす。下記参照
全スロットの受信状況をログへ 5点分の実測距離と算出中心を一覧でログに出す。生成はしない
現在値で出力(揺らぎなし) 受信中の値のまま、揺らぎだけ外して出力する
ログフォルダを開く / 出力フォルダを開く / 控えフォルダを開く / 終了

高さを正確に設定する

パペットは % での大まかな操作しかできず、正確な値を作れません。VRChat は OSC の入力も受け付けるので、ツールから直接パラメータを書き込みます。

トレイ → 「高さを正確に設定(OSC送信)」→ Object N → 値 を選ぶと、その Pivot の注視点と円の高さが選んだメートル値ちょうどになります。正規化はツール側で行います。

**メニューの項目名は Object N のままです。**アバター側の Pivot N と同じものを指します。

送信先は config の send_port です。VRChat 側の OSC 受信ポートと一致している必要があります。

うまくいかないとき

**まずログを見てください。**トレイの「ログフォルダを開く」から開けます。中止したときも失敗したときも理由が出ます。以下は症状から引く索引です。

Confirm を押しても何も出ない

ログに次が出ていれば、レイが固定点に当たっていません。

測距値(...)が届いていないため中止しました。生成も読み込みもしていません

中心が求まらないまま進むと自分の足元を中心にした無意味なパスができるので、意図的に止めています。 確認する順序です。

  1. FloorPointer で印を置いたか
  2. 単独スロット版なら Object_5 に置いたかObject_1 に置いても届きません)
  3. 印とアバターの間に別のコライダーが挟まっていないか
  4. ログに [初期] が並んでいないか → 現在値が揃わないとき

ログに何も出ないなら Confirm 自体が届いていません。VRChat 側で OSC が有効か確認してください。

円が思ったより小さい、中心がずれている

現在値が届かず既定値が使われています。 Confirm 時のログで出どころが分かります。

表示 意味
[受信] 今回のセッションで受信した
[初期] 一度も受信しておらず初期値を使った

[初期] が並んでいたら 現在値が揃わないとき を見てください。

カメラの軌道が印の位置とずれる

原因は4つ考えられます。上から順に疑ってください。

  1. **3点トラッキングで使っている。**測距の基準が頭になり、首を振るだけでずれます。前提に詳細があります。フルトラッキングなら一致します
  2. **手で Import した。**座標は IsLocal(Import した瞬間のプレイヤーの位置と向きが基準)です。確定してから動くと、その移動がそのままずれます。auto_import を使ってください
  3. **目線の高さを取れていない。**ログに次が出ます
目線の高さを取得できませんでした。水平倍率は無補正のままです
  1. アバター側と PC 側で定数が食い違っている。アバターと一致必須の値の表で照合してください。基線長がずれると中心の位置そのものが動きます

読み込まれない、カメラに出てこない

ログに次が出ていれば、送信そのものができていません。

OSC 送信クライアントが無いため自動読み込みできません
自動読み込みに失敗しました: ...

次が出ているのに出てこない場合は、VRChat 側の見ている場所が違います。

自動読み込み  = /dolly/Import へ送信しました
  • カメラが Dolly モードになっているか
  • **見ている Path 番号が合っているか。**単独スロット版は Path 0 固定です
  • Multi ストリーミングは 4パスまでで、それを超えたパスは無視されます

読み込まれるが再生されない

auto_play_delay が負のときは再生を指示しません(既定は -1)。0 で即時、正の値でその秒数だけ待ちます。

再生はされるのに速さが変わらない場合は、VRChat の Motion Control を確認してください。時間ベースなら点ごとの Duration、速度ベースなら Fly Speed スライダーが使われます。

現在値の問い合わせに失敗する

zeroconf が無いため問い合わせできません
現在値を取得できませんでした。アバターの読み込み直しで代用してください

zeroconf が無い、mDNS が通らない、VRChat がまだ起動していない、のいずれかです。現在値が揃わないときの手順で代用できます。

動作

常時 — 測距値(CamDrone/Probe{N}_{A,B,C}_Distance_Hit)を時刻付きのリングバッファへ5秒分、メニュー値は最新値のみ保持します。計算も書き出しもしません。

Confirm の立ち上がりで — そのスロットの軌道を生成します。

  1. 押される1.2秒前〜0.3秒前の区間を切り出す。押した瞬間ではありません。メニュー操作で腕を上げると胸が動き、胸を狙っているレイの値が変わるためです
  2. 中央値を取って三辺測量 → 中心の相対位置
  3. メニュー値を実寸へ戻す
  4. 点を生成して JSON を書き出す

測距値が3本揃わないときは中止します。 中心の (0,0) は「自分の足元」であって推定値ではないので、それで生成すると意味のないパスを読み込ませることになります。生成も読み込みも行わず、どの軸が欠けていたかをログに残します。

メニュー値が未受信の場合はアバター側と同じ既定値で計算し、その旨をログに残します。

出力

R{半径}P{点数}H{中心高さ}_YYYYMMDDhhmmss.json という名前で、同じ内容を2箇所へ書き出します。例: R2.00P6H1.20_20260805031234.json

場所 用途
config の output_dir VRChat から Import する先
exe と同じフォルダの data 控え。出力先を変えても手元に履歴が残る

控えの書き出しに失敗しても、output_dir への書き出しが成功していれば生成は成功として扱います(ログに警告は残ります)。

トレイの「控えフォルダを開く」から data を開けます。

作ったパスを管理する機能はありません

このツールがやるのは「作って VRChat に読み込ませる」までです。 名前を付け直す、要らないものを消す、並べ替える、後から編集する、といった管理機能はありません。生成のたびにファイルが増え続けます。

管理したい場合は、その用途に作られた別のツールを併用してください。

本ツールとは無関係の第三者による作品です。動作の保証や問い合わせ先ではない点にご注意ください。

カメラ設定

5項目をアバターのメニューから変えられます。 ルートメニューの Camera にあります。スロットには属さず、全体で1組です。本仕様では一度に1本しかパスを作れないため、Pivot ごとに分けていません。

Camera
├ Lens ▸
│  ├ Zoom               パペット
│  ├ Zoom 初期化         ボタン
│  ├ FocalDistance      パペット
│  ├ FocalDistance 初期化 ボタン
│  ├ Aperture           パペット
│  └ Aperture 初期化     ボタン
└ Motion ▸
   ├ Duration           パペット
   ├ Duration 初期化     ボタン
   ├ Speed              パペット
   └ Speed 初期化        ボタン

VRChat のメニューは1階層に8個までしか置けません。項目ごとに初期化ボタンを付けると10個になるため、意味で LensMotion に分けています。

範囲 既定 意味
Zoom 20〜150 45 画角
FocalDistance 0〜10 1.5 焦点距離
Aperture 1.4〜32 15 絞り
Duration 0.1〜60 2 1点あたりの秒数
Speed 0.1〜15 3 速度

いずれも VRChat の公式既定値・範囲と一致させてあります。

パペットは % での大まかな操作しかできず既定値ちょうどには戻せないため、項目ごとに初期化ボタンを添えてあります。押している間だけ既定値を書き戻します。

Confirm 時のログに、パペットの % と実寸の両方が出ます。

  Zoom          パペット  19.2%  ->     45.00       [受信]
  FocalDistance パペット  15.0%  ->      1.50       [受信]
  Aperture      パペット  44.4%  ->     15.00       [受信]
  Duration      パペット   3.2%  ->      2.00       [受信]
  Speed         パペット  19.5%  ->      3.00       [初期]

[初期] は一度も受信しておらず既定値を使ったことを示します。

Duration はダイヤルが粗いです。 範囲が 0.1〜60 秒と広いので、1% 動かすと 0.6 秒変わります。既定の 2 秒はダイヤルの 3.2% の位置にあり、狙って合わせるのは困難です。正確な値が要るときは初期化ボタンで 2 秒に戻してください。

DurationSpeed は同時には効きません。 VRChat の Motion Control が時間ベースなら Duration、速度ベースなら Fly Speed スライダーが使われます。Duration を変えても再生が変わらないときは、Motion Control を確認してください。

書き出し先のパス

Pivot ごとに固定です。メニューからは変えられません。

Pivot Path
1 0
2 1
3 2
4 3
5 0

VRChat の Multi ストリーミングは 4 本までしか扱えず、それを超えたパスは無視されます。そのため 0〜3 に収め、5 番目は 1 番目と同じ 0 を使っています。

Pivot 1 と Pivot 5 で生成すると同じパスへ書き込まれるので、片方が上書きされます。

計算で決まらない残りの値

上記以外はサンプルの値をそのまま使います。config の template で上書きできます。

IsLocal true / Hue 120.0 / Saturation 100.0 / Lightness 50.0
LookAtMeXOffset 0.0 / LookAtMeYOffset 0.0 / Exposure 0.0

カメラ設定の5項目と PathIndextemplate に書けますが、アバターから届いた値とスロットの割り当てが優先されます。

自動読み込み

生成した直後に、VRChat へ OSC でパスを送って読み込ませます(auto_import、既定 ON)。

/dolly/Import       s   ファイルパス
/dolly/Play         T   再生
/dolly/PlayDelayed  i   指定秒後に再生

VRChat が OSCQuery で公開している実在のアドレスです。

auto_play_delay で読み込みに続けて再生まで行えます。

動作
省略 / 負の値 再生しない
0 即時再生(/dolly/Play
正の値 その秒数だけ待ってから再生(/dolly/PlayDelayed

撮影開始まで姿勢を整える猶予がほしい場合は正の値を指定してください。

これが重要なのは、座標が IsLocal(Import した瞬間のプレイヤーの位置と向きが基準)だからです。 手でカメラメニューを開いてパスを貼っていると、その間の移動や向きの変化がそのままずれになります。Confirm を押した数ミリ秒後に読み込ませてしまえば、その余地が無くなります。

読み込みに失敗した場合は、トレイの「直前のパスをもう一度読み込ませる」で送り直せます。

Confirm 時点との比較用

測定には Confirm を押す 1.2〜0.3 秒前の区間を使っています。押す動作で腕を上げると胸が動き、胸を狙っているレイの値が変わるためです。

その差を確認できるよう、押した時点の値でも1本出力します(ATPRESS_ を接頭辞に付けたファイル)。これは読み込ませません。 出力するだけです。

ログに両者の中心座標と差が出ます。

[Confirm 時点との比較]
  区間の中央値  = (+0.198, +16.295)  距離 16.300 m
  押した時点    = (+0.241, +16.187)  距離 16.189 m
  差            = 0.117 m

不要なら write_at_press を false にしてください。

アバターと一致必須の値

次の12組はアバター側と PC 側の両方が同じ値を持っています。仕様として固定で、config からは変えられません。

PC 側(VRCDollyPivotManager.py アバター側 意味
PROBE_BASELINE CamDronePlayerProbeSetup.Baseline 1.5 プローブ3点の間隔(m)
HEIGHT_MIN / HEIGHT_MAX CamDroneOrbitGuideSetup.HeightMin / HeightMax −1.5 / 4.0 高さの範囲(m)
RADIUS_MIN / RADIUS_MAX RadiusMin / RadiusMax 1.0 / 25.0 半径の範囲(m)
TILT_MIN / TILT_MAX TiltMinDeg / TiltMaxDeg −30 / 30 傾きの範囲(度)
LOW_POINT_START_DEG / _SWEEP_DEG TiltDirMinDeg / TiltDirMaxDeg 90 / +180 最下点の方位(度)
SLOT_COUNT SlotCount 5 スロット数
ZOOM_MIN / ZOOM_MAX ZoomMin / ZoomMax 20 / 150 画角の範囲
DURATION_MIN / DURATION_MAX DurationMin / DurationMax 0.1 / 60 1点あたりの秒数の範囲
SPEED_MIN / SPEED_MAX SpeedMin / SpeedMax 0.1 / 15 速度の範囲
FOCAL_DISTANCE_MIN / _MAX FocalDistanceMin / FocalDistanceMax 0 / 10 焦点距離の範囲
APERTURE_MIN / APERTURE_MAX ApertureMin / ApertureMax 1.4 / 32 絞りの範囲
DEFAULT_MENU 各パラメータの既定値 8項目 未受信時に使う値
DEFAULT_CAMERA ZoomDefault ほか 5項目 同上(カメラ設定)

アバター側を変えたら、必ず PC 側も同じ値に直して両方をビルドし直してください。 片方だけ直しても動いてしまい、生成結果が静かにずれます。

食い違うと何が起きるか

パペットは 0〜1 の正規化値しか送らないので、実寸に戻すのは PC 側です。範囲がずれれば、届いた % は同じでも別の実寸になります。

基線長の例です。Unity 側が 0.75 m、PC 側が 1.5 m だと

計算される距離 = 本来の距離 × 0.5 + 0.56

となり、中心が本来の位置とプレイヤーのほぼ中間に出ます。

最下点の方位の例では、片方だけ半周に直すとガイドの目印と生成される軌道が最大 180 度ずれます。

確認方法

Unity 側の実際の値はセットアップ時のダイアログとシーン上で確認できます。

  • 基線長 — Object_N/ProbeRig/ProbeBPosition XProbeC は Position Z)。セットアップ実行時のダイアログにも出ます
  • その他 — アバター側のセットアップスクリプト(CamDroneOrbitGuideSetup.cs / CamDronePlayerProbeSetup.cs)の定数

PC 側は起動時のログに基線長を出します。

基線=1.5 m  ρ=0.2 m  揺らぎ既定=±10%(アバターから届けばそちらを使う)

アバタースケールを変えても基線長は変わりません

ProbeRigModularAvatarWorldScaleObject が付いていて、ワールドスケールを 1 に固定しています。これが無いとアバターを 2 倍にしたとき ProbeB のローカル 1.5 m がワールドでは 3 m になり、基線長だけが変わってしまいます(VRCRaycastapplyTransformScale が false なので測距はワールド単位のまま)。

2026-08-08 にアバタースケール約2倍で実測し、円の位置がほぼ一致することを確認済みです。

計算

azimuth = 90° - LowPoint × 180°    最下点の方位(0 が正面奥、+90 が右)
tilt    = -30° + Tilt × 60°
R       = 1 + Radius × 24          半径 (m)
ringY   = -1.5 + RingHeight × 5.5  カメラが回る高さ (m)
centerY = -1.5 + Height × 5.5      注視点の高さ (m)

各点: 円を Points で等分した位置に、半径と高さの揺らぎを与える(下記)
      周回の向きは CW(ON=上から見て時計回り)で反転する
      p' = Ry(azimuth) · Rx(tilt) · p
      位置 = 中心 + p' + (0, 上下の揺らぎ, 0)
      Rotation.Y = degrees(atan2(d.x, d.z)) mod 360
      Rotation.X = -degrees(atan2(d.y, 水平距離))

回転の順序は Unity 側の階層(TiltAzimuth(Y) → TiltPivot(X))と同じでなければ一致しません。逆順にすると傾きの軸そのものが回ります。

回転の式は実サンプルの48点すべてで 1e-5 度以内の一致を確認済みです。

最下点の向き(Low Point)

パラメータ 0〜1 は一周ではなく「右→手前→左」の半周に割り当てています。

Low Point 方位 最下点
0% 90°
25% 135° 右手前
50% 180° 手前
75% 225° 左手前
100% 270°

手前側の半周を使うのは、目印を中心軸の向こう側に置くと軸に隠れて見えないためです。

この方位はガイドの目印(LowPoint)が出る場所そのものです。目印は円の局所 +Z に置いてあるので、Tilt の符号によらず必ずこの方位を指します。

ただし目印が最下点になるのは Tilt が正のときだけです。負のときは同じ場所が最高点になります。 どちらの端でも傾き方はパーティクルの円を見れば分かるので、目印の位置は変えていません。名前が「Low Point」なのは Tilt が正の場合を基準にしているためです。

Tilt 目印の意味
正(パペット 50% 超) 最下点
0(パペット 50%) 円が水平。目印は効きません
負(パペット 50% 未満) 最高点

奥側の半周は Tilt の符号を反転すれば出せます(円が 180° ひっくり返るため)。

最下点 0% 50% 100%
Tilt 正 手前
Tilt 負

2026-08-09 に Tilt ±30°/+15° × Low Point 0/50/100% の5本を実測し、ガイドの目印と生成される軌道が 0.6 度以内で一致することを確認済みです。**ただしこれは半周を奥側に取っていたときの実測です。**2026-08-11 に手前側へ 180° 回しました。ガイドとツールを同じだけ回しているので一致は保たれるはずですが、回したあとの絶対的な向きは未確認です。

一周させる必要がないぶん、パペットの分解能が倍になります。

増える向きは VRChat のラジアルパペットのダイヤルと同じ向きに合わせてあります。逆にするとパペットを右へ倒したのに最下点が左へ行きます(2026-08-09 に実測して修正)。

Tilt が 0(パペット 50%)のときは Low Point を動かしても何も変わりません。 円が水平なので最下点が定まらないためです。

アバター側の CamDroneOrbitGuideSetup.TiltDirMinDeg / TiltDirMaxDeg と、ツール側の LOW_POINT_START_DEG / LOW_POINT_SWEEP_DEG必ず同じ値にしてください。片方だけ直すとガイドの目印と生成される軌道がずれます。ガイドとツールは同じ式・同じ座標系(YawFollow がアバタールートのヨーに追従)で計算しています。

揺らぎ(Random)

半径方向と上下方向にだけ与えます。円周に沿う向きへは動かしません。

半径 = R × (1 + Random% × w(t))       w は -1〜+1 の滑らかな波
上下 = R × Random% × 0.20 × w'(t)     水平の 1/5
w(t) = ( sin(2·2πt + φ₁) + sin(3·2πt + φ₂) + sin(5·2πt + φ₃) ) / 3
t    = 経路全体(全周回ぶん)での進み具合 0〜1

φ は生成のたびに引き直すので、同じ設定でも毎回ちがう軌道になります。倍音が整数なので終端と始端がつながり、周回ごとに表情が変わります。倍音の数で割っているため、指定した % を超えて振れることはありません。

倍音は固定です

(2, 3, 5)プログラム定数で、設定から変えられません。 総点数に対して大きすぎる倍音を入れると、点で山を表現しきれず波がギザギザに化けるためです。

半径 10 m / Points 12 / 4周 / Random 20% での実測です(揺らぎなしの折れ角は 30°)。

倍音 1周あたりの山 半径の振れ 指定値に対して 折れ角 最大
(1, 2) 0.50 1.69 m 85% 30.7°
(2, 3) 0.75 1.87 m 93% 31.9°
(2, 3, 5) 1.25 1.56 m 78% 33.5°
(3, 5, 8) 2.00 1.58 m 79% 38.8°
(5, 8, 13) 3.25 1.58 m 79% 50.8°
(2, 3, 5, 7, 11) 2.75 1.37 m 68% 40.6°

倍音を大きくすると細かく波打ちます。(1, 2) は4周かけて半周ぶんしかうねらず、(5, 8, 13) は1周に3回以上うねって折れ角が理論値の 1.7 倍まで跳ねます。上限の目安は max(倍音) ≦ 総点数 ÷ 5 で、48点なら 9 あたりです。

本数を増やすと山が打ち消し合って振れが弱まります。3本で指定値の約 78% です。

なぜ乱数をそのまま使わないか

以前は X と Z を独立に乱数で振っていました。これだと円周に沿う向きにも揺らぎが乗り、点の間隔が乱れます。 VRChat は点間を一定時間で補間するので、間隔のばらつきはそのままカメラ速度のばらつきになります。

半径 10 m / Points 12 / 4周での実測です。

Random 間隔 最小 最大 速度の振れ
旧(XZ を独立に)20% 1.12 m 10.18 m 9.1倍
現行(半径方向・滑らか)20% 4.39 m 5.97 m 1.4倍

現行の 1.4 倍は Points 3〜12 のどれでも同じです。軌道の崩し具合は保ったまま、速度だけが揃います。

上下だけ係数が小さい理由

上下も半径に比例させますが、水平の 1/5 にしています(config の jitter_vertical_ratio、既定 0.2)。Random 10% で半径の 2%、20% で 4% が上限です。

水平と同じ比率にはできません。半径は 1〜25 m と 25 倍の幅があり、20% をそのまま上下へ入れると半径 25 m で ±5 m 振れて床下や頭上まで抜けます。Random% を手で下げても効くのは 2 倍ぶん(20%→10%)で、25 倍の幅には足りません。

半径 Random 上下 片側 ± 対半径 半径の全振れ
2 m 10% 0.031 m 1.57% 0.31 m
2 m 20% 0.063 m 3.15% 0.63 m
10 m 10% 0.156 m 1.56% 1.57 m
10 m 20% 0.314 m 3.14% 3.14 m
25 m 10% 0.392 m 1.57% 3.93 m
25 m 20% 0.785 m 3.14% 7.85 m

(Points 12 / 4周・300回平均)

式の上限は 2% / 4% ですが、実際に出るのは平均 1.57% / 3.14% です。 3つの波の山がそろうことは滅多にないためで、上限を超えることはありません。

対半径の比が Random ごとに一定なので、上下のために Random を選び分ける必要はありません。 遠景でも近景でも同じ深さのうねりに見えます。

好みで変えられます。0 にすると上下の揺らぎが無くなり、水平方向だけになります。半径 10 m / Random 20% での実測です。

jitter_vertical_ratio Random 10% Random 20% 上下 片側 ±
0.0 0% 0% 0.000 m
0.1 1% 2% 0.156 m
0.2(既定) 2% 4% 0.312 m
0.4 4% 8% 0.623 m
1.0 10% 20% 1.558 m

1.0 は水平と同じ比率です。半径 25 m で ±5 m 振れて床下や頭上に抜けるため推奨しません。

大半径 × 低い円 × Random 20% は床を割ります

上下が半径に比例するため、半径を大きく取ったうえで円の高さを低くすると、うねりの谷が床下へ抜けます。

半径 円の高さ 最低点
10 m 0.4 m 0.002 m
10 m 1.2 m 0.802 m
25 m 0.4 m −0.596 m
25 m 1.2 m 0.203 m

(Random 20% / Points 12 / 4周・300回の最低点)

ドリーカメラは床を貫通するので撮影自体は止まりませんが、絵としては不自然です。半径 25 m 級で低い円を回すときは Random を 10% に落とすか、円の高さを 1 m 以上に取ってください。

座標の正規化

VRChat が IsLocal のドリー座標を正規化するのは水平方向(X, Z)だけです。Y はメートルのまま解釈されます。

JSON の X, Z = 実際のメートル × reference_eye_height ÷ 実際の目線の高さ
JSON の Y    = 実際のメートル(無補正)

目線の高さは VRChat が /avatar/eyeheight で送ってくるので自動で取得します。

VRChat が記録した既存のパスファイルが正しく再生されるのは、その値が最初からこの空間で書かれているためです。

根拠

半径 7.00 m / 高さ 0.90 m のパスを生成して VRChat に読み込ませ、ワールド座標に切り替えて書き出させたものと突き合わせました(2026-08-08)。48点すべての対応から「平行移動 + 向き + 水平方向の一様倍率」を最小二乗で解いた結果です。

変換 精度
向き 121.085953 度 48点のばらつき 3e-05 度
水平 world = local × 0.56893387 残差 最大 2e-06 m
高さ world = local − 0.005000 m 全48点で完全に一定

Y には倍率が掛かっていません。 残る 5 mm は床の高さで、倍率とは無関係です。Y に水平用の倍率を掛けていたため、高さだけがちょうど約2倍で再生されていました。

目線 0.935 m で割り戻すと旋回半径がちょうど 7.00000 m になるため、目線の値と指定半径の両方がこの1件で裏付けられています。

目線の高さの取得タイミング

VRChat が /avatar/eyeheight を送ってくるのは アバターの切り替え時スケール変更時だけです。ツールはそれを保持し続けます。

ツールの再起動には追従できません。 これは VRChat 側が再送しないためで、ツール側からは要求できません。

再起動後に確実な値を得るには、次のどちらかを行ってください。

  • アバタースケールをほんの少し動かす(元に戻して構いません)
  • アバターをリセット/読み込み直す

どちらでも /avatar/eyeheight が送られてきます。

Confirm のログに出どころが表示されるので、確認できます。

  水平倍率      = 1.7577(目線 0.935 m [受信] / 基準 1.6434 m)

一度も届いていない状態では倍率 1.0(無補正)で書き出し、その旨をログに残します。

想定と合わない場合はトレイの「水平倍率の微調整」で合わせてください。

倍率の校正

reference_eye_height の既定値 1.643425 は、上の実測から逆算した値です(目線 0.935 ÷ 0.56893387)。

当初の見込み値 1.85 では水平方向が 12.6% 大きく出ていました。倍率は距離に比例して効くため、中心を遠くに置くほどずれが大きく見えます。

「目線の高さに反比例する」ことは、アバタースケールを変えて確認済みです(2026-08-08)。

目線の高さ 換算係数 k 指定半径 VRChat 上 誤差
0.935 m 0.56893388 7.00 m 7.000001 m 0.000%
1.897 m 1.15436436 7.11 m 7.113841 m 0.054%

0.935 m で校正した値が、その 2.03 倍の目線でもそのまま通ります。2件目の 0.054% はファイル名が半径を小数2桁に丸めているぶんで、実質ゼロです。

ずれが残る場合はトレイの「水平倍率の微調整」で ±1/2/5% ずつ動かしながら合わせてください。

  1. マーカーをできるだけ遠くに置く(ずれが見やすくなります)
  2. ガイドの円周が自分に重なるよう Radius を合わせる
  3. Confirm → Import して、カメラが自分の上を通るか見る
  4. 手前で回っていれば +、外を回っていれば に調整して 3 へ戻る

合ったらログに出る値を config に書き写します。

水平倍率の微調整: ×1.0300  → 実効倍率 1.8104
                (確定したら reference_eye_height を 1.6927 に)

微調整はツールを再起動するとリセットされます。config に書くまでは一時的な値です。

周回数

総点数が 50 を超えない範囲で、50 に最も近くなる周回数を点数ごとに決め打ちしています。

Points 周回 総点数
3 16 48
4 12 48
6 8 48
8 6 48
12 4 48

仕様として固定で、設定から変えられません。 総点数を減らすと揺らぎが破綻するためです。

揺らぎの波は倍音 5 まで使うので、総点数が少ないとサンプリングが足りず、なめらかな波がギザギザに化けます。半径 10 m / Points 12 / Random 20% での実測です。

周回 総点数 倍音5の1山あたりの点数 進行方向の折れ 最大
4(既定) 48 4.8点 33.8°(理論値 30°)
2 24 2.4点 44.3°
1 12 1.2点 72.1°

総点数 24 以上が必要です。上の組み合わせはすべて 48 なので余裕があります。

ループの継ぎ目

最後の点から最初の点へ戻るところは、他の点と同じなめらかさです。

揺らぎの波は経路全体で 1 周期になるよう倍音を整数にしてあるので、継ぎ目も他と同じ 1/総点数 ステップにしかなりません。角度も点数で割り切れているため、戻りの一歩は他と同じ角度です。

条件 継ぎ目の折れ 他の点 平均 他の点 最大
Points 12 / 4周 29.85° 30.00° 33.79°
Points 12 / 4周 / Tilt +15° 29.85° 30.00° 33.98°
Points 6 / 8周 59.93° 60.00° 61.76°
Points 3 / 15周 119.97° 120.00° 120.67°

(半径 10 m / Random 20% / 400回平均。継ぎ目の折れが他の点の最大を超えた割合は 0.0%)

各点での折れ角は点数で決まる多角形の角度(360÷Points)そのもので、継ぎ目もその値に一致します。

ログ

exe と同じフォルダに log を作り、実行ファイル名_YYYYMMDDhhmmss.log を出します。通常ログとデバッグログは同じファイルです。debug: true のときだけ、受信した OSC が1件ずつ記録されます。

未実装・未確認

  • Y 座標は仮想床(固定点)基準の値をそのまま使っています。プレイヤーと固定点が同じ高さの床に立っている前提です
  • FocalDistanceAperture はメニューから変えられません。config の template で上書きするか、既定値のまま使ってください
  • スロット2〜5 は実機で使っていません。スロット1 と同じ作りなので動くはずですが、確認はしていません
  • 点数ごとの周回数(Points 3/4/6/8 のとき)は実機で確認していません。Points 12 のみ確認済みです
  • ループの継ぎ目は計算上なめらかであることを確認しましたが、VRChat 上での再生は見ていません

実機で確認済み

項目 確認日 内容
座標の正規化 2026-08-08 水平のみ目線の高さで正規化、Y は素通し。ワールド書き出しで裏付け
水平倍率の基準 2026-08-08 目線 0.935 m と 1.897 m(2.03倍)で同じ値が通る
アバタースケール 2026-08-08 約2倍にしても基線長が変わらない
トラッキングモード 2026-08-09 フルトラで首を振っても 0.016 m。3点では 11.85 m ずれる
最下点の向き 2026-08-09 Tilt ±30°/+15° × Low Point 0/50/100% の5本が 0.6 度以内で一致
周回の向き(CW 2026-08-09 右回り/左回りとも問題なし

About

VRChat Camera Dolly Circle Path Maker

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages