Live Studio
jp.lilium.livestudio の変更履歴です。
[0.26.1] - 2026-10-25
Changed
- デッキのタブ名を変えても、そのファイル名は変わらなくなりました。デッキはデッキを作成したときに決まるファイル名で識別され、表示名はファイルの中に保存されるため、同じ名前のデッキを 2 つ持つこともできます。以前のビルドで書かれたデッキファイルは、次に書き込まれるまでファイル名を表示します。
DeckControl.deckNameを読んでいたコードはDeckControl.deckIdを読むようになります。 - ページを開いている間、スナップショット一覧が更新のたびに Recordings フォルダを走査し直さなくなりました。アプリの外からフォルダに追加されたファイルも、引き続き拾われます。
[0.26.0] - 2026-09-27
Added
- アセットカタログがライブシーンから外れる前に書かれたライブシーンは、今の形に読み替えて読み込まれます。 カタログが保存されなくなるまでは、カタログが保存されていた影(
_persistedAssets)こそが、ショーを読み戻す場所でした:どのアバターが出ていたか、どのステージが立っていたか、そこにどのプロップがあったか。これらの答えはそれを持つオブジェクトへ移り、復元はもう存在しないメンバーを名指すエントリを黙って捨てるため、そうしたシーンはアバターもステージもプロップも剥がれた状態で開き、ただ空っぽに見えていました。LegacyAssetCatalogMigrationは、ファイルを何かが読む前に、それらのエントリが言っていたことを、今その答えを持つメンバーの言葉で言い直します:有効なアバターのエントリはExternalAvatarSource.selectedAvatarに、セットはStageManager.loadedSetsの要素に(立っていたものならactiveSetにも。無ければ当時と同じく、ブートストラップシーンが出ていたことを意味します)なり、プロップは今ならそうなるはずのシーンインスタンスとして立ち上げられます。プロップの場合は対応表が要りません:古い影はプロップの保存済み設定をobjectIdに係留しており、ファイルはそれを今も{objectId}.components[Item]宛てのエントリとして持っているので、移行したインスタンスに同じ@idを与えれば設定が付き直し、プロップは配置されたときのオフセットでソケット上に戻ってきます。プロップにはエントリがpathを持っている必要があり、これは 0.23.5 から書かれ始めました。アバターとセットは名前で指され、これはどの世代も書いていたため、それより古いファイルでも両方は戻り、失うのはプロップだけです。読み取りのみの一方向で、オペレーターが保存するまでディスク上のファイルには触れず、一度保存すれば確定します。⚠ カタログを持たないビルドで既に保存し直したシーンは復元できません:影はファイルから消えており、プロップについて残っているのは、もう誰も持たない id 宛ての設定エントリだけです。 - スタジオで行ったことがテイクに届きます。 デッキのオペレーションはリモートからの書き込みと同じように
FrameGateを通る(OperationRequestがネットワーク呼び出しが送ったはずのリクエストを組み立て直す)ため、キーの押下もリモートアプリでのタップと同じように記録されます。連続的に動く値は、変更ごとにイベントを書く代わりに state レーンに乗ります:フリーカメラとオービットフォローカメラの yaw / pitch / position / distance / fov / スクリーン位置(そのゲッターは、保存時にしか同期されない影のコピーではなく、ライブのリグを読むようになりました)、カメラのpriority、環境光、スライダーをドラッグしている間のOperationSetの手動値、TransformRefのボーンパスです。ExternalAssetManagerは、必要なアセットがまだ読み込み中の間リプレイを保留します。アバターのポーズはMotionSourceBaseが運びます(Changed を参照)。マシンローカルの設定——画面の出力設定、描画品質、プロジェクトマネージャーのポーリング間隔——はテイクに入りません。 - オペレーター自身のファイルに触れてはならない実行のためのコマンドラインオプション。
LaunchArgsは、ほかの何よりも先に決まっていなければならないことのために-name value形式のオプションを読みます:-savedBase <dir>は保存されるすべてのファイルの置き場所を移します(SavedPaths)。-project <dir>は記憶されたプロジェクトの代わりにそのプロジェクトを開き、書き戻されることはないため、次の通常の起動には影響しません。-noRemoteApp/-noCompanionAppは、ビルドが自身のリモートアプリ/コンパニオンアプリ(Fusion)を起動しないようにし、外部のドライバーを唯一のクライアントにできます。StartupSceneSwitcherはPlayerPrefsを自分で読む代わりにProjectManager.ResolveStartupProjectPathに問い合わせるようになったため、両方の起動経路がどのプロジェクトのベースシーンを読み込むかで一致します。これらは統合テストのハーネスのために追加されました。クラスが知らないオプションは無視されるため、エンジンのオプションとコマンドラインを共有できます。 - アプリに同梱されたシーンを、外部の
*.set.lsbと同じようにセットとして提供できます。 プロジェクトは、どのシーンがセットかをAssets/Resources/以下のBuiltinSetListアセットで宣言します(Assets ▸ Lilium Live Studio ▸ Create Built-in Set List から作成)。宣言された各シーンは、プロジェクトページでは組み込みアセットとして、ステージページではセットとして一覧され、バンドルのセットとまったく同じ経路で読み込み・アンロード・アクティブ化されます——StageManagerはISetAssetを通じて調整するため、教えることは何もありませんでした。宣言は意図的に「ビルド内のすべてのシーンがセット」ではなく明示的にしています:ビルドにはステージではないシーンも含まれ、ブートストラップシーンをセットとして提供すると、スタジオ全体——2 つ目のAvatarControllerも含めて——がそれ自体の上に読み込まれてしまいます。宣言は、素のシーンパスでは運べないもの——表示名とプレビュー画像——をセットが得る場所でもあり、そのインスペクタは、現場で宣言が失敗する 2 つの原因を報告します:ビルドに含まれていないシーン(追加するボタン付き)と、表示名が重複する 2 つのセットです。セットは名前で記録されるため、後者はステージの保存・記録された状態を曖昧にします。セットの同一性はそのシーンの GUID なので、シーンを移動・改名しても保存済みのライブシーンは機能し続けます。シーンパスはキャッシュとして一緒に運ばれ、シーンが移動するたびに導出し直されます。SetAssetBaseはSetBundleAssetとBuiltinSetAssetを 1 つの基底の下にまとめ、リモートアプリが個々の具象型を名指すのではなく、GET /live/typesの継承チェーンから「すべてのセット」を一覧できるようにします——公開されるのは基底クラスだけなので、インターフェースではその役を果たせません。同じ理由でAssetBase.isBuiltinが読み取り専用で公開されます(保存されるシーンは変わりません:永続化は読み取り専用プロパティをスキップします)。セットバンドルがたまたま組み込みセットと同じ名前の場合、名前による検索ではすべて組み込みの方が勝ちます——保存・記録されたステージは欲しいセットを名指し、組み込みのコピーはこのアプリを実行するすべてのマシンが確実に持っているものである一方、*.set.lsbはそのマシンのディスクにあるものにすぎないためです。ステージページはどちらがどちらかを示し、名前を共有するステージがあれば警告で名指します:同名を決着させることで復元が予測可能になりますが、同じ名前の 2 つのステージが良い考えになるわけではありません。 - 1 つのデッキは 1 つのファイル(
*.deck.json)で、プロジェクト内のすべてのデッキファイルがタブになります。 作成したオペレーション層——すべてのOperationSetとDeck——はこれまでライブシーンに埋め込まれていたため、デッキをシーン間で再利用することも、誰かに渡すことも、プロジェクトの一覧で見ることもできませんでした。デッキは今では、プロジェクトのDecksフォルダにあるファイルで、そのデッキのグリッド幅と、その上に配置されたオペレーションセットを保持します。オペレーションページのタブは、プロジェクトのクロールが見つけたデッキファイルそのものです。ファイル名 が デッキの名前です(ファイル内でそれを繰り返すことはありません)。そのため、タブを改名するとファイルが改名され、タブを追加するとファイルが作られ、タブを削除すると、それが持っていたオペレーションごとファイルが削除されます(リモートアプリが先に確認します)。保存ボタンも未保存状態もありません:すべての編集——デッキの関数からも、汎用プロパティ REST からも——は、それが着地したデッキを、そしてそのデッキだけを書き込みます。*.deck.jsonをプロジェクトフォルダに置いて再スキャンすると、タブとして追加されます。OperationManager.operationSets/decksはPersistScope.Customなので、ライブシーンはデッキについて何も運びません。プロジェクト内でシーンを切り替えてもデッキは保たれ、プロジェクトを切り替えると置き換わります。知っておくべき帰結が 1 つあります:スナップショット(とPOST /live/scene/export)はどちらも Scene スコープを書くため、デッキを運びません。 MotionSourceBase.OnSampleResolved(sample, supplied)により、ソースはサンプル自体から基準点を導き出せます。 フレームのサンプルが解決された後、配置される前に、サンプルがライブで取得されたか供給されたフレームから読み戻されたかにかかわらず実行されます。供給されたフレームでは、ソースがキャプチャーを目にする唯一の場所なので、そこから導き出す配置(たとえばカメラ合わせ)はここに置きます。VirgoMotionSourceはこれを使って、リプレイをテイク自身のキャプチャーカメラに合わせます。
Changed
- アセットカタログはライブシーンに書かれなくなり、プロップはほかのすべてと同じくシーンのオブジェクトとして出されるようになりました。
ExternalAssetManagerは、使用中のすべてのエントリを保持するカタログの永続化用の影(_persistedAssets)を持っていたため、ライブシーンは、ショーが既に答えている問いに対するもう 1 つの静かな答えを抱えていました:どのアバターが出ているかはExternalAvatarSource.selectedAvatar、どのステージが立っているかはStageManager.activeSet/loadedSets、出ているプロップはシーン内のインスタンスです。プロップにはそうした答えが無かったため、エントリがその答えを兼ね、さらに、読み込まれたラッパーのキーを付け替えるobjectIdを通じて、保存済みの上書きをぶら下げる係留点も兼ねていました。プロップは今、シーンインスタンスとしてのみステージに出ます。シーンページの「+」から生成され、カメラやほかのオブジェクトと同じく@prefab(IInstantiableProp.instanceKey)で永続化されます。これにより、同じプロップを何度でも出せます。マネージャーは (デ)シリアライズのコールバックをまったく実装しなくなったため、カタログについての何も——このマシンのディスクにどのファイルがあるか——保存されたシーンには届きません。AssetBase.state、AssetBase.objectIdの永続化、reloadsOnAvatarChangeもあわせてなくなり(シーンレベルのインスタンスはアバターと一緒に破棄されないので、次のアバターへ読み込み直すものはありません)、エントリが配置ではないカタログのためのAssetBase.isLoadableに置き換わりました。プロップページは、プロジェクトが持つプロップファイルの一覧になり、読み込みのトグルもエントリごとのプロパティペインもありません。どちらもインスタンスのあるシーンページへ移りました。以前のビルドで保存したシーンは、そのまま読まれるのではなく、読み込み時に今の形へ引き上げられます(「アセットカタログがライブシーンから外れる前に書かれたライブシーン…」の項目を参照)。単独の*.glb/*.gltfは GLTF Model オブジェクトとして出され(そのpathがファイルを選びます)、プロップの*.preset.jsonはまったく読み込めなくなりました。Save as Preset が削除されてからプロップのプリセットは作れなくなっており、それを読むことが影を生かしておく最後の理由でした。 - 「Save as Preset」はなくなりました。 読み込まれているものから名前付きの
*.preset.jsonを書き出す機能でしたが、アバターについては既に動かなくなっていました——リモートアプリが渡す id はアバターの GameObject ラッパーを名指しており、リゾルバーはそれをAvatarControllerと比較して拒否していたためです。この機能が存在した目的は、今は自動で果たされます:アバターの設定は、その隣にあるアバター自身のプリセットに、誰も何も押さなくても書き込まれます(次の項目を参照)。ExternalAssetManager.SaveAsPresetと、リモートアプリのボタン・名前入力ダイアログとその翻訳は削除され、そのために存在したAssetStateSnapshot.CaptureFullDeltaとPropAsset.CaptureDeltaStateもあわせてなくなりました。*.preset.jsonの読み込みには手を加えていません:プロジェクトに既にあるプリセットは、これまでどおり一覧に並び、読み込めます。 - アバターの設定はアバターのものになり、ライブシーンではなくアバター専用のプリセットファイルに保存されます。 表情の設定(入力ウェイトの調整、まばたきの同期、ニュートラルの表情、表情のリスト)、メッシュの表示の上書き、アニメーターパラメーターの上書き、ボディの上書きクリップ、アバターのレイヤー、下半身の固定は、どれも 1 つのモデル——そのモデルのメッシュ、アニメーター、ブレンドシェイプ——についてのものです。シーンと一緒に保存されていたため、別のシーンを開いた途端に失われ、次に来たアバターへそのまま持ち越されていました。
AvatarControllerのこれらのメンバーはPersistScope.Customになったため、ライブシーンにはどれも書かれず、代わりにコントローラーがプリセット(*.preset.json、名前付きプリセットと同じ形式)に、アバターの既定値からの差分として書き込みます。ファイルであるアバターには、その隣に同じベース名で置かれたファイルが対応します——Avatars/hoge.vrmにはAvatars/hoge.preset.json——ので、アバターとその設定を別の場所へコピーすることは、既に対に見える 2 つのファイルを移すことになります。自身のファイルを持たない組み込みアバターは、カタログの GUID をキーに{project}/Settings/AvatarPresets/のファイルが対応し、シーン自身のアバター(default)と、プロジェクト外にあるアバターファイル(そのフォルダは書き込んでよい場所ではありません)も同様です。こうしたファイルはプロジェクト内で独立したエントリにはなりません:それは隣にあるアバターそのものであり、1 つのアバターが 2 回並ばないよう、クローラーはパスでそれをスキップします。デッキファイルと同じく保存ボタンはありません——編集は最後の編集から 0.5 秒後に書き込まれ、コントローラーが持っている内容は、切り替えのたびと、コントローラーが無効化されたときに、出ていくアバターのぶんが書き込まれます。既定値のままのアバターはファイルをまったく書きません(すべてをリセットするとファイルは削除されます)。アバターを切り替えると、入ってくるアバターのファイルを適用する前に、これらのメンバーは既定値へ戻されるようになりました。以前は、前のアバターの値がコントローラーにそのまま残っていました。以前のビルドで保存したライブシーンもこれらを復元し、次の書き込みでアバター自身のファイルへ移されます。そのシーンを再び保存すると、シーンからは除かれます。名前付きプリセット(Save as Preset)は両方のスコープを運ぶようになったため、アバターの設定を、ライブシーンが持つ配置と一緒に戻します。シーンが複数のAvatarControllerを持つとき——加算読み込みしたセットが 1 つ持ち込むことがあります——ファイルを保持するのは、アバターの読み込みが振り向けられるコントローラーだけです:どのアバターが出ているかはその 1 つのコントローラーについて一度だけ答えられるため、両方が設定を保持すると同じファイルに書き込み、互いに上書きしてしまいます。もう一方は、メンバーをそのシーンが置いたままにします。収録は変わりません:テイクが運んでいたメンバーは引き続きレーンを宣言しているので、ショーの途中でメッシュを隠したり下半身を固定したりしても、引き続き収録されます。 - 画面は、立っているセットが持ち込むカメラに従い、
ScreenControllerはなくなりました——その仕事はScreenManagerのものです。 出力設定はブートストラップシーンの 1 台のカメラ上のコンポーネントにあり、そのカメラが全員のものになっていました:セットは、アプリが既に放送に乗せているものと争わずには、独自の構図やポストプロセスや背景を持ち込めませんでした。ScreenManagerは Live Studio System プレハブ内でStageManagerやEnvironmentLightと並ぶ常設オブジェクトで、設定を保持しつつカメラを 探します——立っているセットのシーンにあるカメラで、なければブートストラップシーンのものにフォールバックします。解像度とフルスクリーンは 1 度だけ再適用され、動くことはありません。それらはこのアプリが所有するウィンドウに属するためです。切り替えをまたいでカメラに追従するのは背景と Spout だけで、置き去りにされるカメラからはターゲットテクスチャと送信者が外されます。動作するカメラはちょうど 1 台だけ残ります:CinemachineBrainを持つほかのカメラはすべて、そのオーディオリスナーとともにオフにされます。そうしないと 2 つのブレーンが同じバーチャルカメラに追従して同じ絵を 2 回描き、有効なリスナーが 2 つあると警告になり、聞こえる位置が不定になるためです。ブレーンの無いカメラ——レンダーテクスチャのフィードやオーバーレイ——は他者のものなので触れず、非アクティブな GameObject 上のカメラは、作者が意図的にオフにしたものとして扱います。Spout ストリームはカメラの名前ではなく固定の名前で配信されるようになったため、セットを切り替えても OBS のソースが外れなくなりました。最初に解決されたカメラは引き続き開始時の背景の種になるため、新しいプロジェクトはシーンの作者が設定したものを引き継ぎます。種になるのは 最初の カメラだけです。そうでないと、各セットが作成時に持つ背景が、そのセットの読み込み時にオペレーターの保存済み設定を上書きしてしまいます。ScreenController自体は空の[Obsolete]コンポーネントとして残るため、まだそれを参照するシーンやプレハブも、スクリプト欠落なしに開けます——参照は解決され、コンポーネントは何もせず、都合のよいときに削除できます。何も公開しないのは、ライブクラスとしてのScreenは今ではScreenManagerの名前で、2 つの型がそれを主張すると設定ファイルを奪い合うためです。既存プロジェクトのScreen.settings.jsonはそのまま引き継がれ(jp.lilium.remotecontrolを参照)、同梱のScreenプレハブは素のカメラになったため、それを参照するすべてのシーンは手を加えずに追従します。 - ステージページでステージを選ぶと、完全に切り替わるようになりました。 セットを選ぶとそれがアクティブとしてフラグ付けされ、ほかに読み込まれたセットはすべて立ったままでした。これは操作のマルチシーン的な解釈です。オペレーターの側からすれば、ステージを選ぶことはステージを置き換えることです。ページは
StageManager.SwitchToSetを呼ぶようになり、ほかをアンロードし、対象を必要に応じて読み込みます——デッキのステージ切り替えオペレーションが常にとってきたのと同じ経路です。複数のセットを同時に保持することは変わらず可能です:セットごとの読み込みトグルは元の場所にあり、SetActiveSetは望む呼び出し側のために引き続きアクティブフラグだけを動かします。SwitchToSetByNameが名前を取るのに対しSwitchToSetが id を取るのは、名前が一意ではない——2 つのバンドルが同じ名前を持ち得る——ためで、エントリそのものを持っている側は、どのセットを意味するかを示すべきです。 - Camera、GLTF Model、External Avatar Controller の各プレハブは、
jp.lilium.remotecontrolの移行に従い、同梱のUnityBasicLiveClassesアセットで宣言されます。 シーンページの「+」は同じ 3 つを同じ順序・同じアクセスレベルで提供します。カメラページの「+」はカメラを提供するようになりました。以前はリストがシーンページにあったため、できませんでした。独自のプロジェクトは自身のライブクラスアセットにプレハブを追加します——同梱のものは読み取り専用のままです——そして Studio の UI 定義はもうファクトリーリストをまったく持ちません。 FrameRecorderControllerはjp.lilium.remotecontrol(Lilium.RemoteControl)へ移り、このパッケージはテイクの置き場所を伝えるだけになりました。 このコンポーネントが行っていたことはすべてあちらのパッケージのもの——ゲート、レコーダー、リプレイヤー、テイクの間の state / structure システムの保持、供給されたフレームへの一覧の復元——でしたが、1 つだけ例外がありました:テイクは、シーン・デッキ・アバターと同じく、開いているプロジェクトに属するということです。その答えはFrameRecorderProjectとしてここに残り、ドメインリロード時と再生時の両方でレコーダーのrecordingFolderProviderとして自身をインストールし——プロジェクトのRecordingsフォルダを指します——収録開始時にテイクの画像を撮ります。レコーダーはもうコンポーネントでもありません(jp.lilium.remotecontrolを参照):固定 id を持つ登録オブジェクトなので、Live Studio System プレハブはそれを持たなくなり、公開名は引き続きFrameRecorderです。Lilium.LiveStudio.FrameRecorderControllerを名指すソースにのみ破壊的変更ですが、まだ誰にも出荷されていません。ResolveRecordingFolderとそのテストはFrameRecorderProjectへ一緒に移りました。- ライトは、たまたま
Lightコンポーネントを持つ普通の GameObject になりました——LiveLightはなくなりました。 このプロキシクラスはLiveGameObjectWithTransformのコピー(同じtransformプロパティ、同じTransformRefの親、同じ階層の再アタッチ)に 4 つのLightプロパティを付け足したものだったため、ライトに独自のクラスは要らなくなりました:シーンのエントリは素のLiveGameObjectWithTransformで、enabled/color/intensity/shadowsは このパッケージに同梱されたライブクラスアセット(Runtime/Resources/Settings/UnityBasicLiveClasses.asset)から来て、読み込み時にLiveStudioLiveClassRegistryが適用します。ほかに作るものはありませんでした:LiveGameObjectは既に、登録されたライブクラスを持つ型のすべてのコンポーネントを一覧し、シリアライザーは既にそれらを@sourceエントリとして書くため、値は以前とまったく同じように保存・復元されます。宣言がRemoteControlContainerごとではなくグローバルに登録されるのは、登録されていない型はシリアライザーがスキップするためです——シーンごとに配線すると、参照を忘れたシーンではライトの値が黙って落ちます。セットバンドルとともに移動するアセットは、引き続きコンテナのリストを通ります。リモートアプリでは 2 点の見た目が変わります:ライト自身のプロパティは 1 階層内側、コンポーネントのペインの中に描画され、shadowsは以前のオン/オフのshadowトグルではなく本物のLightShadowsドロップダウンです([assembly: LiveExternalEnum]で登録)。ここで同梱しているすべてのシーンはその場で移行され、ライトのライブオブジェクト id を保っているため、enabled、color、intensity、Transform は既存のライブシーンから引き続き復元されます。shadowとライトのparentは、どちらの名前も残らないため引き継がれません。LiveLightFactoryも一緒になくなりました——どの UI 定義も参照しておらず、「+」メニューがライトを提供したこともありませんでした。 - テイクとスナップショットは、1 つの Recordings ページに並ぶプロジェクトアセットになりました。 スナップショットは 1 フレーム長のライブデータで、テイクはその連続ですが、別々のフォルダと別々のページにありました。
RecordingAssetは、SnapshotAssetがスナップショットを一覧するのと同じようにプロジェクト内のすべての*.live.bin(と旧来の*.livedata)を一覧するため、テイクはほかのアセットと同じくプロジェクトページに現れます——画像は汎用のGET /live/asset/{key}/@imageで提供され、Playボタンと通常の削除が付きます。RecordingManagerは、リモートアプリがテイクのために駆動するものです:トランスポート——収録、再生、単一の停止、loop、そしてseekProgress(テイクをスクラブする 0..1 のスライダーで、ドラッグ中は一時停止します。進行中の再生ヘッドとドラッグが同じフレームを奪い合うため)——と、テイクの一覧で、1 つのカードでどちらも描けるようSnapshotManagerと同じ形です。収録開始時に、ライブカメラの視点がテイクの画像としてその隣に書かれます(LiveCameraThumbnail、スナップショットと共有、FrameRecorderController.onRecordingStartedで起動)。状態の表示(isRecording、recordedFrames、isReplayingなど)はレコーダーオブジェクト自体にあり、keyframeInterval/compressはプロジェクト設定です。⚠ スナップショットはSnapshotsではなく、テイクの隣のプロジェクトのRecordingsフォルダに書かれるようになりました。スナップショット一覧はそのフォルダしか読まないため、既存の*.snapshot.json/*.snapshot.pngファイルをページに残すには移動してください(プロジェクトの一覧は元の場所でも見つけます)。 - ⚠ モーションソースは自身が生成するポーズを収録・再生し、ポーズはマッスル空間になりました。
HumanoidPoseDataは、ボーンごとの回転と腰の位置ではなく、マッスル空間のヒューマノイドポーズ——bodyPosition(アバターのスケールで割ったもの)、bodyRotation、95 個のマッスル(kMuscleCount)で、それぞれに存在値が付く——になりました。ボーンの回転は、それが作られたレストポーズに対してしか意味を持たないため、その形のテイクは撮影したモデルでしか再生できませんでした。マッスルは関節ごとに正規化されているため、同じ数値が別の体でも同じポーズを表します。MotionSourceBaseがポーズを state レーンに載せて読み戻す役を持つようになったため、ソースはサンプリングするだけで済みます:TrySample(out AvatarAnimationData, out long sampledFrom)とframeSourceを実装し、必要に応じてOnFrameHeadBeginとPlacementMatrixを実装します。配置はポーズがレーンに届いた後に適用されるため、記録されるのは配置前のポーズで、リプレイ中でも基準点を編集できます——また、テイクをリプレイするシーンにはキャプチャーの仕組みがまったく要りません。すべての独自モーションソースと、pose.hipPosition/boneRotations/bonePresencesを読むコードには破壊的変更です。OnEnable/OnDisableをオーバーライドする派生クラスは、base を呼ぶ必要があります。AvatarPoseWriterは自身を駆動するアバター(StandardAvatar、VRM0Avatar)にマッスルのポーズを適用し、HumanMuscleは 95 個のマッスルに名前を付けるため、記録されたポーズは番号付きの float ではなく関節として読めます。 - 選択中のアバターと立っているステージがライブシーンに保存され、アセットが現れ次第復元されるようになりました。
ExternalAvatarSource.selectedAvatarとStageManager.activeSet/loadedSetsは、これまでカタログの現在の状態を読んでいましたが、それはシーンの復元直後、アセットのクロールが終わる前には空です。今はオペレーターの選択を保持し、同じ宣言でシーンに保存されて state レーンで運ばれ、カタログが埋まるにつれてそれと調整されます。AvatarController.bodyOverrideClipKeyは、未トラッキングの体の上書きクリップ(ベイク済みクリップまたはパックのメンバー)を表す単一のキーで、その上書きもテイクに届くようになりました。 - 画面の背景の種類と色は、ほかの出力設定(解像度、フルスクリーン、Spout)と同じくプロジェクト設定になりました:合成の背景はマシンごとに選ぶもので、収録されるステージの一部ではありません。
- Project Settings ページは
Project/Lilium Live Studioになりました(以前はProject/Live Studio)。 - Transform マニピュレーターは
jp.lilium.remotecontrolへ移り、このパッケージは REST ルートを 1 つも登録しなくなりました。 マニピュレーターは RemoteControl 自身の概念——ライブオブジェクト、TransformValue、プロパティパス——にしか触れておらず、そのセッション/フレーム/ポーズのルートはこのパッケージが持っていた最後の専用 REST だったため、スタック全体(ManipulatorApiHandler、ManipulatorCameraService、IManipulatorTarget)が基盤パッケージに移り、すべてのホストアプリケーションが無料でギズモ編集を得られます。ワイヤは変わりません:同じ 4 つの/live/manipulator/*ルート、同じペイロードです。PropAttachmentは引き続きIManipulatorTargetを実装し、今では RemoteControl のインターフェースとしてです——依存がようやく正しい向きになりました。カメラ系のルートの廃止(下記参照)とこの移動により、LiveStudio の REST リファレンスは完全に消えます:このパッケージが追加するものはすべて、汎用の面にあるライブオブジェクトです。 - 読み込みとビルドの通知は C# のイベントになり、オブザーバーインターフェースはなくなりました。
IVRMLoadObserverとVRMLoadObserverのブロードキャストラッパーはVRMLoaderの静的イベント(onLoadStarted、onLoaded、onLoadError、onLoadProgress)に、IAvatarBuildObserverはAvatarBuildNotifier.onAvatarBuiltに置き換えられました。どちらのインターフェースも通知を受け取るためだけに存在していたのに、Service<T>のロケーターレジストリにあったため、すべてのオブザーバーは 4 つの VRM コールバックをすべて(たいてい空のスタブとして)実装しなければならず、Service<T>の利用箇所から、登録されたオブジェクトが検索対象なのか単なるリスナーなのかが見えませんでした。イベントは通知ごとに購読でき、コールバック内からの購読解除も言語の保証で許され、Service<T>は実際の検索のために残ります。いずれかのインターフェースの実装者には破壊的変更です:代わりにイベントを購読してください(AvatarBuildNotifier.BuildAndNotifyの呼び出し側は影響を受けません)。 /live/snapshot/imageもなくなり、スナップショットのスクリーンショットは同じ/live/asset/{key}/@imageから取得します。 スナップショットファイルはプロジェクトのアセットの種類の 1 つなので、その画像はアセットの画像です——別のキー(アセット参照ではなくスナップショットの 名前)で、独自のフォルダを読む 2 つ目の画像ルートを持つ理由はありませんでした。AssetBaseに、プレビューがアセットの中に詰め込まれたものではなく、アセットの隣にある素のファイルである種類のためのthumbnailFilePathが加わりました。SnapshotAssetは自身の*.snapshot.pngを返し、プロバイダーはそれをディスクから直接提供します(意図的にThumbnailCacheを通しません——そのファイルが既にキャッシュ そのもの です)。SnapshotInfoに、スナップショットのプロジェクト相対のアセット参照であるreferenceが加わり、クライアントは今それをidとして渡します。得られるうれしい副作用:プロジェクト ページに一覧されるスナップショットにもスクリーンショットが表示されるようになりました(以前は専用のスナップショットページだけでした)。破壊的変更で、互換用の仕組みはありません。/live/avatar/imageはなくなり、アセットのサムネイルは RemoteControl の/live/asset/{key}/@imageで提供されます。 このエンドポイントは、プロップとセットにプレビューが付いた日からあらゆる種類のアセットを受け付けていました——パスだけがまだ「avatar」と言っており、UE 版ではその語に意味は無いと説明するコメントを持たなければなりませんでした。アセットの画像を提供するのはアバターの仕事ではなく、LiveStudio の仕事ですらありません:それは RemoteControl の/live/asset/{key}と/live/assetsの隣に属します。そのためAvatarImageHandlerはAssetThumbnailProviderに置き換えられ、同じ検索(外部アセット →ThumbnailCache、初回リクエストでの VRM/glb 抽出付き)をAssetRegistry.SetThumbnailProviderを通じて登録します。パスと、キーが無い場合に 404 ではなく 400 を返すことを除き、動作は変わりません。破壊的変更で、互換用の仕組みはありません——リモートアプリも一緒に移行します。- RemoteControl の名前空間の移動に従い、このパッケージが提供するすべてのルートは
/api/*ではなく/live/*の下になりました。jp.lilium.livestudioは VirgoMotion 以外のアプリケーションにも組み込まれることを想定した汎用の基盤なので、同じ理由が当てはまります:camera、assets、avatar、expressions、commandsはホストアプリケーションが自分で使いたい名前で、このパッケージはホストのルートテーブルを共有しています。/api/camera→/live/camera(/switchと/imageも)、/api/vrm/loadと/api/vrm/reset→/live/vrm/*、/api/avatar/image→/live/avatar/image、/api/expressions→/live/expressions、/api/assets→/live/assets、/api/snapshot/image→/live/snapshot/image、/api/manipulator/*→/live/manipulator/*、/api/input-actionsと/api/input-actions/bind→/live/input-actionsと/live/input-actions/bind、/api/commands→/live/commands。レスポンス本文とクエリパラメーターは変わらず、接頭辞だけが移ります。破壊的変更で、互換用の仕組みはありません。 ICameraControllerはCameraControllerBaseに改名されました:これは常にabstract classでありインターフェースではなく、I接頭辞は誤解を招いていました——新しい名前はパッケージのほかの抽象基底(AssetBase、OperationBase、MotionSourceBase)に合わせています。シリアライズされたデータには影響しません:[SerializeReference]とライブの@typeはどちらも具象のコントローラー型だけを記録し、基底がインスタンス化されることはありません。- スナップショットはプロジェクトのアセットの種類の 1 つになったため、
*.snapshot.jsonファイルは、既に一覧されていたライブシーンやデッキと並んでプロジェクトページに一覧されます——Restoreボタンと通常の削除付きです(削除は*.snapshot.pngのサムネイルも一緒に消します。エントリがどのファイルを所有するかは、今ではAssetBase.DeleteFilesを通じた種類ごとの責務です)。スナップショットは、アプリ自身が作るファイルの中で、プロジェクトの一覧から見えない唯一のものでした:SnapshotManagerはSnapshotsフォルダを自分でスキャンし、AssetTypeRegistryを通していませんでした。専用のスナップショットページは引き続き持ち——サムネイル、撮影時刻順の並び、撮影/復元/削除の流れは変わりません——さらにスナップショットが現れたり消えたりしたときに再クロールを要求するようになったため、両方の表示が一致します。 - リモートアプリのツールバーボタンは Remote Control の共有ツールバーホストに置かれ、色合いもそこから取るため、新しい Remote Control サーバートグルと 1 組として読めます:2 つのアセンブリがどの順で初期化されても、リモートアプリボタンはサーバートグルのすぐ右に置かれます。ボタン自体は変わりません。
- パッケージのアセット作成メニューを 1 つの Live Studio サブメニューにまとめました。
Assets ▸ Createは 1 つのパッケージのために 3 つのルート——Live Studio(VRM Avatar Setup Settings)、LiveStudio(AvatarExpressionConfig)、Lilium Live Studio(Bundle Thumbnail)——を並べており、無関係な 3 つのパッケージのように読めていました。外れていた 2 つはLive Studioの下に置かれ、AvatarExpressionConfigは兄弟に合わせてAvatar Expression Configと綴られます。メニューパスはコマンドの表示場所にしか影響せず、既存のアセットとそのシリアライズされた型は変わりません。 - RemoteControl の Exposed → Live の改名に従い、
ExposedCamera/ExposedLightはLiveCamera/LiveLightになりました。シリアライズされる@type名はクラス名とともに変わるため、どちらも[FormerlyNamedAs]を持ち、旧名で保存したシーンも復元され(新しい名前で保存し直され)ます。ファクトリーはLiveCameraFactory/LiveLightFactoryになり、Unity でシリアライズされた参照は[MovedFrom]でカバーされ、ランタイムのフォルダExposedType/はLiveType/になりました。 - 表情ウェイトがフェイストラッキングに依存しなくなりました。
VRM1Avatar.Updateは、ボディドライバーがトラッキングなしを報告するたびに早期リターンしており、IExpressionResolver.Resolveをまるごとスキップしていました——そのためsmoothedOutputsが更新されず、すべての表情が 0 と読まれていました。ゲームパッドのバインドやリモートアプリから表情を操作するオペレーターは、アバターがベースポーズで画面に出ている間、何も得られませんでした。Resolve と表情の適用は、3 つのアバターバックエンド——VRM1Avatar、VRCAvatar、VRCFTAvatar——のすべてで毎フレーム実行されるようになりました(未トラッキング時はニュートラルな ARKit ウェイトで)。トラッキング入力 そのもの であるものはすべて、引き続きトラッキングで制限されます:瞬き、視線、口形素(viseme)、ARKit 由来の表情、フェイストラッキングのパラメーター書き込みです。それらにニュートラルな値を適用すると、未トラッキング時にアバターが引き渡すベースポーズのアニメーションや FX クリップと争うためです。 - アニメーションバンドルは汎用の アセットパック になりました:
*.anim.lsbは*.pack.lsbになり、パックはAnimationClipだけでなくあらゆる型のアセットを保持できます。種類を示す拡張子は変わりません——*.set.lsb、*.avatar.lsb、*.prop.lsbはそれぞれ中身を名乗っており、それらを改名すると、中身を開かずに分類できる唯一の手段が失われます——そのため、パックは何でも入る枠を引き受けます:その名前は意図的に中身について何も言いません。したがってPackBundleLoader(以前のAnimationBundleLoader)はメンバーを型なしで読み込み、それぞれをfile:<path>#<assetName>キーで登録し、各メンバーの実行時の型がどのセレクターに提供されるかを決めます。GET /live/assets?type=は既に型名でレジストリを絞り込んでいるため、新しい選択可能なアセット型にここでの変更は要りません。パックの名前がもう中身を示さないため、そのエンドポイントはクリップ以外の型について事前ウォームを省けなくなり、セッションごとにすべてのパックを 1 度開くようになりました(既存のファイルごとのキャッシュにより 1 回限りのコストに保たれます)。既存の*.anim.lsbファイルは旧来の入力として引き続き機能します——拡張子は登録されたままなので、ファイルもそれに対して保存されたfile:参照も触る必要はありません——そしてエクスポートメニューはAssets/Lilium Live Studio/Export Asset Pack (.pack.lsb)になり、選択した任意のメインアセット(フォルダとシーンを除く)を受け付け、パック全体での一意名の要件は型をまたいで適用されるようになりました。取り込んだパックはAnimations/ではなくPacks/に置かれます。プロジェクトのクロールは再帰的なので、既にAnimations/にあるファイルも見つかります。AnimationBundleAssetはPackBundleAssetに改名され、[FormerlyNamedAs("AnimationBundleAsset")]を持つため旧@typeで保存したシーンも復元されます。ExternalAssetKey.BuildClipKey/TryParseClipKeyはBuildMemberKey/TryParseMemberKeyに改名されました(キーの形式自体は変わりません)。パックのメンバーの一覧は、今ではGET /live/assetsの 2 つ目の絞り込み軸(下記参照)で、型にかかわらずすべてのメンバーを返します。 GET /live/animation/clipsはなくなり、1 つのパックのメンバーの一覧はGET /live/assets?pack=<assetId>になりました。 2 つのエンドポイントは、異なる名前で同じ仕事をするようになっていました:どちらも「このアセット参照スロットに何を書き込めるか」に答え、どちらも同じ方法で組み立てたfile:<path>#<name>キーを返し、どちらも一覧の副作用としてPackBundleLoaderでパックを開きます。実際に異なっていたのは絞り込みの軸——型か、含むパックか——と、それを包むレスポンスの形だけでした。/live/assetsは今では両方を受け取り、組み合わせられます:?pack=X&type=AnimationClipは X の中のクリップだけを一覧します。応答はどちらでも通常の{ "type", "assets": [{ "key", "name", "type" }, ...] }なので、メンバーは実行時の型付きで届きます(以前のclips配列は名前とキーしか返しませんでした)——中身がもう構造上クリップではない汎用のパックには、それが必要です。パックへの掘り下げは以前と同じく軽量です:?pack=はその 1 つのパックだけを開いてカタログ全体の事前ウォームを完全にスキップし、キーは組み立て直すのではなくAssetRegistryから読み戻すため、同じメンバーはどちらの軸でもバイト単位で同一に一覧されます。未知のpackid やパックでない id は 404 で、一致しないtypeは引き続き単に空のカタログです。破壊的変更で、互換用の仕組みはありません:まだ/live/animation/clipsを呼ぶリモートアプリのビルドは、パックの展開で 404 を受け取ります。PackBundleAsset.GetMemberNamesAsync()は、メンバーそのものを返すGetMembersAsync()に置き換えられました。
Removed
/live/camera系のルート——GET /live/camera、POST /live/camera、POST /live/camera/switch、GET /live/camera/image——はすべてなくなり、カメラにはほかのものと同じく汎用のライブオブジェクトの面から到達します。 カメラはずっと前から普通のライブオブジェクト(LiveCamera)なので、一覧するのはGET /live/object、ライブに切り替えるのはそのSwitchライブ関数です——リモートアプリは既にオブジェクト一覧からカメラページを描き、レンズとコントローラーの値を素のプロパティとして書いており、一覧ルートの応答は誰も読まない状態に取得されていました。POST /live/cameraには呼び出し元がまったくありませんでした:3 つのアクションのうち何かをしていたのはswitchだけで、2 つのストリーミング用アクションはブロードキャスト本体がコメントアウトされたフラグを切り替えるだけで、UE 版はそもそもこのルートを実装していませんでした。これらを削除すると、ハンドラーのハートビートも丸ごとなくなります——変更されたライブカメラの 100 ms ポーリング、それがプッシュしていたcamera_updateブロードキャスト、camera_imageイベント——変更フィードが同じことを汎用的に報告するためです:LiveCamera.priorityのセッターは実際の遷移をLiveChangeLogに記録するようになったため、切り替え(REST 経路からは見えない直接の C# 書き込みで起きます)は、影響を受ける 2 台のカメラだけを取り直し対象にします。UE 版が既に使っていたのと同じパターンです。これが無いと、切り替え後にクライアントの LIVE バッジが固まっていました。本当に JSON に乗せられない唯一のものであるプレビュー画像は、ルートではなく 画像メンバー になります:LiveCamera.previewは RemoteControl の新しいLiveImageDataを返す[LiveProperty, ImagePreview]のゲッターなので、GET /live/object/{cameraId}/previewは PNG を返し、JSON の読み取りはそのアドレスを値として運びます(RemoteControl のエントリを参照)。CameraApiHandlerは丸ごと削除され、マニピュレーターがこのパッケージの唯一の専用 REST として残ります。汎用の面を完成させるためにプロパティが 2 つ加わります:LiveCamera.isLiveとaspectが公開されるようになりました(読み取り専用、永続化なし。読むたびに Cinemachine カメラから導出されるため)。最初から両方を持っていた UE のプロキシに合わせたもので、クライアントはこれらから LIVE バッジを描きプレビューの大きさを決めます——Unity 相手では以前はできませんでした。破壊的変更で、互換用の仕組みはありません。GET /live/input-actionsとPOST /live/input-actions/bindはなくなり、入力マップはほかのものと同じくライブオブジェクトになりました。 どちらのルートにも呼び出し元が残っていませんでした——リモートアプリの入力ページはある時点で削除され、翻訳文字列だけが残っていました——一方でオブジェクト側は既に半分できていました:AvatarInputはずっとライブクラスInputActionsで、deviceName、actionNames、すべてのバインドのsettingsスナップショットを公開していました。欠けていたのはアクションごとのハンドルで、そのためにactionsが加わります。これはライブのマップから組み立て直される読み取り専用の配列で、その要素はアクションのnameを[LiveKey]として、バインドされたキーを人が読めるbindingとして、そしてenabledを持ちます。再バインドはその要素のRebindライブ関数で、表情やオペレーションの再バインドと同じRuntimeKeyBindingSystemの経路を実行します。待ち受けが始まるとすぐに戻り、捕まえたキーはbindingに現れます。そのため、アクションは専用 DTO へのリストのインデックスではなく名前でアドレスされ(actions[ToggleCamera].binding)、クライアントは汎用のオブジェクトの面から一覧と再バインドボタンを描画します。InputActionsApiHandlerとその 4 つの DTO も一緒に削除されました。破壊的変更で、互換用の仕組みはありません。POST /live/vrm/loadとPOST /live/vrm/resetはなくなり、アバターの読み込みと解除は汎用のライブオブジェクトの面から行います。 どちらも既にすべての呼び出し元を失っていました。アバターファイルを選ぶことは、それをExternalAssetManagerに登録して有効化すること(アバターは排他的なので、1 つを有効にするとほかは解除されます)で、モデルのパスを持つコンポーネント——GltfModel.pathなど——は、プロパティが書き込まれるとそのセッターから読み込みます。リモートアプリのファイルセレクターはしばらく前にそちらへ移っており、プロパティを書くだけです。これらのルートをまだ呼んでいた 2 つの関数は、フックからエクスポートされていたものの、どこからも呼ばれていませんでした。ルートがプロパティ書き込みに対して提供していたのは進捗で、その部分は残ります:VrmLoadApiHandlerはVrmLoadNotifierになり、VRMLoaderの素のリスナーとして同じvrm_load_start/vrm_load_progress/vrm_load_complete/vrm_load_errorイベントを同じペイロードで受信箱にプッシュし、読み込み中に接続したクライアントにも引き続き伝えます。進捗は、リモートから開始されたものだけでなく すべての 読み込みを報告するようになりました——以前は REST 経路だけが設定するクライアント id で制限されていたため、アプリ自身の UI から開始した読み込みは進捗イベントを黙って飛ばしていました。破壊的変更で、互換用の仕組みはありません。GETとPOST /live/expressionsはなくなり、表情にはほかのものと同じく汎用のライブオブジェクトの面から到達します。 ルートの両方の半分には既にそちらに厳密な同等物があり、リモートアプリもそちらへ移っていました:表情の一覧はライブ関数POST /live/function/{avatarId}/getavailableexpressions、ウェイトは通常のライブプロパティGET/PUT /live/object/{avatarId}/expressions[<name>]/weight(AvatarController.ExpressionEntry。そのnameが[LiveKey])です。実のところGETは 2 つのうち劣る方で、weightをハードコードされた0、displayNameをnameのコピーとして報告していました——ライブのウェイトがこのルートから流れたことは一度もありません。ルートが提供していたのは、解決すべきオブジェクト id なしに、表情名だけでアクティブなアバターをアドレスできることでした。それが失われる唯一のものですが、使っているクライアントはありませんでした。ExpressionsApiHandlerとその 4 つの DTO も一緒に削除されました。破壊的変更で、互換用の仕組みはありません。ExpressionServiceは変わりません——AvatarController.expressionsはそれを通して読みます。POST /live/commandsはなくなりました。 これは既に[Obsolete]で、代替としてライブ関数(POST /live/function/{id}/{path})が示されており、実際にはもう到達できなくなっていました:これが実装していた唯一のコマンドはinput_actionで、その本体は押下をシミュレートすることすらなく——アクションを有効化または無効化して TODO を残すだけでした——呼ぶものもありませんでした。リモートアプリ側でも、それに供給していた連鎖全体が死んでいました:sendCommand→sendMessage→ どのページにも渡されていないcommonProps.sendMessage、さらにどのコンポーネントもインポートしていないstartCalibrationを持つuseHomePageフック(キャリブレーションはこれではなく Fusion のエンドポイントです)。これらはすべてルートとともに削除されました。POST /live/commands/quitとPOST /live/commands/resetは 影響を受けません——これらはこのハンドラーではなく、RemoteControl のQuitApiHandler/ResetApiHandlerが提供する別のルートで、どちらも使われています。FrameRecorderProjectはLiveDataRecorderProjectに改名されました。jp.lilium.remotecontrolでのFrameRecorderControllerからLiveDataRecorderへの改名に従うものです。ソースには破壊的変更です。
Fixed
- アプリの実行中に開いたシーンで、外部プロップが 1 つも立ち上がっていませんでした。
*.prop.lsbのプロップインスタンスは、このマシンのカタログがそのファイルを知って初めて解決できる@prefabキーから復元されるため、復元はまだ作れないものをキューに入れ、誰かがそれを取りに戻ってくる必要があります。それをしていたのはプロジェクトのクロールだけでした——そしてシーンを開いた後にクロールは走らないため、プロップは起動時には現れる一方、同じシーンをリモートアプリから開くと再起動するまで欠けていました。プロップが失われたことはありません(排出されなかったエントリはそのまま保存し直されます)が、なぜステージが空なのかを示すものは何もありませんでした。ExternalAssetManagerはLiveSceneSerializer.onLiveSceneRestoredでも排出するようになりました。これはカタログが再構築されたときだけでなく、シーンが読まれるたびに発行されます。この穴は、カタログが保存されなくなったときに開きました:そのデシリアライズのコールバックが排出へのもう 1 つの経路で、カタログと一緒になくなったのです。 - シーンページの「+」から生成したアバターアイテムが、アニメーターパラメーターも表情もまったく動かしていませんでした。
AvatarItemは親をたどってアバターを探していたため、アイテムがアバターの子である間しか見つけられませんが、「+」から追加したアイテムはベースシーンに単独で立っています。ソケットへの追従は動いていたので、アイテムは正しい位置にありながら、見るからに動いていませんでした。今はPropAttachmentのソケット探索やAvatarChairが既にしていたのと同じくアバターサービスに問い合わせるため、どこにあっても動きます。解決済みの参照も、アバターの差し替えをまたいで正しく解決し直されるようになりました:参照はインターフェースとして保持されており、Unity の破棄済みオブジェクトの==が効かないため、差し替えで外れたアバターのコンポーネントが、セッションの残りのあいだ存在しているように見えていました。 - 保存済みシーンがもう認識しない組み込みエントリは、シーンに居座らずに破棄されるようになりました。 組み込みアセットには id を組み立て直すためのファイルパスが無いため、同一性を解決できないエントリ——プロジェクトが宣言をやめた組み込みセットや、同一性が別の形だったときに保存されたもの——は空の id で戻ってきていました:決して読み込めず(差分は空の id をスキップします)、id をキーにした重複排除をすり抜け、保存のたびにそのまま書き戻されていました。
ExternalAssetManagerは、復元が着地した時点でそうしたエントリを、それを名指す警告とともに破棄するようになりました。失われるものはありません——まだ宣言されているすべての組み込みは直後に注入し直されます。 - エディタで改名・移動した組み込みセットが、それを読み込んだ状態で保存したシーンにも反映されるようになりました。 重複排除は復元されたエントリを残す(それが有効状態を運ぶため)ので、保存時の名前とパスも運んでいました:改名したセットはセッションの間古い名前のままで、シーンが移動したセットはもう読み込めないパスを保持していました。表示用のフィールドは、カタログが注入されるときに宣言から読み直されるようになりました。
- バンドルから生成したプロップのインスタンスが、リプレイで戻ってくるようになりました。以前は黙って戻りませんでした。 テイクはインスタンスの作成元のキーを運び、それを再構築するメーカーは、既に手元にあるプレハブにしか答えられません——Resources から同期的に読む組み込みプロップには当てはまりますが、キーがプロジェクト相対パスで、プレハブを先にバンドルから読み出さなければならない外部の
*.prop.lsbのプロップには当てはまりません。その読み出しを行うものが無かったため、インスタンスはリプレイの毎フレームで未解決として数えられ、どこにも報告されていませんでした。PropInstanceRecipeResolverが 2 つ目のレシピリゾルバーとして置かれ(jp.lilium.remotecontrolのリゾルバーチェーンを参照)、1 つのことだけを行います:読み出しを開始し、結果をPrefabRegistryに入れます。それにより、これらの再構築方法を既に知っていたメーカーの手がキーに届くようになります。意図的に オブジェクトを作成することはありません——作成は、自分がいるフレームの一覧を見ている調整処理の仕事なので、読み出し中にスクラブで消えたインスタンスは、先へ進んだワールドに遅れて現れるのではなく、単に作られません。読み出しの間、リプレイはインスタンスなしで進むのではなく保留されます(FrameGate.HoldSupply、アセット読み込みと同じ待ち):バンドルにかかる時間はこのマシンの性質であって、テイクの性質ではないためです。何も返さなかった読み出し——ローダーがエラーとして報告する、消えたバンドルファイル——は記憶され、カタログが変わるまで再試行されません。そうしないと、壊れたプロップ 1 つで、フレームの先頭ごとにコンソールが同じ行で埋まってしまいます。「+」もプロップを生成するときにそのプレハブを登録するため、同じセッション内のリプレイはバンドルを再び開かずに解決します。ExternalAssetManagerは「このキーはどのプレハブか」への単一の答えを保ち、遅延したライブシーンの復元とリプレイの両方がそれを通ります。 - リモートアプリで選択中と表示されるアバターが、切り替え後に一瞬前のアバターになることがありました:
AvatarSelection.GetSelectedNameはリスト順で最初の有効なアバターを返しており、調整中は一瞬両方が有効になるためです。マネージャー自身の選択(selectedExclusive)を読むようになったため、これから読み込まれるものと一致します。 LiveCameraのプレビューキャプチャーが、RenderTexture がまだカメラのターゲットテクスチャである間にそれを破棄しなくなりました(Unity が警告を出していました)。先にカメラから外します。- アバターのメッシュが 1 つのライト/リフレクションプローブのアンカーを共有するようになり、部位ごとにライティングが異なることがなくなりました。 各レンダラーはこれまで自身のバウンズの中心でプローブをサンプリングしていました——VRM 1.0 の読み込み経路にも、
.lsavatarにも、組み込みプレハブにもRenderer.probeAnchorを設定するものが無かったためです——そのため髪、体、服が目に見えて異なる環境光を拾うことがありました。AvatarControllerは、アバター以下のすべてのSkinnedMeshRenderer/MeshRenderer(非アクティブなものを含む)を Chest ボーンに向けるようになり、リグに無い場合は Spine、次に Hips にフォールバックします。プレハブでアンカーが既に設定されているレンダラーは、作者の設定のまま残ります。
[0.25.3] - 2026-07-22
Added
- ライブプロジェクトを開くときに、未保存のライブシーンの編集を破棄してよいか確認するようになりました(Save / Don't Save / Cancel)。以前は黙って破棄していました。プロンプトは
RemoteConfirmSystemを通じて上げられるため、マシン上と、そのライブプロジェクトを要求したリモートアプリの両方に表示されます。「名前を付けて保存」をキャンセルした場合は、ユーザーが残すと選んだ編集を破棄せず、開く操作自体を取りやめます。
Fixed
- ライブプロジェクトを開いたあとのアプリの状態が、そのライブプロジェクトで起動した場合と同じになりました。以前は、直前に開いていたライブプロジェクトが持ち越されていました。
OpenProjectは通常のライブシーン読み込みを再利用しており、これは読み込むシーンが別のベースシーンを指す場合にしかベース Unity シーンを再読み込みしません。そのためベースシーンを共有する 2 つのライブプロジェクト間では再読み込みが完全にスキップされ、前のライブプロジェクトで読み込まれたプロップ/アバターがそのまま残り、読み込む(差分)シーンファイルが省略した値もすべて維持されていました。ライブプロジェクト単位の設定はさらに悪く、そもそもシーンファイルに含まれず加算的に適用されるため、新しいライブプロジェクトが記録していない設定は前のライブプロジェクトの値を保持していました。LiveSceneManager.OpenProjectSceneは、復元の前にすべての公開オブジェクトを取得済みの既定値へリセットし、ベースシーンの再読み込みを強制するようになりました。ライブプロジェクトフォルダのクロールもリセットの後ろへ移したため、前のライブプロジェクトで有効化・読み込み済みだったカタログのエントリ——クロールは意図的にこれらを削除しません——が切り替え後に残ることもなくなりました。
Changed
- 組み込みアセットカタログが
AnimationClip専用ではなくなりました。Resourcesフォルダから焼き出すアセット型はBuiltinAssetTypeRegistry(AssetTypeRegistryの組み込み版)で決まります。各 kind が、担当する Unity のアセット型と、一覧に出すAssetBaseのエントリを宣言するため、パッケージはベイカーやランタイムのレジストリに触れずに追加できます。BuiltinAssetCatalogは型ごとの配列ではなく、所属 kind を名乗るエントリの単一リストを持ちます。BuiltinAssetCatalogBuilderは登録済みの kind ごとに 1 回走査し、ファイル拡張子ではなくアセット型でインポートを分類し(kind が自分の型の取りうる拡張子を列挙せずに済みます)、エディタ読み込みごとに 1 回再構築します——新しく登録された kind を拾うのはこの再構築です(kind を知らせるアセットインポートは発生しないため)。BuiltinAnimationAssetの参照リソースとしての挙動(読み込み/解放のトグルなし、削除されない、永続化されない)は共有のBuiltinAssetBaseへ移り、各 kind は自身の公開@typeを持ちます。既存の焼き込み済みカタログは、次のエディタ読み込み時に新しい形へ書き換えられます。
[0.25.2] - 2026-07-21
Fixed
ChildProcessHost経由で起動した子プロセスを、kill-on-close のジョブオブジェクトでホストの寿命に紐付けるようにしました。従来はすべての正常停止の経路がマネージドのクリーンアップから走っていたため、巻き戻し(アンワインド)せずに死んだホスト(クラッシュ、タスクの終了、終了ウォッチドッグ)は、フュージョンとリモートアプリを孤児化させ、それらがポートを掴んだまま/ビルド出力をロックしたまま残っていました。StartChildApplicationも、プロセスの起動に失敗したときは、そのまま使い続けずに早期リターンするようになりました。ExpressionEntry.weightが、配列差分の生成する空名のテンプレートインスタンスで例外を投げなくなりました。FacialKeyは空のカスタム名を拒否し、駆動すべき expression スロットも無いため、このプロパティは0を返し、書き込みを無視します。この例外はwantsToQuitを抜けてしまい、Unity は未保存チェックが一度も走らないまま終了を通していました——これは一見、終了が成功したように見えますが、そうではありません。
Changed
- 描画品質の設定を、静的な
LiveSceneManagerクラスから、シリアライズ可能なIExposedObject(StageManagerと同様)であるRenderQualityへ移しました。リモートアプリの設定ページに品質セクションを表示するかどうかは、登録によって決まるようになりました。設定のNavigatePageはRenderQualityの id を列挙し、NavigateObjectSelectorは何にも解決しない id をスキップするため、アプリはRemoteControlContainer(またはホストのRemoteControlBehaviour)のオブジェクト一覧にRenderQualityを置くことで品質コントロールを露出し、インスタンスを外せば隠せます。Live Studio Systemプレハブは既定で 1 つ登録します。LiveSceneManager.quality/.qualityNames/.currentQualityIndex/.SetQualityは削除され、永続化されるレベルはLiveSceneManager.settings.jsonからRenderQuality.settings.jsonへ移ります(レベルは一度だけライブプロジェクト既定にリセットされます)。
[0.25.0] - 2026-07-17
Added
AddSwitchAvatarOperationが、アクティブなアバターを切り替えるオペレーションセットを作成します。デッキタイルの全面背景に、そのアバターのサムネイルが設定されます。DeckControlにbackgroundAssetIdが追加され、可搬なライブプロジェクト相対参照として保存される(AvatarImageHandlerが解決)ため、保存したデッキはライブプロジェクトを移動したり別のマシンで開いても維持されます。InvokeFunctionOperationが、引数を取る公開関数やネストした関数を呼び出せるようになりました。ターゲット上の無引数関数だけでなく、位置引数を JSON 配列(argsJson)として、任意のpropertyPath(例:StageManagerのセット要素のWarpTo)とともに保存します。AddFunctionOperationに末尾の任意引数argsJson/propertyPathが追加されましたが、既存の無引数オーバーロードはソース互換のままです。VRCAvatarが、VRCFTAvatarと同様にAvatarBodyDriver(PlayableGraph)を通じて body アニメーション・トラッキングのゲーティング・メッシュの表示・下半身ロックを駆動するようになりました。これにより VRChat アバターでも、未トラッキング部位の body override クリップと下半身のポーズロックが使えます。ビセーム/音声と表情のパラメータは、ラップしたコントローラーの playable を通じて読み書きされます。- 組み込みアニメーションアセットのカタログを追加しました。
Resourcesに同梱されたAnimationClipはBuiltinAssetCatalog(Editor のBuiltinAssetCatalogBuilder)に焼き込まれ、BuiltinAnimationAssetリソースとして公開されます。BuiltinAssetRegistryが各クリップを GUID でAssetRegistryに、先行して冪等に登録するため(再生開始時・エディタ読み込み時・オンデマンド)、永続化された body override の参照がシーン復元時に同期的に解決されます。カタログのエントリはライブプロジェクトのアセット一覧に注入されisBuiltinが付くため、ExternalAssetManagerがそれらを削除・永続化することはなく、除去は拒否されます。 AvatarControllerの body override スロットが、インスペクタの_bodyOverrideClipPresets配列(削除されました)ではなくGET /api/assets?type=AnimationClip(Resourcesの組み込みクリップと外部の*.anim.lsbバンドル)から候補を取得するようになりました。VRCFTAvatarが_bodyOverrideClipをシリアライズフィールドとして公開し、Initialize/OnValidateで転送するようになりました。AvatarExpressionConfig.syncBlinkは、左右の瞬きウェイトを小さい方に揃えます。アバターによっては左右の目で瞬きウェイトの精度が非対称で、瞬きが左右で偏ることがあります。これを有効にすると左右対称に瞬きします。LiveStudioOrbitalFollow: 単一のターゲットを球面上で周回する Body ステージの Cinemachine コンポーネントです(yaw/pitch/distance、位置ダンピングとローカルフレームのターゲットオフセット付き)。カメラ自身の親フレームで解決されるため、カメラの親を回転させると周回リグ全体が回転します。OrbitalFollowCameraControllerはこれを通じて位置決めし、位置と注視の両方に単一のトラッキングターゲットを使うようになりました。- Studio Template のシーンに、最初から
OrbitalFollowカメラが含まれるようになりました。
Changed
- オペレーション駆動のプロパティ経路から、毎フレームの GC とヒッチを排除しました。
SetPropertyOperationは解決済みのExposedPropertyをキャッシュし(型付き読み取りに失敗したら自己修復)、毎フレームのbool/floatの書き込みに、FindPropertyを毎フレーム再走査して要素パスやコンポーネント配列を確保する代わりに、ボックス化しない型付きアクセサを使います。AvatarControllerは expressions 配列をキャッシュし(アバター/AvatarItemの変更で無効化)、キーつき配列パスの解決時の O(N^2) の GC を回避します。またInvalidateExpressionsからレジストリのキーつきコレクションの世代を進め、expression の要素が再構築された後にキャッシュ済みの解決が再解決されるようにして(古いキーへの書き込みを防止)、OnPropertyChangedを、T ポーズ/ソケットの再構築を毎フレーム走らせて数ミリ秒のヒッチを起こしていた_PostSetupAvatar全体の代わりに、変更されたプロパティだけを再適用するように絞り込みました。カスタムのFacialKeyのハッシュ計算もアロケーションフリーになりました。 - Fusion の UI Definition が、キャプチャーページをライセンス設定より前に並べるようになりました。
AvatarControllerが作成するアバター装着ソケットにS_プレフィックスを付けるようにしました(S_Hips/S_Spine/S_Chest/S_Head/S_Neck/S_WristLeft/S_WristRight)。同名のボーンがソケットを隠せなくなります。名前ベースの参照も更新しました(PropAttachmentの既定ソケット、bone-follower / look-at / orbital-camera のS_Headターゲット)。旧いソケット名を参照していたプロップやカメラは選び直しが必要です。
Fixed
- 下半身ロックが、body override クリップの「Root Transform Position (Y)」オフセット(ヒューマノイドのマッスルクリップの
level設定)を尊重するようになりました。AvatarBodyDriverは、applyRootMotionを無効にしたままAnimationClip.SampleAnimationでロックした腰の位置と足 IK ゴールを焼き込んでおり、このオフセットが暗黙に落ちていました。そのため Y オフセット付き(腰を沈めるため)で作った座りクリップは、オフセットなしの高さでロックされて浮いていました(オフセットが 0 のクリップは問題なく見えていました)。ロックのオフセットと足ゴールは、ルートモーションを有効にして取り直すようになり、クリップの Y オフセットが適用されます。ロック経路外で使う基本ポーズは変更ありません。 - モーメンタリなデッキボタンが、素早いタップでときどきアクションを取りこぼさなくなりました。押下と解放は 2 つの REST 呼び出しとして届きますが、両方が 1 回の
Updateフレーム内に収まると、マネージャがheld=trueを一度も観測できず、解放トリガーが黙って失われていました。SetHeldは立ち上がりエッジで解放パルスをラッチするようになり、ちょうど 1 回だけ発火します。 - 下半身ロックが、腰の親に回転を持つリグ(X=-90 でインポートされた Blender の Z-up アーマチュアなど)で頭を左右に振らなくなりました。ルートのヨー補償を腰のローカル回転に前置していたため、ワールドのヨー差分がロールに化けていました。現在はリグの親チェーンに依存せず、腰のワールド回転にワールド空間の差分として適用されます。
- 下半身ロックが、
humanScaleが 1 でないアバターで、膝軸が縮退したまま脚を完全に伸ばしきらなくなりました。足の IK ゴールは生のルートローカル位置として取得される一方、ロックされた腰は humanScale で正規化されていたため、腰から足までの距離が変わりクリップの脚ポーズが到達不能になっていました。ゴールは現在、サンプリングした腰からの相対で取得され、正規化されたロック腰のオフセットに再ベース化されます。 - 下半身ロックの高さがアバターの身長でずれなくなりました。
AvatarBodyDriverは、humanScale で正規化したルート相対のオフセットをワールド空間で適用して、腰を override クリップの腰位置にロックします。 QuitTerminationGuardが、終了時にマネージドのウォッチドッグスレッドではなく、切り離したウォッチドッグプロセスを起動するようになりました。マネージドスレッドは、遅れて起きるネイティブのテアダウン(WGI)のハングより先に Mono ランタイムとともに死んでしまい、Player ビルドの Fusion が終了後も残留していました。テアダウン中に自身がデッドロックしうる発火時刻のログは削除されました。LookAtCameraControllerが、Cinemachine 3 でも再び自前で LookAt ターゲットを駆動するようになりました。CustomLookAtTargetを設定して、注視が(トラッキングターゲットへ暗黙にフォールバックするのではなく)割り当てた look-at ターゲットを使うようにし(Cinemachine 2 と同等)、破棄時にフラグをクリアします。
[0.24.3] - 2026-07-09
Added
AvatarControllerが、キャプチャーが追跡していない部位を override のAnimationClipで駆動できるようになりました。クリップはインスペクタのプリセットか、外部のアニメーションバンドル(*.anim.lsb。複数のクリップを同梱します)から、統一されたアセットキー(焼き込みアセットは素の GUID、外部はfile:<path>#<clip>)で選びます。キーは遅延解決され、シーン復元時と REST の書き込み後に再適用されるため、バンドルの非同期読み込みをまたいで選択が維持されます。VRM1 / VRCFT の再生に対応します。- 「下半身のポーズをロック」トグルを追加しました。腰とルートをキャプチャーのアンカーに足 IK つきでピン留めし、上半身はキャプチャーへの追従を続けたまま、下半身をその場に保持します。
- ライブプロジェクトごとのセッションログを追加しました。Unity のログが
{project}/.livestudio/logに、セッションごとに 1 ファイルとしてネイティブの Player.log と並んでミラーされます。ProjectPathsにGetLogDir/EnsureLogDirヘルパーが追加されました。
Changed
- オペレーションセットの手動ホールドと手動値が、シーンの再読み込みをまたいで永続化されるようになりました(従来はどちらもリセットされていました)。復元時にエッジのベースラインが復元後のホールドで初期化されるため、セットをオンのままにしたシーンを読み直しても、オペレーションを再発火させる立ち上がりエッジが作られることはありません。
Fixed
- 被写体がキャプチャーの視錐台から外れる際に、アバターのメッシュが点滅しなくなりました。アバタールートの valid バイトが body / face のトラッキングのビットマスク(body = MediaPipe、face = ARKit)になり、表示は非対称(両方の信号が落ちたときのみ非表示、face の立ち上がりエッジで表示)になったため、フレームごとの body の有効性の揺れがメッシュをちらつかせることはなくなりました。
[0.24.2] - 2026-07-05
Added
- エディタに、リモートアプリを起動/終了する ツールバーボタン を追加しました(ランタイムの
RemoteAppHostと同じ設定パス/引数・子プロセス管理で動作します)。 LookAtCameraController(ICameraController)を追加しました。TransformRef(ターゲット所有者名+ボーン名/パス)から解決したボーンを、カメラのLookAtターゲットに割り当てます。参照が変わったときやアバターを差し替えたときだけ再解決するため、アバター再読み込み後も LookAt 先のボーンが維持されます。変更するのはLookAtのみで、Trackingターゲットや Aim コンポーネントはそのまま残します。BoneFollowerコンポーネントを追加しました。TransformRefから解決したボーンに自身のトランスフォームを追従させ、アバター差し替え時に再解決します。LiveStudio のカメラパイプライン外(別のステージ/セットバンドルでオーサリングした Cinemachine カメラなど)でオーサリングしたカメラを、差し替え可能なアバターのボーンに橋渡しします。シーンをまたいだボーンへの直接参照が差し替え後に維持できないケースに対応します。AvatarControllerに、読み込んだアバターのアニメーションレイヤーをシーン単位で上書きできる機能を追加しました。ProjectManagerが、選択中/非選択のカメラプレビューのポーリング間隔設定(ライブプロジェクトスコープで永続化)を公開し、カメラプレビューを更新する頻度を制御できるようにしました。
Changed
- Value モードの Deck スライダーが、常に正規化した 0..1 の生値を書き込む代わりに、割り当て先プロパティの最小値/最大値の範囲にドラッグをマッピングするようになりました。これにより、たとえば 0..100 のプロパティにバインドしたスライダーで全域を駆動できます。正規化値は内部的に保持されるため、ゲージやエッジ検出には影響しません。
- 読み込み済みのプロップ/アバターオブジェクトを、ライブシーンのトップレベル差分を唯一のソースとして永続化するようにしました。所有アセットの読み込みが完了していない状態はキューされ、アセット到着時にバインドされます。セッション中に一度もバインドされなかったエントリは、警告して破棄する代わりに、読み込み→保存のサイクルをまたいでそのまま保持されます。
Fixed
- シーンやプロップバンドルに直接オーサリングしたオペレーションセットに、初期化・復元時に安定した ID をバックフィルするようにしました。以前は既定の空 ID のままだったため、ID で指定する関数がすべて無言の no-op になり、入力マップの再構築時にセットがスキップされていました。
- 同じ内部 AssetBundle CAB を共有する複数の単一プレハブ・プロップを同時に読み込むと衝突すること("same files already loaded")がありました。バンドルのオープンを直列化し、ビルドした各バンドルにソースアセット GUID をシードとした一意の内部 CAB を与えることで、プロップが CAB を共有しないようにしました。
- ライブシーンの再読み込み時に、読み込み済みプロップ/アバターのオーバーライド(オーサリング位置から動かしたプロップなど)が失われないようにしました。読み込み完了時の再ベースラインが、適用済みの値をダーティ検出のベースラインに畳み込む代わりに、復元で適用したオーバーライドを保持するようになりました。
[0.24.1] - 2026-06-30
Added
- アクションセットを Deck(プッシュボタン / トグル / スライダーのコントロールを並べたグリッド)に配置できるようになりました。リモートアプリから自由に配置・編集できます。コントロールの種類はコントロールの配置時に選択し、アクションセットの入力モードとは独立しています。
- Value モードのアクションセットに手動の値オーバーライドを追加しました。スライダーコントロールで 0..1 の値を直接駆動できます(Studio へはスロットリングして送信)。
Changed
- アクションの「パネル」概念を全面的に Deck へ改名し、アクションセットのふるまいを単一のコントロール軸で駆動するようにしました。Deck のタイル幅はコントロール種別ごとに宣言され、欠落した Deck パネルは自動的に再生成され、タイルレイアウトのフィールドは汎用リモートエディタから隠されます。
Fixed
- アバターが非アクティブな間でも表情ウェイトを読み取れるようになりました。
KeyInputSourceのコントロールパスをバインディングのシャドウフィールドとして保持するようにし、キーバインドが正しく永続化・解決されるようになりました。- プレイモード開始時にアクティブなライブシーンのパスを起動時状態から再解決するようにし、最後に開いていたシーンが確実に復元されるようになりました。
[0.24.0] - 2026-06-26
Added
- アクションセット/入力バインドシステム(
ActionManager/ActionSet/InputSource/ActionBase)を追加しました。リモートアプリから編集でき、キーボードやゲームパッドの入力を、順序付きのアクション列(表情ウェイトの設定、アバター/ステージの切り替え、GameObject の表示切り替え)にバインドできます。多態的な入力/アクションは[SerializeReference]+[TypeSelector]を使い、入力モードは Button / Toggle / Value です。Live Studio Systemプレハブに組み込まれています。 InvokeFunctionActionとSetPropertyActionにより、任意の[ExposedFunction]や bool/float コントロールの隣に汎用の「キーへバインド」機能を提供します(安定した id でアドレッシング)。機能ごとの個別バインドを置き換えます。- アクションセットの相互排他トグルグループ: 空でない
groupを共有し入力が Toggle モードのセットは、ラジオグループのように振る舞います(1 つを ON にすると他が OFF になり、すべて OFF も許容)。 SwitchStageActionはステージを丸ごと切り替えます(SwitchToSetByNameが他の読み込み済みセットを取り外し、対象を必要に応じて読み込み)。ステージページのSetActiveSetは従来どおりの非排他選択の挙動を維持します。- アバターが座る椅子プロップ用の
AvatarChairコンポーネントを追加しました。AvatarControllerの下に再ペアレントしてアバターのルートモーションで引きずられないようにし、各パーツを記録した静止ポーズを基準にアバターの骨盤(Hips ソケット)へ追従させます。swivel/reclineは対象トランスフォームを作成時のローカル軸まわりに回転させ(Hips のヨー/ピッチ)、lateral/height/depthは軸に沿って平行移動させます(Hips の X / Y / Z)。静止ポーズはActivateアクションで記録します。各軸(ChairAxis)は可動範囲(min/max、0/0でロック)、ソースと追従対象の遊び(バックラッシュ)となるデッドゾーン(Hips の震えを吸収)、ダンピング(回転はSmoothDampAngle)を持ちます。 - ライブプロジェクト単位の状態とライブプロジェクトスコープ設定: 公開メンバーが永続化スコープ(Scene / Project)を持つようになりました。ライブプロジェクトスコープの値はクラスごとの
{Class}.settings.jsonに保存されます(画面出力のサイズ/全画面/Spout、ライブシーンの画質が Project スコープに)。StartupStateStoreがライブプロジェクトごとに最後に開いたライブシーンを記憶し(Settings/startup.json)、StartupSceneSwitcherが最初のシーン読み込み前にそこへリダイレクトします。 - 2 階層(メモリ+ファイル)のサムネイルキャッシュを、隠しの
.livestudioライブプロジェクトフォルダ(ProjectPaths)下に保存するようにしました。
Changed
- 破壊的変更: アバタープロップの
AvatarPropコンポーネントを、組み合わせ可能な兄弟コンポーネントに分割しました。新しい共有のProp(公開名"Prop"を維持。ソケット追従と位置/回転/スケールのオフセットを担う)と、ふるまいコンポーネントです。AvatarPropはAvatarItem(アバター→プロップのパラメータブリッジと表情駆動)に改名され、[MovedFrom]により旧型参照は解決され続けます。既存の*.prop.lsbバンドルは旧来の単一コンポーネントしか持たないため、再書き出しが必要です(再書き出ししたバンドルのルートはPropとAvatarItemの両方を持ちます)。ライブシーンの"Prop"状態キーは変わりません。 - 表情のキーバインドが、アクションシステムを通じて汎用的に駆動されるようになりました。
SetPropertyActionがexpressions[name].weight(データ駆動の表情スロット)を書き込むため、専用のSetExpressionAction/ExpressionBindingSystemと旧/api/expressionsバインドルートは削除されました。既存の表情キーバインドは作り直す必要があります。 - 共有の
FormatHeaderが、ライブシーン/ライブプロジェクト設定/プリセットの各ファイルでバージョン管理を統一します(生 JSON シリアライズ、孤立エントリの除去)。 - オブジェクトプリセット(
.preset.json)が、プロップとアバターの状態を差分として、寛容なリーダーで取り込むようになりました。 - Button モードのワンショットアクション(アバター/ステージの切り替え)が、キー押下時ではなくキー解放時に確定するようになりました(
ActionContext.triggered)。 ActionManager/ExternalAssetManager/StageManagerのシングルトンが、リモートアプリの汎用シーンオブジェクト一覧から隠されるようになりました(HideInScene)。専用ページからは引き続きアクセスできます。
[0.23.6] - 2026-06-23
Added
AvatarPropにscaleOffset(Vector3、既定1,1,1)を追加しました。プロップに設定されたローカルスケールに乗算されるため、アバターに装着したプロップの大きさをリモートアプリ/インスペクタから調整できます。毎フレーム適用され、位置・回転のオフセットと同様に unload/reload をまたいで保持されます。
Changed
- ステージマークが、単独の
AvatarStageControllerではなくStageManager経由でアバターをワープするようになりました。各SetBundleEntryが自身のセットシーン内のStageMarkラベルを公開し(セット単位にスコープされた新しいmarksフィールド)、StageManager.WarpTo(setId, markLabel)がそのセットのシーン内の名前付きマークへアバターのアンカーを移動します(空のラベルは原点へワープ)。
Removed
- 破壊的変更:
AvatarStageController(place/availablePlacesで読み込み済みの全マークから選ぶ「Avatar Placement」公開オブジェクト)を削除しました。ワープは上記の通りStageManager.WarpToでセット単位に行います。Live Studio Systemプレハブからも削除されています。
[0.23.5] - 2026-06-22
Changed
- オブジェクトのプリセットファイル(
*.preset.json)が、ライブシーンのファイル形式に合わせてjp.lilium.remotecontrol.presetのフォーマット識別子とformatVersionフィールドを持つようになりました。冗長だったsourceKindヒントは削除しました(ソースパスから導出されます)。
Removed
- 旧
prop.presetプリセット形式の後方互換読み込みを削除しました。この変更より前に保存したプリセットは保存し直す必要があります。
[0.23.1] - 2026-06-21
Fixed
- 起動のたびに Studio が未保存の変更を報告する問題(ライブプロジェクトフォルダにファイルがあると、未保存変更ダイアログで終了がブロックされる)を修正しました。
ExternalAssetManagerのアセット配列を直接永続化していたため、遅延実行されるライブプロジェクトフォルダのクロールが、ライブシーン保存の基準を取得した後にその配列を埋めていました。使用中のみを対象とする永続化シャドウを復元し、クロールで構築したカタログが保存/変更状態の対象に入らないようにしました。
[0.23.0] - 2026-06-21
Added
ExternalAssetManager: プロップ・アバター・セットバンドルを統一的に読み込み/解放する単一の実行時カタログ(旧PropManagerを置き換え)。アバター選択、プロップ読み込み、セットバンドル管理はすべてこれを経由します。- 実行時アバタープロップ:
.prop.lsbバンドルを実行時にアバターへ装着し、AvatarPropを介してボーン/ソケットに追従させられます。ソケット装着はアバターのリグやスケールに対して堅牢になり、プロップの公開オブジェクトのパラメータと id が unload/reload をまたいで保持されます。 - プロッププリセット: 読み込んだプロップの調整済みパラメータを
.preset.jsonとして保存し(SaveAsPreset)、新しいプロップとして再読み込みできます。プロップの既定値からの差分のみを保存します。 - ライブシーンのアセットとバンドルサムネイルを備えたライブプロジェクトアセットカタログ。書き出した
.prop.lsb/.avatar.lsbバンドルにはサムネイルが埋め込まれ、リモートアプリのアセットカードで使われます。 - アバターの VRM サムネイルを
/api/avatar/image?id=で配信するようにしました。UniVRM への依存なしに VRM 内蔵のサムネイルから抽出します。 - クロスシーンのリモート制御: 追加読み込みされたセットバンドルが、単一の常駐ホストサーバーにオブジェクトを公開します。新しいステージマーク(
StageMark/StageMarkRegistry/AvatarStageController)により、リモートアプリから名前付きマーク(および組み込みの Zero 原点)へアバターをワープできます。また、保存されたアクティブシーンが読み込み時に復元されます。 - アセットの追加時にライブプロジェクトへインポートするようにしました:
AddAssetは選択したファイルを、開いているライブプロジェクトフォルダのアセット種別ごとのサブフォルダ(Avatars/Props/Sets、種別ごとに解決)へコピーし、ライブプロジェクト内のコピーを登録します(名前が衝突した場合は(n)サフィックスを付与)。すでにライブプロジェクトフォルダ内にあるファイルはコピーせずその場で登録します。現時点では選択した単一ファイルのみコピーします。 - ライブプロジェクトフォルダを保存データの拠点にしました: 初回起動時に
Documents/<brand>/<project>を作成して開き、ライブシーンの既定の「名前を付けて保存」先として使用し、「保存フォルダを開く」でこのフォルダを開きます。
Changed
- リモート制御ホストとそのマネージャを、ベースシーンの再読み込みをまたいで破棄・再生成せずに常駐させるようにしました。これにより、シーン切り替え時もリモート接続とマネージャの状態が維持されます。Studio のプレハブとテンプレートはこの常駐ホストを前提に再構成しました。
VRCFTAvatarの表情をExpressionResolver経由で処理するようにし、変換したアバターにハンドジェスチャー表情を自動付与するようにしました。- 受信したポーズを、可変フレームレートのレンダー更新時に最も近い 2 つの受信 60fps フレーム間で時間補間するようにしました(
AvatarAnimationSystem.Lerp)。これによりアバターが滑らかに進み、フレーム保持時に SpringBone が震えなくなりました。 - 破壊的変更: シーンバンドルの概念を「set」、ワールドの概念を「stage」に改名し、バーチャルプロダクションの用語に合わせました(ステージはセット・プロップ・マークを内包します)。セットバンドルは
.set.lsbとして書き出されます。SceneBundleAsset→SetBundleAsset、SceneBundleLoader/LoadedSceneBundle→SetBundleLoader/LoadedSetBundle、SceneBundleExporter→SetBundleExporter(メニュー「Export Set Bundle (.set.lsb)」)、LiveStudioBundle.SceneExtension/IsSceneBundle→SetExtension/IsSetBundle、WorldManager→StageManager(公開関数AddSet/RemoveSet/SetActiveSet、公開プロパティsets)。従来の.scene.lsb拡張子は入力としては引き続き受け付けます(SetBundleAssetとして読み込み)が、書き出しでは生成されません。旧@type名(例:SceneBundleAsset/WorldManager)を参照する保存済みライブシーンは移行されず、保存し直す必要があります。そのような解決できないエントリは null の穴を残さず消えるようになりました。StageManagerには[MovedFrom("WorldManager")]が付いているため、シーン内の[SerializeReference]の配線は自動的に移行されます。 ExternalAssetManagerがアセット配列をライブシーンへ直接永続化するようにしました。ライブプロジェクトフォルダのクロールが読み込み時に無効化されたカタログを再構築するため、別途の永続化シャドウは削除しました。SavedPathsが基準ディレクトリを設定されたブランド名から導出し、保存データをライブプロジェクトごとのフォルダに整理するようにしました。従来の単一のSceneサブフォルダと独立したLiveStudioPathsInitializerを置き換えます(フォルダの初期設定はProjectManagerに移動)。
Fixed
ResetCameraがヨー/位置のオフセットを、ポーズ駆動のアバタールートではなくキャプチャーカメラチャンネルから導出するようにしました。これにより繰り返しリセットしても安定します。また、設定可能なカメラ高さ(既定 1.5m)により、仮想カメラを床に落とさず目線の高さに保ちます。- Arms Post Rig を
VRM1Avatarのときのみ追加するようにしました。 - ステージ/シーンの表示状態とアクティブ状態を起動時に正しく復元し、常駐アクティブエントリが復元時に確定するようにして、保存基準が復元後の状態と一致するようにしました(終了時に誤った未保存変更プロンプトが出ません)。
- 実行時に読み込んだアバターが、
PlayableGraphの構築から 1 フレーム後にアニメーションパラメータのオーバーライドを再適用するようにしました(コントローラーの既定値に戻りません)。また、VRMAvatarSetupSystemはIAvatarが既に存在する場合に重複した表情コントローラーを追加しないようにしました。
[0.22.0] - 2026-06-14
Added
- シーンバンドルのインポート:
.scene.lsbの AssetBundle を、実行時に追加(additive)で読み込めるようになりました。新しいRuntimeSceneManager/SceneBundleLoaderコンポーネントと共有のBundleBuildUtilityを追加し、LiveStudioBundleが複合拡張子を判別することで.scene.lsb(シーン)と.avatar.lsb(アバター)のバンドルが 1 つのパイプラインを共有します。 CaptureCameraControllerにchannelIndexプロパティ(ローカライズキーCAMERA_CHANNELINDEX)を公開しました。CaptureCameraTrackerが追従するキャプチャーカメラのチャンネル(0 または 1)を選べます。HumanoidPoseDataがボーンごとのトラッキング presence(bonePresences、0..1)を持つようになりました。AvatarBodyDriverのポーズジョブは、この重みでモーションのポーズをアバターのアニメーションにボーン単位でブレンドします。presence 1 のボーンはモーションの回転を完全に採用し、0 のボーンはアニメーションをそのまま流し、中間値は両者を slerp します。これにより、トラッキングされていない部位はAnimatorControllerのアニメーションを再生し続け、トラッキングされている部位はモーションに追従します。
Changed
ExternalAvatarSourceのファイル選択が、複合拡張子.avatar.lsb(従来は.lsb)でフィルタするようになり、アバターバンドルを.scene.lsbと区別します。旧.lsavatar拡張子も引き続き受け付けます。- アバターフレームがフレームごとに複数のキャプチャーカメラチャンネル(チャンネルごとに 1 つの
CameraData)を持つようになり、worldA / worldB のカメラを個別に伝達します。 VRM1AvatarとVRCFTAvatarが、毎フレーム Humanoid ボーンの Transform を直接書き込む代わりに、PlayableGraph(新しい共有AvatarBodyDriver)でボディアニメーションを駆動するようになりました。アバターに付けた任意のAnimatorControllerはグラフに組み込まれ、トラッキングがロストしている間はそのアニメーション(アイドルや ROM クリップなど)が再生され、トラッキング中はモーションのポーズで上書きされます。共有のトラッキング状態処理・メッシュ表示・アバタービルドの定型処理をAvatarBodyDriverとAvatarBuildNotifier.BuildAndNotifyに切り出しました。
Fixed
VRM1Avatar/VRCFTAvatarがBuildAvatar内でAnimatorを遅延解決するようにしました。同期的な.lsavatar読み込み時にStartより前に実行されることがあり、従来は animator が null のままエラーをログ出力していました。AvatarBodyDriverが、組み込んだAnimatorのapplyRootMotionを無効化するようにしました。コントローラーのルートモーションでアバター全体が床に沈む問題(VRCFT アバター)を解消します。
[0.21.3] - 2026-06-12
Changed
- 公開している
Lightの影トグルが、ハードシャドウではなくソフトシャドウ(LightShadows.Soft)を有効にするようにしました(プロパティのセッターとデシリアライズの両方)。影のエッジがなめらかになります。
[0.21.2] - 2026-06-11
Added
ChildProcessHost.RequestCloseAndReleaseを追加しました。終了シグナルを使わず、ウィンドウを持つ子プロセスへ WM_CLOSE を送り、終了を待たずに即座に解放します。
Changed
- Studio 終了時に、子プロセスの終了待ちでブロックしなくなりました。Fusion プロセスには終了シグナルを送って即座に解放し(
RequestStopAndRelease)、Remote アプリには WM_CLOSE を送って即座に解放します(RequestCloseAndRelease)。従来のように各プロセスで最大 5 秒WaitForExitを待つことはありません。 - Studio のビルド出力フォルダと exe 名を、プロジェクトフォルダ名から決めるようにしました。専用の VRC ビルドエントリーポイントと App VRC ビルドプロファイルのパスを廃止しています。
Fixed
SkyboxBackgroundが、Skybox/Cubemap シェーダーがプレイヤービルドから除外された場合に null シェーダーを伝播させる代わりに、エラーをログ出力するようにしました(ExposedObjectContainer.Initialize中に実行されるため例外を投げてはなりません)。
[0.21.1] - 2026-06-09
Added
ExternalAvatarSourceが、VRM に加えて AssetBundle ファイル(.lsavatar)からのアバター読み込みに対応しました(拡張子で自動判別)。
Changed
VRMAvatarSourceをExternalAvatarSourceに改名しました(既存シーン向けに従来の公開名は維持)。アバター読み込み APIRequestLoadVRMをIAvatarService/AvatarController/AvatarServiceでRequestLoadに改名しました。- モデルファイルのフィールドラベルを汎用化し、ヘルプテキストを VRM 専用の案内に絞りました。
[0.21.0] - 2026-06-08
Fixed
- Windows ビルドで、終了後にバックグラウンドプロセスが残ることがある問題(Windows.Gaming.Input 関連のネイティブシャットダウンのハングが原因)を修正しました。グレースフルシャットダウンが停滞した場合にプロセスを強制終了するウォッチドッグ(
QuitTerminationGuard)を追加しました。
[0.20.12] - 2026-06-04
Added
- 視線を VRM10 の
LookAtコンポーネントに委譲するようにし、アバターの目が直接駆動される代わりに設定した注視ターゲットを追従するようにしました。
[0.20.11] - 2026-06-03
Added
ChildProcessQuitSignal: 特定の子プロセス(PID で識別)を Windows の名前付きイベント経由でグレースフルに終了させ、ウィンドウなしの子プロセスが強制終了される代わりに自前の終了時保存を実行できるようにしました。- 選択した prefab アセットから missing script を削除する「Assets/Lilium Live Studio/Remove Missing Scripts」コンテキストメニューコマンドを追加しました。
Changed
- ウィンドウなしの子プロセスを停止する際に、終了を待たずに終了シグナルを送って即座に戻るようにしました(
ChildProcessHost.RequestStopAndRelease)。
Fixed
- パッケージの Readme インスペクターのボタン(言語切り替え・セクション操作・Install)が、パッケージを変更不可の git/レジストリ依存として取り込んだ場合でもクリックできるようにしました。
[0.20.10] - 2026-06-02
Added
netsh経由でインバウンド UDP 許可ルールを登録するWindowsFirewallヘルパーを追加しました。ルールはプログラムパスではなく待ち受けポートで識別されるため、ツールを移動・再インストール・別のパッケージキャッシュパスから配布しても 1 つのルールで動作します。ルールが存在しない場合のみ昇格を要求します。
[0.20.9] - 2026-06-02
Changed
- RemoteControl のハンドラーをインスタンス単位で登録し、ルートをコンストラクターで宣言するようにしました。
[0.19.1] - 2026-04-29
Added
- 初回リリース。
jp.lilium.virgo.studioから分離し、VTuber アプリ汎用の基盤(Camera / Lighting / Scene / Build / RemoteControl ベース / 共有 Localization)を、VirgoMotion 固有のモーション受信・アバター制御から切り離しました。