Remote Control
jp.lilium.livestudio.remotecontrol の変更履歴です。
[0.25.3] - 2026-07-22
Added
RemoteConfirmSystemが、確認をすべての画面へ同時に上げます——アプリを動かしているマシンの OS ダイアログと、接続中の各リモートアプリのモーダルです。最初に答えた画面で解決し、残りは閉じられます。スマートフォンから操作しているオペレーターは PC 側にしか出ていないプロンプトに答えられず、PC の前にいる人はリモートアプリにしか出ていないプロンプトに答えられませんでした。プロンプトはローカライズキーとして送られ、アプリの翻訳がフォールバックとして添えられるため、各リモートアプリはそれぞれの言語で描画します。回答はPOST /api/confirm(フレームワーク自体がプロンプトを上げるため、既定のルートです)で返ります。NativeConfirmDialogは OS ダイアログを専用スレッドで表示し、別スレッドから閉じられます。ブロッキングするConfirmDialogではミラーされたプロンプトを支えられません。表示中はアプリがリモートの回答を運ぶ REST 呼び出しを処理できず、それに基づいて動くこともできないためです。ConfirmDialogは、単一画面で完結する終端の経路のために残ります。RemoteControlBehaviour.TrySaveOrPromptは、現在のパスへ保存するか、プラットフォームの「名前を付けて保存」ピッカーを開きます(Editor / プレイヤーのどちらのダイアログを使うかは自身で選びます)。QuitApiHandler.onQuitRequestingが、REST 経由で要求された終了を拒否できます。プレイヤーにはApplication.wantsToQuitがありますが、Editor ではリモートからの終了がプレイモードを強制的に抜けさせ、その結果のExitingPlayModeは中断できません。ここに拒否の仕組みが無いと、非同期の未保存確認に答える前に Editor が破棄され、プロンプトがリモートアプリに届きませんでした。Editor で Stop を押した場合は、従来どおりローカルのミラーされないダイアログを使います(その経路は開いたままにできないためです)。LiveSceneSaveSystem.ResetAllToDefaultは、dirty 状態にかかわらず、含まれるすべてのオブジェクトを取得済みの既定値へ戻します。RevertAllToDefaultは dirty なプロパティを報告するオブジェクトしか触らず、読み込みはすべてを clean として基準を取り直すため、読み込み直後には何も戻しません。現在の状態を破棄しなければならない呼び出し側(別ライブプロジェクトへの切り替え)には、無条件の形が必要です。LoadCurrentData/LoadCurrentDataFromがforceBaseSceneReloadを受け取り、ファイルが既にアクティブなベースシーンを指している場合でもベースシーンを再読み込みします。これが無いと、同じベースシーンへの読み込みは現在の状態の上に逆シリアライズされ、(差分)ファイルが省略した値はすべて以前のまま残ります。
Changed
- 終了時に表示される未保存確認が
RemoteConfirmSystemを経由するようになり、マシン上だけでなくリモートアプリにも表示されます。遅延用のコルーチンも不要になりました。プロンプトがwantsToQuitをブロックせず、単に終了を拒否したうえで、回答が新しい終了を開始するためです。
Fixed
- Unity 6.5(6000.5)でパッケージがコンパイルできるようになりました。同バージョンでは
Object.GetInstanceID/Resources.InstanceIDToObject——および Unity 6.3 以降それらを動かし続けていたEntityId⇔intの暗黙変換——が Obsolete エラーに格上げされました。すべての呼び出し箇所はExposedObjectUtilityを通るようになり、ここがバージョン差分を持つ唯一の場所になります。ID はlongで運ばれるため、EntityIdが 32 ビットを超えても収まります。@instanceIDフィールドとExposedObjectHandlerの数値 id フォールバックは呼び出し側から見て不変です。値はセッション内でのみ有効で、永続化してはいけません。
[0.25.2] - 2026-07-21
Added
ExposedObjectHandle.MarkClean/ExposedObjectDefaultRegistry.MarkCleanは、オブジェクトの現在の状態をユーザー変更の基準として採用します。これにより、次の書き込みまで、そのオブジェクト上のどの状態も未保存の編集として数えられなくなります。動くのはユーザー変更の基準だけで、シリアライズの基準は取得済みの既定値を保持するため、その後の差分保存でも全差分が書き出されます。ディスクから状態が届く箇所で呼び出します。
Fixed
- 未保存判定が、起動のたびにシーンを dirty と報告しなくなりました。この誤判定は、既存ライブプロジェクトを開いたスタジオの終了をブロックしていました。旧来のチェックはシーン全体をシリアライズしてファイルと文字列比較していたため、「ユーザーが何かを編集した」のか「このビルドが、ファイルを書いたビルドとは異なる形でシーンをシリアライズする」のかを区別できませんでした——古いバージョンで書かれた
live.jsonを持つライブプロジェクトは、常にこの判定に失敗し続けていました。現在は、ディスクから状態が届くたびに基準を取り直した、オブジェクト単位の dirty 追跡(シーン読み込み、保存、遅延した保留エントリ、アセット読み込みの後処理)に問い合わせます。 IsDirtyとGetDirtyPropertiesを、永続化の形状(forPersistence: true)で比較するようにしました。CaptureDefaultsとプロパティ単位の基準に揃えます。dirty は「保存したら何か異なるものが書き出される」を意味すべきなので、読み取り専用や永続化しないメンバー——まさに復元後の非同期読み込みが churn させるもの、すなわちクロールで構築されるExternalAssetManager.assetsカタログ、再構築されるStageManager.sets、TransformRef.availableOwnerNames——はもう数えられません。
[0.25.0] - 2026-07-17
Added
- 引数つき・プロパティパス経由での公開関数の呼び出しに対応しました。
ExposedPropertySerializer.BuildInvokeArgumentsが JSON 配列から型付きの位置引数配列を構築し(REST の invoke 経路と、保存された呼び出しを再生する側で共有)、ExposedObjectHandle.ResolveFunctionが、プロパティパス経由で到達する関数(メンバー上のネストされた関数)を、REST 層がネストしたメンバーを解決するのと同じ方式で解決します。 ExposedProperty.TryGetValue<T>/TrySetValue<T>: Source Generator が生成する型付きデリゲート(Func<object,T>/Action<object,T>)を通じて、値型のメンバーをボックス化せずに読み書きします。ShowIf/HideIfが複数の条件を受け取れるようになり、メソッドにも付けられるようになりました。条件はvisibilityConditions配列としてシリアライズされ、クライアント側で AND 評価されるため、メンバー(や関数ボタン)を複数の条件で同時にゲートできます。条件が 1 つの場合は従来どおりvisibilityフィールドを出力するため、型の出力はバイト単位で互換のままです。ImagePreviewAttribute: サーバー相対の画像 URL をポーリングして表示する読み取り専用コントロールです(キャプチャーのカメラプレビューを支えます)。ExposedObjectRegistry.keyedCollectionVersionとNotifyKeyedCollectionChanged()を追加しました。キーつきコレクションの要素が再構築されたときに、キャッシュ済みのプロパティ解決を無効化して、古いキーへ書き込みが着地するのを止められます。
Changed
- 公開プロパティのパイプラインから GC を排除しました。
bool/enumのゲッターはBoxedValues経由で正規のボックス化済みインスタンスを返し、購読者がいないときはoldValueの読み取りとプロパティ変更イベントをスキップします。FindProperty/GetPropertyは span ベース(name.ToString()なし)になり、配列要素のディスクリプタはキャッシュされ(ExposedPropertyType.GetArrayElement)、要素の探索はコレクションを 1 度だけ読んで一致した要素のみを構築します。PropertyPathのスラッシュ変換とAppendIndexはアロケーションフリーになり、ExposedUnityObject.componentsは LINQ のフィルタを 2 パスの手書きに置き換えました。REST のレスポンスとscene.jsonは変更ありません。
[0.24.3] - 2026-07-09
Added
AssetSelectorコントロール: アセット参照のフィールドがアセット GUID としてシリアライズされます(AssetRegistry。OnValidateで焼き込み、ランタイムで登録)。GET /api/asset?guid=が GUID をアセットの名前と型に解決するため、クライアントが選択肢にラベルを付けられます。サブアセットはguid:localIdの複合キーを使い、任意のrefPropertyがスキーム接頭辞つきの外部キー(例:file:<path>#<sub>)を運びます。外部キーの対象はまだ読み込まれていない場合があるため、AssetRegistryに名前フォールバックと表示名の解決が追加され、未読み込みのキーにもラベルを付けられます。GET /exposed/orphansとPOST /exposed/orphans/remove: 保存済みシーンが持っているが、今のセッションにはオブジェクトが存在しないルート参照を、削除可能な「missing」項目として提示します(パスを持つ子のオーバーライドは、アセットが非同期に読み込まれればバインドされるため除外されます)。削除すると、その id をルートに持つキュー済みのエントリがすべて破棄されるため、次のシーン保存でそれが再出力されることはありません。RemoteErrorNotifierが、Unity のエラー(Error / Exception / Assert)を、接続中のすべてのリモートアプリへエラー通知として転送します。ブロードキャストはメインスレッドへマーシャリングされ(サーバーレジストリはそこでのみ変更されます)、同一のメッセージは重複排除され、バーストには上限が設けられるため、毎フレームの例外がアプリを溢れさせることはありません。既定で有効です(RemoteErrorNotifier.enabled)。
Changed
- ライブシーンのシリアライザが、永続化ファイルのエントリに対して曖昧な復元の警告を出さなくなりました。それらは
@nameを持たず型でも一意に特定できないため、ユーザーが対処できない警告を出す代わりに、未解決のままリモートアプリが提示・削除できるようにします。
[0.24.2] - 2026-07-05
Changed
- 破壊的変更:
GET /exposed/objectsとGET /exposed/object/{id}が、既定で 深さ 1 のオブジェクトを返すようになりました。ネストしたインライン(未登録)の複合子要素は、完全に展開する代わりに切り詰めスタブ{ "@type": ..., "@truncated": true }として出力され、オブジェクトグラフが大きくなってもペイロードを小さくスケーラブルに保ちます。配列は深さを消費しないため、要素数と要素ごとの型は引き続き見え、登録済みの子要素は@ref形式のままです。?nested(または?nested=true)を渡すと従来の無制限展開に戻せます。プロパティ読み取り(GET /exposed/object/{id}/{path})、SSE ブロードキャスト、PUT レスポンス、永続化(scene / project / preset)は変更なく、常に完全展開されるため保存ファイルはバイト単位で同一に保たれます。オブジェクト一覧からネスト値をたどっていたクライアントは、?nestedを渡すか、切り詰められた各子要素をプロパティ GET で遅延取得する必要があります。
Added
POST /exposed/batchエンドポイントを追加しました。複数のオブジェクト/プロパティ/関数の操作を 1 リクエストで適用し、項目ごとに continue-on-error します(各項目のステータスとボディが順番に返されます)。公開 REST API はDocumentation~/openapi.ymlにドキュメント化されました。- メンバー単位の永続化スコープ:
[ExposedField]/[ExposedProperty]にpersistScope(Scene 既定 / Project)が追加され、シリアライザがライブシーンの状態とクラスごとのライブプロジェクト設定を分離できます。 - 公開キーパスアドレッシング: プロパティパスが、安定した
[ExposedKey]値で配列要素を指定できるようになりました(例:expressions[Joy].weight)。汎用のSetPropertyActionの「キーへバインド」フローを支えます。 [Collapsed]属性: リモートアプリが配列やネストした構造体を既定で折りたたんで表示するヒントです(展開トグルは残ります)。プロパティコントローラーとは独立した単独のcollapsedフラグとして出力されます。- 多態的な配列プロパティに対して
elementTypeOptionsを出力し、リモートアプリが「…型の要素を追加」を提示できるようにしました。 - 専用の
GET /api/performanceエンドポイントを追加しました。アプリの現在の FPS とプロセスメモリを返します(プレイヤーループからPerformanceMonitorでサンプリングし、メモリは Windows のネイティブProcessMemoryReader経由で読み取ります)。リモートアプリは、オーバーレイ表示中のみこのエンドポイントをポーリングでき、他のリクエストに相乗りする必要がなくなります。
Fixed
ExposedPropertyUtility.CreateDefaultElementが、抽象型/インターフェース型の要素に対して最初の具象[ExposedClass]サブタイプにフォールバックするようにしました。これにより、多態的な[SerializeReference]配列への要素追加がMissingMethodExceptionを投げなくなります。ExposedObjectHandleの構築時に、既に適用済みのオーバーライド値をダーティ検出のベースラインに焼き込まないようにしました。コンストラクタは無条件のSetDefaultの代わりにEnsureDefaultsCaptured(未設定のときだけキャプチャし、既存のベースラインは保持)を使うようになり、ハンドル登録前にライブシーンの復元でキャプチャされたオーバーライドが、現在の(適用済みの)値で上書きされて保存時に失われることがなくなりました。
[0.23.6] - 2026-06-23
Changed
[StringSelector]を、プロパティ・フィールドに加えてメソッドの引数にも付けられるようにしました。これを付けたExposedFunctionの引数は、所有オブジェクトのsourcePropertyNameプロパティを選択肢とするドロップダウンとして表示され、制約された文字列引数(例: 名前付きのステージマークの選択)を取れるようになります。
[0.23.4] - 2026-06-22
Fixed
?type=Xクエリが、GameObject が汎用ラッパーハンドル経由でも公開されている場合に、ファーストクラスの[ExposedClass]コンポーネント(例: 「Avatar」として公開されたAvatarController)を取りこぼさないようにしました。コンポーネントとラッパーは別個の公開アイデンティティです。従来の GameObject アイデンティティによる重複排除では、アバターがExposedGameObjectWithTransformとして公開されると RemoteApp のページが「利用可能なアバターがありません」と表示していました。同一ターゲットの本当の重複は引き続きFindByTargetで統合されます。- ライブシーンのシリアライズが、ベースシーンの再読み込み中に「実在したが破棄された(fake-null)」Unity 参照のみをスキップし、最初から裏付けの参照を持たない純粋なプロキシは引き続きシリアライズするようにしました。
[0.23.2] - 2026-06-21
Fixed
FrameRate.AsFrameNumberが、floatを経由してキャストする代わりにdoubleで計算するようにしました。2^24(60fps で約 3.2 日)を超えるとfloatは整数精度を失い、連続するフレーム番号が同じ値に潰れて Studio の再生バッファのルーティングがカクついていました。doubleは 2^53 までのフレーム整数を正確に表せます。
[0.23.0] - 2026-06-21
Added
[ExposedExternalEnum(typeof(T))]アセンブリ属性を追加しました。型を変更せずに、外部 enum や組み込み enum(例:HumanBodyBones)をリモートアプリのドロップダウンとして登録できます。excludeNamesに対応し、登録はReset()をまたいで保持されます。ExposedObjectSnapshotで、公開オブジェクトの既定状態をキャプチャし、それとの差分を計算できるようになりました(CaptureDefaults/CaptureDelta)。プロッププリセットの基盤となります。- ライブシーンの保存時にリモートアプリへ通知するようにしました。
Fixed
- 解決できない
@typeを参照する保存済みライブシーンのエントリが、null の穴を残さずきれいに消えるようにしました。 - ライブシーンの復元時に、シーンの表示状態とアクティブ状態を起動時に正しく再適用するようにしました。
[0.22.1] - 2026-06-14
Fixed
- ライブシーンの保存時に、
baseSceneNameとしてビルド設定に含まれるシーンのみを記録するようにしました。追加読み込みした.scene.lsbのワールド(buildIndex -1)がアクティブシーンの状態で保存すると、従来はそのワールド名が書き込まれ、ワールドを含まないビルドでそのファイルを読み込むとアクティブシーンが変更扱いになり、未保存の変更ダイアログでグレースフルな終了がブロックされていました。名前はアクティブシーンがビルドシーンの場合のみそれを採用し、そうでない場合は最初に読み込まれたビルドシーン(ワールドはその上にキャリブレーションされる)を採用するようにし、保存とHasUnsavedChangesの基準の両方に適用しました。
[0.21.2] - 2026-06-11
Fixed
ExposedObjectUtility.InstanceIDToObjectが、内部 API のUnityEngine.Object.FindObjectFromInstanceIDをリフレクションで呼ぶ代わりに、公開 API のResources.InstanceIDToObjectを呼ぶようにしました。Unity 6.1 (6000.3) で内部 API の引数がintからEntityId構造体へ変わり動作しなくなっていた問題に対応しています。公開 API は Unity 2021.3〜6000.3 を通じてint引数を維持します。
[0.21.0] - 2026-06-08
Changed
- メンバーアクセスをソース生成アクセサー方式に変更し、
Unity.Propertiesへの実行時依存を排除してリフレクションのオーバーヘッドを削減しました。 ExposedObjectを readonly struct 化し、ExposedObjectHandleに改名しました。プロパティアクセス時の一時ビューのアロケーションを排除しています。
Fixed
- 静的な
[ExposedClass]型が、全ゲームアセンブリの読み込み前に静的コンストラクターが走ると公開対象から消えてしまう問題を修正しました(アセンブリ読み込み後に属性を再スキャン)。 - 継承した
[ExposedClass]メンバーを宣言順で登録するようにしました。
[0.20.12] - 2026-06-04
Added
- LiveScene の読み込みに
switchSceneOnLoadオプションを追加しました。シーンを読み込んだときにそのシーンへ切り替えるかどうかを制御できます。
[0.20.11] - 2026-06-03
Added
- シーン保存時に live scene ファイルのパスをログ出力するようにしました。
[0.20.10] - 2026-06-02
- 機能的な変更はありません(モノレポのリリースに合わせてバージョンを同期)。
[0.20.9] - 2026-06-02
Added
- サーバーを全ネットワークインターフェースにバインドする
allowExternalConnectionsオプションを追加しました。
Changed
- ハンドラーをインスタンス単位で登録し、ルートをコンストラクターで宣言するようにしました。