オーサリングカーネル — 第二のオーサリングサーフェスを構築する
上級者向け。SheetForge のエンジンの上に、独自のオーサリング UI(たとえばノードグラフのキャンバス)を構築したいアセット/ツール作者のためのページです。Data Studio を使うだけのゲームチームには、このページは必要ありません。
オーサリングウィンドウはエンジンでは ありません。Data Studio——そしてその横にあるブラウザーアプリ——は、ウィンドウに依存しないオーサリングカーネルの 利用者 です。
それらが行うことはすべて、公開された型を通じて駆動されており、第三のサーフェスも同じように駆動できます: ステージング、検証、反映のオーケストレーション、undo の境界、再インポート。すでに二つのサーフェスがそうしているという事実こそが、この継ぎ目が願望ではなく現実であることの実践的な証明です。
まるごとサーフェスを構築する前に、その必要性をすでに満たす拡張ポイントがないか確認してください。プラグインは、ウィンドウをまったく所有することなく、同梱されているウィンドウに動詞・パネル・バッジ・セルウィジェットを追加できます。それらはデータとして記述されるため、エディター と ブラウザーの両方で描画されます——プラグイン作成 §4.16 を参照してください。このページは、あなた自身のキャンバスが欲しい場合のためのものです。
no-IVT のコンシューマーシミュレーションテストアセンブリ(SheetForge.Tests.Consumer — Core や Editor への InternalsVisibleTo を持ちません)が、公開 API のみに対して、仮想的なオーサリングサーフェスをエンドツーエンドで実装しています。もし必要なメンバーが internal であれば、そのアセンブリはコンパイルできません(CS0122)。そのため、これは以下で説明するサーフェスの実行可能な仕様として機能します。
三つのオブジェクトから成るエンジン
┌─────────────────────┐ ┌──────────────────────────┐ ┌───────────────┐
│ AuthoringSession │────▶│ AuthoringDispatcher │────▶│ BaselineStore │
│ (staging state) │ │ .Reflect() │ │ (round-trip │
│ │ │ (the full cycle) │ │ snapshots) │
└─────────────────────┘ └────────────┬─────────────┘ └───────────────┘
│ binds
┌────────────▼─────────────┐
│ AuthoringDispatchCallbacks│
│ (view concerns — YOUR UI) │
└──────────────────────────┘AuthoringSession — ステージング状態
[Serializable] な、意図的に ScriptableObject ではないただのクラスです。これをあなたの EditorWindow の [SerializeField] フィールドに保持するだけで、Unity 標準の Undo スナップショットと、ドメインリロードを乗り越える耐性が、追加コストなしで手に入ります——これは、同梱されているウィンドウの Ctrl+Z の背後にあるのと同じ仕組みです。
これは、保留中のすべての状態を保持します:
- セルの編集(
Edits)、新規行(NewRows)、構造操作(StructOps) - タブごとの並べ替え(
Reorders)、タブの名前変更(TabRenames) - baseline のアンカー、分離された編集
それに加えて、変更/照会用の API も持ちます:
SetStaged(...)— セルの編集を保留状態にします。編集は 論理アドレス(タブ・RecordId・フィールド)を持ち、物理的な行番号は、反映の直前に再解決される、そこから導出されたキャッシュです。ResolveBaselineEdits(provider)— すべての編集を、現在の baseline に対して再アンカーします。解決可能な編集はそのまま進み、解決不能な三つのケース(外部での名前変更 / 外部での削除 / キーの競合)はIsolatedEditsに移されます——反映からは除外され、バッジ表示され、サイレントに破棄されることも、セッションをブロックすることも決してありません。- Baseline を読み取るためのサーフェス:
TabNames、TryGetBaselineTable(tab, out SheetTable)— パーサーに自分で触れることなく、型付けされたスキーマアクセス(TypeToken、@desc、@overlap)を得られます。 EffectiveStructOps()/PendingStructCount()— 構造操作の、合成された正規のビューです。- 再マッピングのフック(
RemapFieldName/RemapRecordId/RemapTab)は、名前変更の前後で保留中の状態の一貫性を保ちます。 LastProjectionResultは、最新のプロジェクションをキャッシュします。
AuthoringDispatcher — 反映のオーケストレーション
var dispatcher = new AuthoringDispatcher(session, callbacks, baselineStore);
dispatcher.Reflect(); // the entire cycle, one callReflect() が実行するのは、順に次のとおりです:
- 事前検証
- ソースごとの反映 — ローカルなら精密な書き込み、Google なら安全な書き換え、カスタムプロバイダーならあなた自身のターゲット
- 保持された状態のクリーンアップ
ClearUndoによる確定境界- レポート付きの自動再インポート
さらに:
BuildProjectionResult()— 現在の保留中の状態を、副作用のない 形でImportResultとしてプロジェクションします(「反映されたと仮定して検証する」)。ライブのエラーバッジに使ってください。- 公開されている
Session/Callbacks/Baselines— カスタムソースプロバイダーは、これらを使って自身の反映ターゲットを組み立てます。
AuthoringDispatchCallbacks — あなたの UI の契約
ディスパッチャーが、あらゆるビュー関連の関心事のために呼び出す、13個の一般的なデリゲートの束です: ResolveSettings、確認ダイアログ(ConfirmKeyRenames、ConfirmTabRenames など)、RenderReport(Action<ImportReport> — null 許容で、観測用です)、PushApprover、TriggerReimport、ClearUndo、Rebuild など。組み込みの Local/Google ソースに特有の14個のダイアログデリゲートは、独立したオプトインの BuiltInSourceDialogs バンドルに分離されています——外部のサーフェスやプロバイダーは、それらをバインドする必要が一切ありません。同梱されているウィンドウは、ダイアログを表示するデフォルトの実装をバインドします。あなたのキャンバスは、独自の実装をバインドします(あるいは何もしません)。エンジン自体が UI を描画することは決してありません。
グラフ用の素材
「ノード = レコード、エッジ = 参照 ∪ 宣言」というプロジェクションのために:
ReferenceScanner(Core)— すべてのテーブルを横断して、参照の出現箇所(スカラー、リスト要素、明示的なデフォルト値)を列挙するための 唯一の信頼できる情報源 です。参照バリデーターが使うのと同じ列挙であるため、あなたのグラフと検証は、構造的に一致します。Scan(tables)/ScanTable/ScanField/IsReferenceField。IEdgeContributor/EdgeSpec/EdgeContributorRegistry(Core)— ドメインプラグインが、スキャナーからは見えないエッジ(カスタムタイプの値の内側、type列によるリンク、ペイロードレコードを伴うレコードエッジ)を宣言します。これらは Editor のPluginRegistry.BuildEdgeContributorsを通じて収集してください。ReferenceIndex/RecordEdge(Core)— Data Studio 自身のキャンバスが動作する基盤となる、組み立て済みのスナップショットです:Build(...)は、スキャンされた参照とコントリビューターのエッジを一度だけマージし、その後OutEdges/InEdges/InCountはレコードごとに O(1) で答えます。メンバーの完全な一覧は API リファレンス にあります。IRecordCanvasAugmenter/CanvasAugmentBuilder(Core)— タブ単位のオーバーライド契約です。ドメインパックに、Studio のキャンバスを拡張するのと同じ方法で あなたの キャンバスも拡張してほしい場合に使います(仮想ノード、追加のエッジ、レイヤーと表示のヒント)。ProjectionErrorMapper(Editor、純粋)— プロジェクションエラーの物理的な座標(タブ/行/フィールド)を 論理アドレス(タブ/RecordId/フィールド)に変換します。これにより、エラーバッジを行番号ではなくノードに紐付けることができます。
補助的な要素
| 型 | あなたのサーフェスがそれを何に使うか |
|---|---|
ImportEvents | 二つのバス、どちらも 公開契約です。ImportCompleted(ImportCompletedArgs: Tabs · BakeFolder)は、自動チェーンがベイクまで最後まで実行されたときに発火し、購読者はベイクされたアセットを読み取れます。BaselineUpdated(BaselineUpdatedArgs: Tabs · Quarantined)は、シートのスナップショットが保存されるたびに発火します——検証が失敗した実行も含みます——これは、失敗したシートを表示して修正させたいサーフェスが購読するものです。あなたのビューがシートと焼き込まれた値の両方を表示するなら、両方を購読してください。OnDisable では、対称的に購読解除してください。 |
IPipelineObserver | 知る必要があるものが、ウィンドウではなく プラグイン である場合は、こちらがより軽量な経路です: オブザーバーを登録すれば、各インポートサイクルの終了時に不変の PipelineRunView を受け取れます。エディターへの依存は一切なく、ブラウザーホストでも動作します。プラグイン作成 §4.17 を参照してください。 |
RecordIdMinter | 新規レコードの id を提案します — 接頭辞の検出 + 衝突を避ける一意化。これは 提案 のための API であり、意図的に自動採番ではありません。 |
EphemeralSoApply | 保留中の値を、ベイクされた SO の上に一時的にプレビューします(再インポートすると元に戻ります)。計算可能な部分集合だけを適用し、保留中の列やパース失敗についてはスキップ理由を返します。同梱されている UI は、もはやこれを一切駆動しません。そのため、このプレビューを望むサーフェスは、そのためのボタンを自分自身で持つことになります。 |
KeyRenamePlanner | キーの名前変更(3段階: 抽出 / 伝播 / 書き換え)を計画します。同梱されているウィンドウと同じ方法です。タブの名前変更の確認は、代わりに公開の ConfirmTabRenames コールバックを経由します。 |
SourceProviderRegistry | 設定 UI と同じ方法で、有効なソースプロバイダーを解決します。 |
カーネルが強制する基本ルール(そしてあなたが引き継ぐもの)
- シートは正規であり続けます — あなたのサーフェスはステージングと反映を行いますが、SO に書き込むことは決してありません。
- 検証してから反映する — 事前検証が失敗した場合、
Reflect()は何も書き込みません。 - サイレントな損失はありません — 解決できない編集は、理由とともに分離されます。確認は、あなたのコールバックを通じて行われます。
- Undo はネイティブに統合されます — セッションをシリアライズされたフィールドに保持し、あなたのウィンドウで undo スナップショットを登録してください。
ClearUndoが反映の境界を示します。 - ドメインに依存しません — カーネルにはドメイン語彙が一切含まれません(ガードテスト済み)。あなたのドメインは、カーネルの変更を通じてではなく、プラグイン契約を通じてやってきます。
関連ページ
- API リファレンス — ここで名前が挙がったすべてのもののシグネチャ
- プラグイン作成 — カーネルと並んで、あなたのドメインが使う契約
- Data Studio — あなたのサーフェスが再現または置き換える振る舞い