本文へスキップ
SheetForge

機能と制限

このページには、SheetForge が意図的に 行わない こと、まだできないこと、あるいはあなたの予想とは違う形で行うことのすべてを、その理由・回避策・将来的な余地の有無とともに掲載しています。

各項目のフォーマット: 何が / なぜ / 回避策(+ 意味がある場合は将来的な余地)。


1. プラットフォームと依存関係

Addressables は必須 — なければパイプラインはロックされる

  • 何が: アドレスによる読み込みがランタイムの経路であり、AssetRef@Group 型は com.unity.addressables を必要とするため、このアセットを使うにはパッケージのインストールが必要です。アセット自体は、それがなくてもコンパイルできます——Addressables を使用するコードはすべて、パッケージが存在するときにのみ有効になる SHEETFORGE_ADDRESSABLES バージョン定義の内側にあります。
  • 欠落時の挙動: パイプライン全体(インポート・エクスポート・Push・オーサリングの書き戻し)は ロックされ、機能低下ではありません——どのエントリーポイントを実行してもインストール通知が表示され、処理が止まります。部分的/サイレントな抜け道はありません(「アセットキー検証をスキップする」といったフォールバックはありません)。Editor はパッケージなしでもコンパイルされるため、Safe Mode に陥ることは決してありません: 「はじめに」ウィンドウは正常に開き、その Addressables 行は Open Package Manager ボタンとともに ✗ を示します。依存関係を持たない SheetForge.Setup ブートストラップは、他の無関係なコンパイル失敗に備えたガイダンスネットとして残っています。
  • Data Studio 内で、パッケージがない場合: アセットセルの ⊙ ピッカーボタンとそのドラッグ&ドロップターゲットは無効化され、理由がツールチップとして表示されます。セルへのアドレスの手入力は引き続き機能します。
  • なぜ Resources/Addressables の二重の抽象化にしないのか: オーバーエンジニアリングだと判断され、意図的に作られていません。
  • 回避策: Addressables をインストールしてください。Asset Store のインポートプロンプトが、コンパイルの前にこれを処理します。もし Skip した場合は、コンパイルできる Editor がインストールを案内します。
  • 検証: 両方の分岐が、実際に確かめられています——バージョン定義を取り除いた状態(「Addressables が存在しない」ことをシミュレート)で、製品とテストアセンブリはエラー0件でコンパイルされます。復元後は、エラー0件、警告0件です。さらに、Addressables 未インストールの新規プロジェクトへこのアセットを実際にインポートし、エンドツーエンドで独立検証済みです: プロジェクトはコンパイルされ、設計どおりインストール案内ウィンドウが表示されます。

Unity Localization は任意です——待たされるのは StringTable の同期だけです

  • 何が: ローカライズシート(@loc)、LocRef 参照、キー定数、カバレッジレポート、Export、Push、xlsx、そしてウェブアプリのすべてが、com.unity.localizationインストールしなくても動作します。唯一待たされるのは StringTable 同期の出口 です: インストール通知(セッションにつき一回)を表示して止まります。パッケージに触れるコードはすべて SHEETFORGE_LOCALIZATION バージョン定義の内側にあるため、パッケージがなくても、すべてのアセンブリと生成コードのすべての行がコンパイルされます——生成されるフィールドは常に素の LocRef 構造体であり、パッケージの型になることは決してありません。
  • 不在時の挙動: 他のどこかで機能が低下したり、こっそりスキップされたりすることはありません——シートは完全なシートのままです。ロックされるのは同期の出口だけで、その理由も表示されます。
  • 対応バージョン: 1.5 以降。
  • 回避策: テーブルが欲しくなったときにパッケージをインストールしてください。それ以前に作成されたものはすべて、次に完了したインポートで同期されます。ローカライズシート を参照してください。

ワンクリックでのプログラム的インストールはない

  • 何が: セーフティネットウィンドウは案内するだけであり、パッケージ自体をインストールすることはありません。同じ規則が、Unity Localization のインストール通知にも当てはまります。
  • なぜ: Asset Store の申請ルールが、パッケージのプログラム的な変更を制限しています。ガイダンスウィンドウは、安全でルールに準拠した選択です。

SHEETFORGE 製品検出用 define は自動削除されない

  • 何が: Editor アセンブリは、あらゆるビルドターゲットに対して SHEETFORGE スクリプティング定義シンボルを自己登録します。これにより、他の アセットがコンパイル時に SheetForge のインストールを検出できます(プラグイン作成 ▸ 他のアセットから SheetForge を検出する を参照)。この登録は冪等です(未登録のときにのみ追加され、登録済みであれば再コンパイルは発生しません)。
  • 制限: 後でアセットを削除しても、その define は 残ったまま です——削除に気づくはずだったコードも、アセットと一緒に消えてしまっているためです。
  • 回避策: Project Settings ▸ Player ▸ Scripting Define Symbols(プラットフォームごと)で手動で削除してください。一つのシンボルを掃除するためだけに、バックグラウンドのウォッチャーを常駐させることは、意図的に行っていません。これは SHEETFORGE_ADDRESSABLES(Addressables パッケージが存在するかどうかだけを反映する、内部的なバージョン定義)とは別のものです。

2. Googleシートソース

ExportUrl モードは読み取り専用である

  • 何が: ExportUrl モードでは、Push・反映・構造編集・削除のすべてが無効化されます。
  • なぜ: これは認証を伴わない、リンク共有によるエクスポート経路であり、本質的に読み取り専用です。Push は常に SheetsApi の認証情報を必要とし、それはいかなるネットワーク呼び出しの前にも強制されます。
  • 回避策: 何らかの書き戻しが必要な場合は、SheetsApi モード(サービスアカウント — Googleシートの設定 を参照)を使ってください。

ExportUrl は gid マップを必要とする

  • 何が: gid マップが空だとインポートは失敗します。重複した gid は却下されます。
  • なぜ: gid のないエクスポート URL は、サイレントに最初のタブだけを返してしまいます——これはサイレントな破損の罠であるため、インポートはこれを拒否します。SheetsApi はタブを自動的に検出します。
  • 回避策: 各タブの #gid= の値を登録するか、SheetsApi を使ってください。

Push はキーで行を削除し、検証されたものだけを削除する

  • 何が: ローカルで削除されたレコードは、Push 時に——送信前の再取得がそのキーがインポート時に見えていた行にまだあることを確認したあとで——本番シートから削除されます。すでになくなっている行は完了として扱われます(べき等な再 Push)。別の行でキーが見つかった場合は通知とともにスキップされ、位置によって削除されることは決してありません。削除は承認サマリーの独立した区画に一覧表示され、各タブの中で下から上へ、最後に送信されます。
  • なぜ: 本番シートに対してキーで照合することが、変化してしまっている可能性のあるシートで削除を安全にする方法です。照合で確認できないものはそのまま手つかずにされます。
  • 限界: 行削除の能力を持たないソース(その能力を持ったことのないカスタムプロバイダー)は、以前の挙動にフォールバックします——削除は報告され、本番シート上の行はあなたが削除できるように残されます。

Push は競合するセルをスキップする(設計によるもの)

  • 何が: あなたのインポート後に第三者が編集したセル、キーがあいまいに移動した行、存在しない行、あるいは重複した本番のキーは、警告とともに スキップされます——上書きされることはありません。
  • なぜ: これはセーフティネットが機能している証拠です。送信されるセルは有効であり、スキップは他の人の変更を保護し、誤った行への書き込みを防ぎます。
  • 回避策: レポートの適用/スキップ件数を確認してください。整合性を取るために再インポートし、もう一度 Push してください。(競合解決 UI は別の機能になるでしょう——予定はありません。)

Push はキー列を必要とする

  • 何が: RecordId のキー列がなく、変更のあるタブがあると、それはプランエラーとなり、Push 全体をブロックします(どのタブも送信されません——部分送信はありません)。
  • なぜ: Push は本番シート内で行を キーによって 再特定します。キーがなければ、誤った行への書き込みを防ぐガードが機能しません。
  • 回避策: キー列を追加するか、ファイルに Export して貼り付けてください。

3. xlsx ソース

  • タブの名前変更は、xlsx 由来のタブを対象外とします — 複数シートのワークブックを保護するためです。ワークブック内で名前を変更し、再インポートしてください。

  • xlsx 由来のタブへのキー名変更の伝播は、バッチ全体をブロックします — xlsx の経路は、セル単位の精密な更新を安全に行うことができず、また部分的な反映は決して許されません。そのタブを直接編集し、再インポートしてください。

  • 表現できないセルは却下されます — キャッシュされた値を持たない数式セル、エラーセル、そしてセル内のタブ文字/改行です。組み込みの OOXML リーダーは、意図的に最小限に留められています(サードパーティコードはゼロです)。数式は値として実体化し、リストには ; を使ってください。

  • 値のみ — 数式・日付・書式は正直に解釈されます — 数式セルはキャッシュされた値を提供し(決して再計算されません)、日付書式のセルは yyyy-MM-dd の表示テキストとして読み取られ、それ以外の数値書式・結合セル・チャートはインポートされません。Web アプリのインポートダイアログは、実際に発生したことを「How this workbook was read」という注記の中で名指しします。エディターでは、同じポリシーがセルごとにサイレントに適用されます(上記の却下は引き続きセルごとに報告されます)。

  • エクスポートされるドロップダウンルールの一部は運べません — エクスポートは一つのワークブックであるため、参照列のドロップダウンは、対象シートのキー列にわたる本物の 範囲 として書き込まれます——これは Google のルールが持つのと同じ意味です。それでも次の三つのケースは除外され、一つの DropdownNotSupportedByFormat 警告としてまとめて名指しされます:

    • カンマを含むリストのメンバー(インラインの区切り文字がそれを分割してしまいます)。
    • フォーマットの 255 文字上限(引用符を含む)を超えるインラインリスト。
    • 対象タブがそのワークブック内にない範囲。

    いずれの場合も値そのものは完全にエクスポートされます。ソース・エクスポート・プッシュ を参照してください。

4. オーサリング — Data Studio

キー列のないタブは新規レコードを受け付けず、値の編集はアンカーを失う

  • 何が: RecordId キー列のないタブも、通常どおりインポートされ、表示されます。そして 構造編集は完全に機能します——列やマーカーの追加・削除・名前変更・並べ替え、さらにシートレベルの名前変更や削除も含めてです。手に入らないのは 新規レコード だけです。キーのないレコードには、名前を付けることも参照することもできないためです:

    • 行追加コントロールは無効化されます。
    • 参照ピッカーはそこに作成することを拒否します(「…にはキー列がないため、新しいレコードを作成できません」)。
    • そこに書き込もうとするキャンバスやインスペクターアクションも何もしません。

    値セルは編集可能です——ただし、行をアドレス指定するキーがないため、その編集は行の 位置 に対してのみステージングされます。

  • なぜ: 保留中の編集は通常、(tab, record key, field) という論理アドレスで指定され、書き込みの直前にシートに対して再解決されます。これによって、編集は再インポート・行の並べ替え・他の誰かが上に行を挿入することを乗り越えて生き残ります。キー列がないとそのようなアドレスが存在しないため、代わりに編集は行番号に固定されたまま通過します——このセーフティネットの外側です。

    そのため、書き戻す前にシートの行があなたの知らないところで動いてしまうと(再インポート、あるいは誰かがソースを直接編集した場合)、位置に固定された編集が誤った行に着地してしまう可能性があります。これらは短いバッチでステージングして反映してください。

  • 回避策: RecordId 列を追加し(構造編集が使えるので、同じウィンドウ内で行えます)、反映すれば、論理アンカーが戻り、そのタブは完全にオーサリング可能になります。キーのないタブも、インポート する分には完全に有効です——これはオーサリングの制限であり、スキーマの制限ではありません。

列の名前変更 / @type の変更は、参照しているゲームコードを壊す。反映後は元に戻せない

  • 何が: 生成されるフィールドの名前/型が変わります。それを参照するゲームコードは、手動で更新する必要があります。Ctrl+Z が機能するのは、反映の だけです。
  • なぜ: 強い型付けのためです——フィールドは、生成されるスキーマの一部です。コンパイルの break は、自動チェーンの安全な中断によって、実行可能な文とともに捕捉されます。列の は完全に保持されます(変わるのはマーカーセルだけです)。
  • 回避策: 確認ダイアログが事前に警告します。あなたのコードを更新し、次のインポートで再開させてください。

タブの名前変更は、参照しているゲームコードを壊す。反映後は元に戻せない

上記と同じ仕組みです——生成されるクラス名が変わります(FooDatabaseBarDatabase)。再インポートが、アセット側のクリーンアップ(古いクラス、SO、アドレス)をすべて自動的に処理します。

相互(入れ替え)および循環的なタブの名前変更はサポートされている

  • 何が: Alpha→Beta + Beta→Alpha(入れ替え)や、より長い循環(A→B→C→A)は、一回のバッチ でステージングして反映できます——どちらの半分を先にステージングしても機能し、タブバーには入れ替えられた名前がただちに表示されます(WYSIWYG、undo 可能)。UI のゲートは 最終的な名前集合の一意性 を基準にしています(却下されるのは、二つの名前変更が同じ名前を対象にしているという 本物の 競合の場合だけです)。反映はこれを厳密に強制します。
  • 参照は名前ではなくデータ(タブの同一性)に追従します: A↔B の入れ替えの後、RecordId@A はアトミックに RecordId@B へ書き換えられます(一回限りの処理 — 二重に適用されることは決してありません)。そのため、B へ移動した同じデータを指し続けます。
  • ローカル: 入れ替えは、二つのファイルの内容を一回の書き込みで交換します。異なる拡張子 をまたいで名前を再利用する連鎖は、古い拡張子の使われなくなったファイルを削除します(パスに基づく削除ガード)。そのため、再インポートが重複したタブを目にすることは決してありません。
  • Google: タイトルの変更は トポロジカルに順序付けられ、一時的なタイトル(A→tmp, B→A, tmp→B)によってあらゆる循環を断ち切ります。そのため、本番シートが瞬間的にも重複したタイトルを持つことは決してありません。もしタイトル変更が途中で失敗した場合、一時的な名前のまま残されたタブは、復旧のためのガイダンスとともに報告されます。

Google 限定の特殊ケース: 互いを参照し合う入れ替えタブは、向き先を変更されない

  • 何が: 入れ替えられた 二つのタブが 互いを 参照している場合(タブ ARecordId@B 列を持ち、タブ BRecordId@A 列を持つ場合)、Google の経路は、その場でのタイトル変更によって内容を保持し、それら 自身の @type セルを書き換えることはありません——そのため、この相互の自己参照は、Google 上では向き先を変更されません。
  • なぜ: Google はタイトルを変更することでタブの名前を変更します(設計上、内容には触れません)。名前変更されたタブ自身のグリッドを書き換えてしまっては、それが台無しになります。ローカルソースは、名前変更されたタブのプロジェクションを書き換えるため、ローカルはこれを完全に処理します第三のタブからの 参照は、どちらの経路でも向き先が変更されます。
  • 回避策: Google 上では、相互参照を第三のタブ経由にするか、中間の名前を介して入れ替えを反映してください。

保留中の値の SO オーバーレイには、もはやボタンがない

  • 何が: 「保留中の値を SO 上にプレビューする」オーバーレイ(EphemeralSoApply)は、ワークベンチのボタンによって駆動されていましたが、そのウィンドウはなくなりました。この型は、それを必要とするツールのために公開 API として残っています。SO インスペクターの テスト編集 トグルが、ランタイムの数値を試すという日常的なケースをカバーします。
  • それでも呼び出す場合の制限: このオーバーレイは、(a) 保留中/新規の列と (b) パースに失敗したセルを、バッジとともに断ります。新規の はサポートされています。実際のパース + ベイクの経路を再利用しているため、正しく計算できないものは、偽装するのではなく断ります。
  • 回避策: これはあくまでプレビューでした。実際の変更には、通常通り反映を行ってください。再インポートは、常に真実の状態を復元します。

保留中の編集は、行が外部で名前変更・削除・キー競合した場合に分離される

  • 何が: ステージングと反映の間に、行が外部で名前変更された、外部で削除された、あるいはキーが競合した保留中の編集は、反映から除外され、「isolated」のバッジが付きます。
  • なぜ: その論理アドレスを再解決できないためです——ただし、それがサイレントに破棄されることも、セッションをブロックすることも許されません。
  • 回避策: 個別に破棄し(確認の上で)、再度ステージングしてください。

キー名変更の伝播は、baseline のセルのみを対象とする

  • 何が: 同じバッチ内で たった今入力した、古いキーを参照するテキストは、自動的には書き換えられません。
  • なぜ: ユーザーが入力したばかりの内容をサイレントに書き換えることは禁じられています。代わりに、事前検証が宙に浮いた参照を検出します。
  • 回避策: 保留中の参照を自分で修正するか、先に名前変更を反映してください。

以下の項目はすべて、唯一のオーサリングウィンドウである Data Studio に関するものです。より古いワークベンチウィンドウは削除されており、それだけが提供していた三つの機能は、先に Studio と設定インスペクターへ移設されています——ワークベンチはどうなったか を参照してください。

検証に失敗したシートは編集のために開くが、Export・Push・ビルドはブロックされたままになる

  • 何が: ソースを最後まで読み取れていれば、検証が失敗していても、そのシートは baseline として保存されます。そのため Studio はそれらを開くことができ、あなたはその場でエラーを修正できます。エラー件数がゼロになるまでコード生成とベイクは実行されず、そのシートがその状態にある間は Export・実運用シートへの Push・プレイヤービルドのすべてが拒否され、それぞれが理由を伝えます。
  • なぜ: その三つの出口はすべて、直前に 成功してベイクされた 値と、より新しい シートを組み合わせます。今それを実行してしまうと、誰かがすでに修正したセルの上に、古い値を継ぎ足してしまいます——サイレントなロールバックです。出口をブロックすることが、入口を開いたままにしておける理由です。
  • 修正を反映するときは一度だけ確認されます: quarantine されたシートでは、書き戻しに追加の確認が表示されます。pre-flight がそこでは厳格なゲートになれないためです(そのシートにはすでにエラーがあります)。見つかったものはすべて、その反映の中で警告として報告され、自動的な再インポートがシート全体を再検証します。健全なシートには影響しません——pre-flight は引き続き書き込みを拒否します。
  • 回避策: 報告されたすべてのエラーを修正し、もう一度 pull してください。このブロックは、それを解除するちょうど一箇所——ベイクまで完走した実行——で、自動的に解除されます。

Data Studio の並べ替えとフィルターは表示専用であり、有効な間は行の並べ替えを無効化する

  • 何が: Studio の シートごとの並べ替え(任意の列、昇順/降順、プロジェクトごとに保存)と テキストフィルター は、表示順序だけ を変更します。ガターは実際のシート行番号を保持したままで、どちらもステージング・反映・push・export には影響しません。並べ替えまたはフィルターが有効な間、行の ▲▼ 並べ替えツールはツールチップとともに無効化されます。
  • なぜ: ビューが並べ替えまたはフィルターされている間に「見た目の隣」で並べ替えると、ユーザーには見えていない行の隣に、サイレントに行を移動させてしまいます。本当の行順序の変更は構造操作です——まず並べ替え/フィルターを解除してください。
  • 注記: 「最新順で並べ替え」は、あなたのシートにそれを符号化する列(例えば IntId や日付のような文字列の列)がある場合にのみ成立します——シート自体はタイムスタンプを保存しません。

キーの名前変更がステージングされている間、Data Studio の Problems は下書きになる

  • 何が: キー(RecordId)セルに保留中の編集がある間、Problems パネルには draft バッジが付き、その中の未解決参照のエントリは誤報である可能性があります。
  • なぜ: メモリ上のプレビューは、キー名変更の伝播を適用しません——それは反映時に、すべてのタブにわたって実行されます。診断情報を隠したり、伝播を偽装したりする代わりに、ウィンドウは、名前変更が書き込まれるまでこのリストが下書きであることを伝えます。
  • 回避策: 名前変更を反映してください(伝播はそれ自身の確認とともに実行されます)。その後、更新されたリストを読んでください。

テーブルは200行を超えると行仮想化される — 知っておくべき二つのエッジケースとともに

  • 何が: 200行 を超えると、テーブルは表示ウィンドウ内の行要素だけを構築します(加えて、オーバースキャン用の十二行)。上下にはスペーサーが置かれ、真の合計高さを保持するため、スクロールバーが嘘をつくことはありません。ウィンドウの境界をまたいでスクロールすると、生き残った行は再利用され、新しく入ってきた行だけが構築されます。ブラウザー版のグリッドも、同じ閾値で同じことをします。

    それでも、二つのケースではすべてが構築されます。200行以下では、以前とまったく同じように、すべての行が構築されます。ビューポートの高さをまったく尋ねられないテーブル——ウィンドウの外にあり、レイアウトが決して届かない場合——も同様です。そこでは、正直なフォールバックは「すべて構築する」ことだからです。まだレイアウトされていないだけの大きなテーブルは、一フレームだけ待ちます。そのため、すべてを構築してから捨てるのではなく、最初の描画から仮想化された状態になります。

  • 編集中の行は、スクロールで画面外に出た後も生き続けます。 そのためキャレット、フォーカス、そして入力した内容は保持されます。この keep-alive には距離の上限があり、それを超えると、開いていたエディターは無期限に持ち運ばれるのではなく、コミットされてフォーカスを失います。そうなっても何も失われません——値はすでにステージングセッションの中にあります。

  • 仮想化されるのは要素の作成だけです。 列幅のサンプリング、検索、並べ替え、座標、そしてステージングのオーバーレイは、引き続きすべての行を考慮します。画面上のものだけを見ていたら、それぞれ異なる答えを返してしまうからです。そのため、非常に大きなシートに切り替えても、その規模に比例した処理は行われます——行わなくなったのは、何千ものウィジェットを構築することだけです。

  • ブラウザーでは、仮想化モードはレイアウトに任せる代わりに、列の幅を自ら計測します。 自動レイアウトの幅は、たまたまウィンドウ内にある行から計算されてしまうため、スクロールするたびに列がぴくぴくと動いてしまいます。仮想化モードでは、幅はすべての行にわたるデータ駆動の推定値から決まり、その後固定されます。フルレンダーモード(200行以下)は、引き続き自動レイアウトを変更なしに使います。

キャンバスは自身のスクロール範囲内でのみパンでき、循環を示すダッシュ線のワイヤーは、大きくなるほど粗くなる

  • 何が:

    • Ctrl/Cmd + マウスホイールで、レコードキャンバスを 25% から 200% の間でズームします。カーソルの下にある点は動かないままです(中心固定のズームだと、見ていたカードが画面外に滑り出てしまいます)。キャンバスヘッダーのパーセンテージ表示はボタンになっており、100% に戻せます。通常のホイール操作は、そのままスクロールです。
    • 中ボタンドラッグ——あるいは中ボタンのないハードウェアでは Alt + 左ボタン——でパンします。 押している間はカーソルがつかんでいることを示します。左ドラッグは選択とリンクのために残されているため、「ビューを動かす」ことも同時には意味できません。
    • このペインはスクロールビューなので、パンできる範囲はスクロール範囲そのもの です: 空白へ漂っていく代わりにコンテンツの端で止まり、コンテンツがビューポートより小さい場合はまったく動きません。これは無限のキャンバスではありません。
    • 循環を示すダッシュ線のワイヤーは、描画するダッシュの数に上限を設けており、長い経路ではダッシュの周期を倍にします。そのため、非常に長いループは、くっきりとではなく、粗く表示されます。
  • なぜ: Unity は、メッシュの頂点を 描画呼び出しごとに 割り当てており、そこには 65,535 という厳格な上限があります。それを超えると、テッセレーションのコストを払っているにもかかわらず、描画がまるごと消えてしまいます。ダッシュの上限は、一つの Stroke をこの予算の中に、設計によって収めています。

    背景のドットグリッドは、かつては同じ崖っぷちにありましたが、今はそうではありません。これは、頂点コストがゼロで、キャンバスがどれだけ大きくなっても一定時間で再描画される、小さな繰り返しの 背景タイル です。(パスとして描画される一つのドットは、四つの角ではなく、実測で 28 頂点のコストがかかります——これが、描画フォールバックの 1,800ドット予算の裏にある計算であり、このタイルが採用された理由です。)

  • 回避策: グリッドについては、何も必要ありません。広い範囲を見るには、一つの画面にすべてを収めようとする代わりに、ズームアウトする、方向のセグメントを狭める、あるいは隣接するノードを新しい terminus として開いてください。

まだテーブルがないシートは、インポートされるのではなくスキップされる

  • 何が: 必須の三つのマーカーのいずれも持たず、かつ データ行もないタブ——コメントや @style の行だけを持つ真新しいシート——は、三つのマーカー欠落でインポートを失敗させる代わりに、EmptyTabSkipped 警告 とともにスキップされます。すでに生成されているコード、ベイクされたアセット、アドレスは、タブが削除されたかのようにクリーンアップされるのではなく、保持されます。Export と Push も対称的にこれをスキップします。三者とも同じ述語を尋ねるためです。
  • なぜ: 一つの未完成のシートが、他のすべてのタブのインポートを止めてしまうことがあってはならず、また作者は通常、ヘッダー行より先にシートを作成するものだからです。
  • 境界: 中途半端に書きかけのシート(必須マーカーのいずれかが存在する)は スキップされません——正直に失敗します。サイレントにスキップしてしまうと、実際の作業が隠れてしまうためです。列 B ではなく列 A から型付けされたシートも同様にパーサーに渡されるため、その本当の診断(「列 A はマーカー列であり、データは B から始まります」)は失われません。

プラグイン C# から登録された enum は、シートからメンバーを増やせない

  • 何が: T がプラグインによって enums.Register<T>() で登録された Enum<T> は、コードが所有しています。enum 定義シートは、その名前を主張できず(DuplicateEnumName)、そのような列では、セルのドロップダウンの 「Add a new member…」 行が単純に存在しません。
  • なぜ: シートが正規であるのは、シートが定義するものについてだけです。もはやコンパイルされる型を決めていないシートにメンバーを書き込んでも、コードには決して現れないメンバーが生まれてしまいます——これは製品が守れない約束です。行が存在しないというのは、失敗するアクションを提供する代わりに、UI がそれを伝える方法です。
  • 回避策: シートに所有させたい場合は、その enum を enum シートへ移してください。あるいは、あなたのプラグインの C# 側でメンバーを追加し、再コンパイルしてください。
  • 構造も同じ所有権の境界に従います: enum 定義シートの構造は、両方のホストで完全にオーサリングできます——定義・名前変更・削除・列の並べ替え・基になる型と説明の編集——ただし、そのいずれもコードが所有する enum 名には触れられず、定義シートもその名前を主張できません。拒否にはその理由が添えられます。

enum 定義シート: メンバーは末尾への追加のみで、並べ替えはできない

  • 何が: enum シートの構造は、エディターとウェブアプリのどちらでもその場でオーサリングできますが、メンバー行は常に末尾への追加のみです——空いた場所へ後から埋め込むことは決してありません——そしてビューには並べ替えもフィルターもありません。
  • なぜ: メンバーの位置は、その整数値 そのもの です。空いた場所を埋めたりメンバーを並べ替えたりすると、すでにアセットやセーブデータに焼き込まれている値の番号を、サイレントに変えてしまいます。対照的に列の順序には意味がないため、列の並べ替えは常に許可されています。
  • 回避策: 値を明示的に固定したい場合は Name=value 構文を使ってください。それ以外の場所での表示順序は、シートではなく消費側の関心事です。

シート定義の enum は、常に設定フォルダーに生成される

  • 何が: 生成される タブ の型は、すでに存在しているフォルダーでそのまま再生成されますが、enum ファイル(SheetForgeEnums.cs)にはアンカーとなるタブが存在しないため、常に設定にある生成コードフォルダーへ書き出されます。生成コードが 自分自身のパッケージフォルダー にあるタブが、シート定義の enum を使っている場合、そのパッケージアセンブリは CS0246 でコンパイルに失敗します。
  • なぜ: enum はタブ単位ではなくプロジェクト単位の出力であるため、シート定義のすべての enum は一つのファイルにまとめられます——そのため、それが従うべき単一のタブのホームが存在しません。
  • 回避策: 両方の生成フォルダーを一つのアセンブリにまとめるか、代わりにその enum をプラグインコードから登録してください。この失敗は、欠落している型が名指しされた、目に見えるコンパイルエラーであり、サイレントな破損では決してありません。

型付きアセット参照は、読み込まれたタイプに対して解決される — 一意なときだけ短縮名、定義済みアセンブリのタイプは不可

  • 何が: AssetRef@Group<Type> は、プロジェクトが読み込めるあらゆる UnityEngine.Object 派生のアセットタイプを、エンジン組み込みか自作かを問わず受け付けます。許可リストはありません。次の三つは、推測されるのではなく却下されます: 複数の読み込まれたタイプが共有する短縮名(AmbiguousAssetType — インストールされているパッケージによっては TextAsset がその一つになり得ます)はフルネーム(UnityEngine.TextAsset)で書かなければなりません。未知の 名前は最も近い候補の提案とともに UnknownAssetType になります。そして 定義済みアセンブリ(Assembly-CSharp とその仲間——アセンブリ定義を持たないスクリプトフォルダすべて)に属するタイプは AssetTypeNotReferenceable になります。
  • なぜ: 生成されるコンパニオンアセンブリはアセンブリ定義であり、アセンブリ定義は定義済みアセンブリを参照できません——そのような T に対する AssetReferenceT<T> はコンパイルできません。曖昧な名前をどれか一つ選んで解決してしまうと、その列がサイレントに間違ったタイプへ結び付けられてしまいます。
  • 回避策: タイプをアセンブリ定義の中に移動するか、<…> の制限を外して無制限の AssetRef@Group のままにしてください。コンポーネントとエディター専用タイプは決して候補になりません。
  • また: ブラウザーはタイプ名を解決しません(解決対象となるプロジェクトを持たないためです): ウェブアプリは <Type> をパースして列のツールチップに表示しますが、三つの型名診断のいずれも生成せず、ピッカーもドロップも提供しません。コード生成は、未解決の名前をそのまま出力することは決してありません——解決できない名前は AssetReference にフォールバックし、AssetTypeUnresolvedFallback 警告が出ます。

アセットピッカー・ドロップ・ステージングされた登録 — 何が自動で、何がそうでないか

  • 何が: アセットをドロップまたはピックすると、アドレスは即座にセルへ書き込まれ、Addressables への変更(追加・移動・グループ作成)が反映に向けてステージングされます。この変更が実行されるのは シートの書き込みが成功した後だけ です——あるいは、ステージングされているのが登録だけである場合は、登録単独で実行され、その後に自動的な再インポートが続きます。タブがワークブック由来であるために書き込めなかった反映は、登録をステージングされたままにします。どのセルからも参照されなくなった登録は、セルが再入力されると取り消され、反映まで生き残ったものも unreferenced としてスキップされます。すでにそのグループにあるアセットは既存のアドレスを保持します。別のグループにあるアセットは、それを参照している他のセルの名前を示す確認の後にのみ移動されます。自動アドレスは拡張子なしのファイル名で、そのグループ内ですでに別のアセットが使っているアドレスはリネームではなく却下されます。新しいグループはデフォルトの BundledAssetGroupSchemaContentUpdateGroupSchema を得ます。適用されたものとスキップされたものは、理由とともに Console に記録されます。ローカルフォルダーソースの場合、反映の完了ダイアログはそのサマリー(Addressables: N registered, M skipped)を繰り返し表示します。
  • なぜ: シートが正規であるため——シートに到達しなかった反映によってプロジェクトが変わることは決してあってはならず、どのセルも指していないエントリは、シートが説明しない孤児になってしまいます。
  • 回避策: 登録がスキップされた場合、次の再インポートでそのセルが UnknownAssetKey として報告されます。原因を修正して、もう一度反映してください。登録には常に Addressables パッケージが必要です。

サブアセットは parent[sub] としてアドレス指定され、Sprite モードのテクスチャは <Sprite> を通過する

  • 何が: サブオブジェクトのエントリ(テクスチャ内のスプライト、フォント内のマテリアルなど)は、Addressables がそれに与える名前の付け方——parent[sub]——でアドレス指定され、そのキーはサブオブジェクト自身のタイプに対してチェックされます。親のアドレスは、自分自身のタイプ 、自分が含むすべてのサブアセットのタイプの両方を満たします。これが、Sprite モードでインポートされたテクスチャが <Sprite> 列を通過できる理由です。サブアセットをドロップすると、登録のために がステージングされ、セルには parent[sub] が書き込まれます。
  • 境界: parent[sub] が検証を通るためには、そのサブオブジェクトのエントリが Addressables のカタログに存在していなければなりません。ピッカーは、把握しているサブキーを、その親の後ろに一覧表示します。

カラーに HDR はなく、カーブのタンジェントはモードに従い、グラデーションは量子化される — 設計によるもの

  • 何が: Color は四バイトです——1 を超えるチャンネル(HDR)は、Export 時に 0…1 にクランプされます。辺が AutoLinearConstantClampedAuto のいずれかである AnimationCurve のタンジェントは、インポート時にモードから再計算されます。そのため、モードと矛盾する手入力の数値は置き換えられます(モードが適用されるときに Unity が行うのと同じ再計算です)。OnceClampForever として読み取られ、書き戻されることはありません。キーを持たないカーブにはテキスト形式が存在せず、任意列の空セルとしてのみ存在します。Gradient のキーの時間は、インポート時に(Unity が保存する方式そのままに)16 ビットへ量子化され、単一のキーを持つグラデーションは Unity を経由すると同一の二つのキーとして戻ってきます。色空間は、設定されていた場合にのみ書き込まれます。
  • なぜ: シートが示す値は、エンジンが保持する値と一致していなければなりません。そのため、本来 Unity が後で行うはずの正規化を、入口で一度だけ行い、シート・エディターのフィールド・ウェブのプレビュー・ベイクされたアセットというすべての面が、同じ一本のカーブと同じ一つのグラデーションを示すようにしています。
  • 回避策: タンジェントの数値をそのまま使いたい場合は Free/Free を使ってください。HDR の強度は、別の float 列に保存してください。

チップエディターとネイティブフィールドが存在するのは、三つの視覚型だけである

  • 何が: Data Studio は ColorAnimationCurveGradient のスカラーを Unity 自身のフィールドとして、それらの List<> をチップエディターとして表示します。ウェブアプリは、それ自身のエディターとチップリストによるプレビューを表示します。それ以外のすべてのリスト列——List<int>List<Enum<…>>、ラッパーのリスト——は、両方のホストで正規のテキストのままです。三つのうちのいずれかを含むラッパー(Pair<Color>)もテキストのままです。
  • なぜ: その三つの型だけが、要素ごとに絵を持つ型だからです。それ以外については、一本の正規のテキスト行がすでに最も正確な表現であり、ラッパーの外側の記法はそのプラグインが所有するものです。
  • 余地: その三つの値のいずれかを保持するプラグインの型は、対応する StudioCellEditorHint のアーキタイプを宣言することで、同じエディターを採用できます(プラグイン作成 §4.16 を参照)。

所有者の色分けは、シート対コードのみ

  • 何が: サイドバー/凡例は、正確に二つの由来——実在のシートタブと、コードレジストリの仮想タブ——だけを区別します。三つ目の「生成された」という分類はなく、列ごとの所有者による色分けもありません。
  • なぜ: 由来は、ウィンドウがすでに確実に知っているタブそのものから導かれます。列ごとの分類には、それを正直なものにするための、また別の拡張ポイントが必要になり、それを求めた利用者はいませんでした。
  • シートの色とは別物です: @style は、シートが自身の色を名付けることを可能にしますが、その色は作者が選んだ表示用のメタデータであり、そのシートがどこから来たかについては何も語りません。この二つの色分けは、別々の場所から読み取られ、決して混ざり合うことはありません。

参照ドロップダウンが答えるのは所属であり、順序ではない

  • 何が: RecordId@Tab セルには検索可能なドロップダウン(List<> の場合はチェックリスト)がありますが、リストセルのドロップダウンは要素の追加と削除しかできません——移動はできません。リストの並べ替えはキャンバス上で行い、そこでは各要素が自分自身の行を持ちます。
  • なぜ: ドロップダウンが答えるのは「ここに何が入っているか」であり、「どの位置か」には、物事を並べるサーフェスが必要です。それはすでにキャンバスが担っています。二箇所に重複させると、維持すべき答えが二つになってしまいます。
  • また: このドロップダウンは、ラッパーではない通常の参照列にのみ付きます。ラッパーセルのテキストはラッパー自身の記法を運んでいるため、そこに裸のキーを貼り付けてしまうと値が壊れてしまいます——ラッパーの内部に到達するのは、登録済みのセルウィジェットの仕事です(プラグイン作成 を参照)。

All 検索は、エディターではベイクされたデータを、ブラウザーではライブセッションを読み取る

  • 何が: Data Studio の All エントリは ベイクされた データベースを検索するため、最初に一度成功したインポートが必要で、インポートが完了したときやアクティブな設定が変わったときに自動的に更新されます。ウェブアプリの All エントリは、そのセッションが今まさに表示している値——ステージング済みの編集も含みます——を検索します。マッチング、結果の順序、50 行のページはどちらも同じコードであり、どちらも結果をダブルクリックする(または選択して Enter を押す)と、一致したセルが選択された状態でそのシートが開きます。
  • なぜ: ブラウザーにはベイクされた ScriptableObject がありません——あるのはライブセッションであり、画面上の値で答えることこそがブラウザーセッションの本分です。エディターは、すでに手元にあるベイクされた真実を読み取り続けます。
  • 境界: エディターでは、ステージングされてはいるがまだ反映されていない編集は、インポートが実行されるまで All に見つかりません。また、セッションが読み込んでいないシートからの結果は遷移せず、一覧の下の注記がその理由を伝えます。ブラウザーでは、ステージングされた編集は即座に見つかり、どの結果にもジャンプできます。

5. パフォーマンス

  • 通常の経路は線形で高速です: 50,000 行 × 20 列で約 628 ms(実際のエディター、Mono。ヘッドレスでは 144 ms)。180k 件の参照セルを含む 50 タブ × 2,000 行で約 294 ms。典型的なプロジェクトの規模では、まったく問題になりません。
  • メモリ使用量は線形ですが、ボクシングが多く発生します: 保持されるメモリは約 59 バイト/セル(インポート中のピークは約 138)。Googleシートのセル数の上限(約10Mセル)で外挿すると、インポートに約 6.3 秒、保持メモリ約 590 MB、ピーク約 1.4 GB になります——極端な規模では、低スペック/32ビット環境に注意してください。(列指向の IR は、認識済みのバックログ項目です。)
  • オーサリングテーブルは、200行を超えると行仮想化されます——エディターでもブラウザーでも同様で、大きなシートを開いても、もはや行ごとにウィジェットを構築することはありません。仮想化 されない のは、画面上のものだけを見ていたら異なる答えを返してしまう、行単位のロジックです——幅のサンプリング、検索、並べ替え、座標、ステージングのオーバーレイです。詳細と二つのエッジケースは §4 にあります。
  • エラー経路も線形です: 参照が 大量に 壊れる場合でも、最も近い候補の計算は有界のままです——フィールドごとの提案予算と、長さによる事前フィルタリング付きの早期終了型編集距離が、壊れた参照の件数に対しておおむね線形になるよう保っています(壊れた参照 4,000 件で約 45 ms、ヘッドレス。同じ規模で有効なデータの場合は約 2.7 ms)。参照先のタブを名前変更しても、そもそも参照が大量に壊れることはありません——名前変更によって、参照している @type セルが書き換えられるからです。

6. デモシーン

  • サンプルは選択的インポートです — 二つのデモサンプルとそのシーンは、デフォルトでは存在 しません。これらは Unity パッケージ(Assets/SheetForge/Examples/SheetForgePluginDemo.unitypackageSheetForgeCoreDemo.unitypackage)として同梱されています。どちらか一方をインポートすると(ダブルクリック、または「はじめに」ウィンドウの Import Plugin/Core Demo ボタン)、Assets/SheetForge.PluginDemo/… または Assets/SheetForge.CoreDemo/… が復元されます。それまでの間、サンプルはプロジェクトの中にはまったく存在しません——これらはそのパッケージとしてのみ提供されます——そのため、あなたのプロジェクトと衝突することは決してありません——コアの製品は、これがなくても完全に自己完結しています。
  • まず一回のインポートが必要です(マシンごとに)——デモが読み込む Addressables アドレスは、コミットされていないキャッシュです。それより前は、ガイダンスメッセージが表示されます。
  • デモに名前空間の設定は不要です — コミットされているサンプルの型は、Example* というクラス接頭辞を持つデフォルトの SheetForge.Generated 名前空間を使うため、generatedNamespace の設定なしに、デモを再インポートすればそのまま再生成されます。

7. プラグイン拡張 — 提供済みの継ぎ目(境界あり)と、依然として予約されているもの

十六の拡張契約が提供されており、そのすべてが Core への変更ゼロで参加します——完全な一覧は プラグイン作成 にあります。十六すべてが発見(Unity の TypeCache、ブラウザーのアップロード済みアセンブリスキャン)によって見つけられ、アセンブリ参照も編集すべきマニフェストも不要です。そのうち十一の Core 契約は、追加する内容を登録するためのレジストリを渡され、一方で五つの Editor 契約——グラフウィジェット・インスペクターアクション・セルエディタープロバイダー・パネルプロバイダー・ソースプロバイダー——は、単に発見されそのまま使われます。

提供済みの継ぎ目のうち五つには、見つけてもらうのを待つのではなく、ここで述べておく価値のある境界があります:

カスタムセルタイプへの参照 — 自前の記法に完全な RecordId@Tab 整合性を

  • 何が: IReferencingCellType も実装している登録済みのセルパーサー(そしてその値が IRefBearingValue を実装している場合)は、自身の記法の内部に埋め込まれたキーを、どう読み取りどう書き換えるかを Core に伝えます。その列は、組み込みの参照が得るものすべてを手に入れます:

    • 最も近い候補の提案付きの整合性チェック。
    • ペイロードを保ったままのキー名変更伝播(attack:add:10power:add:10)。
    • グラフのエッジとポート、 ピッカー、被参照数。
    • 孤立検出、エクスポートされるドロップダウンルール。

    発見は、すでに登録済みのパーサーをキャストすることによって行われます——新しい登録経路はなく、これを実装しないカスタム型は、ビットレベルで以前と変わりません。プラグイン作成 §4.4a を参照してください。

  • 境界: ペイロードに ; を含めることはできません——Core は、あなたのパーサーがテキストを見る前に、リストセルを要素に分割するため、値の中のセミコロンは二つの要素に分断されてしまいます(ラッパー型も同じ制約を持ちます)。そして @target実在するシートのタブ を指していなければなりません: コードレジストリの仮想タブは、RecordId@Tab の場合とまったく同じように、UnknownTargetTab として拒否されます。この制限があるからこそ、Core 自身の未解決参照の報告と名前変更の伝播が、変更なしに適用されます。

<> ラッパー型(MyWrapper<T>)— 却下ルールあり

  • 何が: プラグインは、ICellWrapperType を通じて汎用の値の形(例: Pair<int> = 1~2)を登録します。内側の型は Core が再帰的に解決します(シート構文 を参照)。

  • 却下ルール:

    • Pair<List<T>> は却下されます——リストはラッパーの内側に置けません。List は常にフラットかつ最も外側です。
    • Pair<int>@Tab は却下されます——@ は内側の末端に置いてください: Pair<RecordId@Tab>
    • Pair<int?> / Pair<int=1> は却下されます——省略可否/デフォルト値はフィールドレベルのものであり、内側の型の一部ではありません。

    List<Pair<T>> は許可され ます が、ラッパー自身の区切り文字は ;(リストの区切り文字)とは異なる必要があります — これは、Core が強制できない、プラグイン作成側の責任です。

カスタム構造マーカー(@yourMarker)— 列ごとのメタデータ専用

  • 何が: プラグインは、IStructuralMarkerDefinition / ISheetForgeMarkerPlugin を通じて @marker 行を登録し、@overlap の列ごとの検証を一般化します。その値は FieldSchema.MarkerValues のメタデータとして保存されます。
  • 境界: マーカーが持つのは、列ごとの値の検証 だけです——行全体のデータの形をパースすることを乗っ取ることは ありません(データの形を表現する方法は、引き続き正規化です)。そして、コード生成はマーカーの値をベイク しません: @overlap と同様、これらは検証/表示用のメタデータに過ぎず、スキーマフィンガープリントからは見えません——そのため、マーカーに関連する何かが、生成コードやベイクされた SO に到達することはありません。

カラープリセット — 私たちが描画するサーフェスであり、Unity のウィジェットではない

  • 何が: プラグインは、ISheetForgeThemePlugin / ThemeRegistry を通じてカラープリセットを登録します。それは組み込みの DefaultHigh contrast プリセットの隣に、Preferences ▸ SheetForge ▸ Theme に表示され、ユーザーがそれを選んだ場合にのみ適用されます(登録しただけで画面を乗っ取ることはありません)。プリセットは、自身が名指ししたスロットだけを上書きします——それ以外のスロットはすべて製品のデフォルトのままなので、スロットが追加されてもプリセットは有効であり続けます。
  • 境界 — 混在した見た目は想定内です: テーマがカバーするのは、SheetForge 自身が描画するもの(ウィンドウの背景、ヘッダー、テキスト、アクセント、グリッドとステージングの色)です。それらのウィンドウの内部に描画されるネイティブな Unity ウィジェット——ボタンの外枠、フィールドの境界線、ポップアップの矢印——は、引き続き エディターのスキン に従います。これは Unity がパッケージによる再スタイルを許可していないためです。そのため、エディターがダークスキンで動作している間に Always light を選ぶと、ライトな SheetForge のサーフェスの上に、ダークなネイティブウィジェットが乗った状態になります。統一された見た目にしたい場合は、エディターのスキンを合わせて設定してください。
  • 境界 — 色のみです: プリセットが持つのは色(スロットごとの 0xRRGGBB)だけです。間隔、フォントサイズ、レイアウトはテーマの対象外であり、半透明の塗り(バッジの背景、モーダルのスクリム)は、個別に設定できるのではなく、スロットの色に固定のアルファ値を組み合わせて導出されます。

宣言的オーサリングサーフェス — 意図的に境界を定めた語彙

  • 何が: ISheetForgeStudioPlugin を使うと、パックは動詞・パネル・列バッジ・セルエディターの形状を データとして 記述でき、一度の登録がエディターとブラウザーの両方で描画されます。この語彙は固定されており、追加によってのみ成長します: 五つのアクション配置、十三のノード種別、七つのセルエディター・アーキタイプです(最新の二つ、CurveEditorGradientEditor は、組み込みのカーブ型とグラデーション型が使うものです)。
  • 境界 — これは UI フレームワークではありません。 任意の描画、複合的な入力、複数ステップのフローには、ここに語彙がなく、それらを追加することは、永遠にミニチュアの UI ツールキットを保守し続けることを意味します。それが IStudioPanelProvider の存在理由です: 記述済みパネルと同じ id で登録すれば、エディターはリッチな方を描画し、ブラウザーは記述的な方を描画します。Web 専用 のエスケープハッチはありません——ブラウザーは UIToolkit の型を読み込めず、そうでないふりをすることは、プラグインの拡張を一方の画面だけに置くことになってしまいます。
  • 境界 — アクションの権限は、正確に四つだけです: 一つのセルをステージングする、複数のセルを一つの Undo ステップとしてステージングする、レコードにフォーカスする、再描画を要求する。そのためプラグインの動詞は、手入力のものと同じゲート・pre-flight・push を通過する、通常のステージング編集です。オーサリングセッション自体は、意図的に公開されていません。
  • オブザーバーについては、ゼロ発火であり、誤発火は決してありません: IPipelineObserver は、明示的なインポートサイクルの終了時に発火します。二つの経路は、そこに一切到達しません——パイプラインが始まる前に止まった実行(アクティブな設定がない、Addressables が未インストール)と、コンパイルエラーによって中断された codegen → コンパイルの区間です。「インポートが試みられたこと」を知る必要がある場合は、これを Editor 側の ImportEvents バスと組み合わせてください。

設計はされているが、まだ実装されていないもの(利用者がまだいない)

  • 行全体のデータ形状マーカー(例: 2次元の行列を一つのフィールドとして読み取るマーカー)— 意図的に実装されていません。マーカーが持つのは列ごとの検証であり、行のパースではありません。そして正規化(参照 + type 列 + List<T>)は、表現力の面で完全です。MarkerRegistry の登録用の継ぎ目自体は提供されています。予約されているのは、この 形状のパース という解釈だけです。
  • カスタムマーカーの値を生成コードに焼き込むこと — マーカーのメタデータをコード生成の定数/属性として必要とする利用者が現れるまでは、対象外です。
  • レコード単位 / 遅延読み込みの SO コンテナ — 設計は完了していますが、実装されていません。現在のコード生成が作るのは、タブ単位の一括読み込み Database SO のみです。

8. スキーマの進化

  • Export/Push は、スキーマ変更後に新しいベイクを必要とします — 古いベイクのままだと ExportSchemaMismatch(フィンガープリントの不一致)で失敗します。この拒否画面は、いまではそのインポートを代わりに実行することを提案してきます:確認を一度行うだけで開始し、そのあとに自動で Export や Push が実行されることはありません——インポートが完了したら、元々行おうとしていた操作をもう一度押してください。
  • スキーマ変更後の最初のインポートは、内部的に二段階です(コード生成 → コンパイル → ベイク)——自動的で、ユーザーの操作は一回だけです。それを止めるのはコンパイルの失敗だけです(安全な中断、実行可能な文、試行上限3回)。

9. ローカライズの範囲

レポートの 詳細断片(問題のある値、提案)、低レベルの例外、開発者向けログは、ローカライズされたレポートの骨格の内側で、英語のまま埋め込まれます——ランタイムで埋め込まれる内容は、言語テーブルのキーにはなり得ません(業界標準の境界です)。エディターが描画するものすべてと、レポートの骨格、そして「なぜ」/「どうすればよいか」の文は、10 言語すべてで完全にローカライズされています。

十言語すべてが完全に翻訳されています。 すべての言語テーブルのすべてのキーが、実際の翻訳を保持しています——Data Studio、テーマ設定、ダイアログ、ログの行も含めてです。十のファイル間のキーのパリティはテストによって強制されているため、生のキーへのフォールバックも、プレースホルダーの破損も起こりません。

言語ごとにごく一部の項目が英語版とまったく同じに読めますが、これは翻訳待ちではなく翻訳上の判断です: 記号やプレースホルダー専用の文字列(+)、固有名詞やフォーマット名(Google SheetsSHA-256)、そしてその言語でも実際に英語と同じ綴りになる語(OKAlpha)がそれに当たります。

プラグイン自身のラベルは、そもそも Core のテーブルには一切含まれません: ISheetForgeStringsPlugin で登録すればユーザーの言語に追従し、登録しなければそのまま逐語的に表示されます。

10. エディターでの操作

  • Ctrl+Z の範囲: フォーカスされたテキストフィールドが、Ctrl+Z を最初に消費します(OS の標準的な挙動)。反映が成功した後、ステージングの履歴はクリアされます——undo が、すでにシートに書き込まれた内容にまで及ぶことは決してありません(シートが正規だからです)。
  • 言語の変更は、Import/Export/Push の実行中はロックされます(メニュー再生成の再コンパイルを引き起こすためです)。テーマの変更はロックされません——テーマは再コンパイルを一切引き起こさないため、明るさとプリセットは、実行中も含めていつでも切り替えられます。
  • テーマと言語は、プロジェクトごとではなくユーザーごと(EditorPrefs)です——チームメイトはそれぞれ自分の設定を保持し、どちらもバージョン管理には現れません。デフォルト以外のカラープリセットを選ぶと、Assets/SheetForge/Editor/Generated/(gitignore 対象、自己修復)の下に、生成されたスタイルシートが一つ書き出されます。デフォルトのプリセットは何も書き出さず、それを削除します。
  • 生成コード + ベイクされた SO + Addressables グループは、マシンごとの、gitignore 対象のキャッシュです — 各マシンは Run Import を一回実行します。ゲームコードはアドレスで読み込み、シーンからの直接参照は決して使いません。

11. ライセンス

リポジトリには LICENSE 通知が同梱されています: Unity Asset Store の EULA が支配的な契約です。リポジトリの閲覧に関する通知もあります(ソースは参照用途、およびライセンスを取得した購入者のために閲覧可能です。書面による許可なしに、EULA の範囲外での再配布/再販売はできません)。サードパーティのコード: 一切ありません — 自前で実装した OOXML の xlsx リーダー/ライターも含めてです。

12. 検証済みの状態(リリース時点)

  • 二つのテストハーネス: ヘッドレス .NET テスト 2,150 件(2,150 件成功)+ EditMode テスト 3,021 件(3,021 件成功、失敗 0 件、スキップ 4 件)。この二つの数値が、件数が示される唯一の場所です。他のすべてのページはここにリンクしています。
  • この四件のスキップは、実際の Google とのラウンドトリップ に関するもので、サービスアカウントの認証情報が環境に存在するときにのみ実行され、今回の実行ではスキップされました。認証情報がある場合は、本物のスプレッドシートに対して繰り返し確かめられています——fetch → push、行が移動した場合の競合検出、ロケールに依存しない浮動小数点数の扱い、タブをまたぐ混在バッチ、そして一回の送信につき、一つにまとめられたレポートと、ちょうど一回の自動再インポートを検証する、ディスパッチャーの完全な実行を含みます。結果: 4/4 件成功。
  • Web アプリには独自のゲートがあり、すべて成功しています: 型チェック、lint、単体テスト 414 件、WebAssembly の publish + スモークラン(実際のプラグイン DLL を読み込みます)、プロダクションビルド、エンドツーエンドのブラウザーテスト 56 件、そしてクロス言語の定数チェック(Unity↔Web の六つのペア: ホストバージョン、プラグインフォーマット、レジストリのスキーマバージョン、OAuth スコープ、ホストアセンブリ名、行ウィンドウの閾値)。
  • コンパイルされるすべての製品 asmdef: エラー 0、警告 0。(サンプルの asmdef——各デモにそれぞれ二つ——は、デモパッケージをインポートして初めて存在し、それまではコンパイルされません。)
  • ガードテストはすべて成功: 製品のソースに韓国語のリテラルはゼロ、カーネルの継ぎ目にドメイン語彙はゼロ(どちらも /Samples~/ はスキップします — サンプルはドメインのコンテンツだからです)、10 言語のキーは整合しています。
  • no-IVT のコンシューマーシミュレーションアセンブリは、公開 API のみに対してコンパイルされます(コンパイラによって強制されています)。そこにある no-IVT のミニプラグインプローブは、公開サーフェスのみを使って、十六のうち十五 の拡張契約(ISheetForgePlugin / validator / edge / marker / template / graph / code registry / theme / studio UI / strings / pipeline / ISheetSourceProvider / Studio widget / Studio inspector action / Studio cell editor)を実装しています——そのため、Plugin Demo サンプルがコンパイルされていない状態であっても、契約が公開されていることは証明され続けます。十六個目の IStudioPanelProviderVisualElement を返すため、代わりに Editor 側のテストで検証されます。同じプローブは、IReferencingCellType / IRefBearingValue を含む、オプトインの capability インターフェースも実装しており、公開サーフェスを通じてそれらを実際に動かしています(キャストによる発見、五つのフックすべて、残りの部分の保存)。

Asset Store への申請時にのみ検証できる項目(リポジトリ内では検証できません): .unitypackage のインストールプロンプトの挙動、Portal の依存関係宣言、配布パッケージからのテストアセンブリの除外、そしてクリーンなプロジェクトでの警告0件の再チェックです。

13. ローカライズシート(ゲームのテキスト)

ローカライズシート と Unity Localization ブリッジの境界を、正直に述べます。(パッケージ自体は任意です——§1 を参照。)

ブリッジは一方向であり、外部でのテーブル編集は尋ねられるだけで、決してマージされない

  • 何が: 同期は シート → StringTable の方向にのみ流れます。ブリッジは自分が所有するテーブルに印を付け、同期のたびにフィンガープリントを記録します。それ以降に何か他のもの——Localization Tables ウィンドウ、Unity 自身の Google Sheets 拡張、XLIFF インポート——がテーブルを編集していた場合、次の同期は 止まって尋ねます: シートから上書きするか、差分レポートとともに中止するかを。
  • なぜ: 同じデータに対して二つの書き込み可能な操縦席があると、サイレントな上書きに行き着きます。シートが正規であるため、もう一方の操縦席は、サイレントではなく明示的でなければなりません。
  • 回避策: 翻訳をシート経由に一本化してください——翻訳ワークブック(xlsx エクスポート + ロケールのみの部分的な再インポート)は、まさにそのために存在します。

Studio の外でのキーのリネームは、削除と追加を意味する

  • 何が: Data Studio 内で キーをリネームすると、LocalizedString の参照が結び付く内部 id を保ったまま、テーブルのエントリがその場でリネームされます——シーンの参照は生き残ります。シートのソースを直接(Google Sheets、Excel で)リネームすることは、一つのキーを削除して別のキーを追加することと見分けがつきません: ブリッジは新しいエントリを作成し、古いエントリは孤児になり、シーンの参照は依然としてその孤児を指したままになります。
  • なぜ: テキストレベルの差分比較では、推測なしにリネームと削除+追加を区別できず、推測を誤れば参照がサイレントに再結合されてしまいます。
  • 回避策: キーは(どちらのホストであれ)Data Studio 内でリネームしてください。外部でのリネームの余波は、孤児レポートが捕捉します。

テーブルの孤児キーは、デフォルトでは保持される

  • 何が: テーブルには存在するがシートにはもう存在しないキーは保持され、孤児として報告され、明示的なクリーンアップ操作を通じてのみ削除されます——あるいは、同期時に削除する 設定をオプトインしていれば自動的に削除されます。副作用として削除されるものは何もありません。
  • なぜ: シートの行が欠けているのは、編集途中のミスである可能性があります。それを理由に翻訳を破壊してしまえば、取り返しがつきません。

文字列テーブルのみ——アセットテーブルは対象外

  • 何が: ブリッジが埋めるのは StringTable コレクションです。Unity Localization の AssetTable の軸(ローカライズされたスプライト、オーディオ、プレハブ)は、シートから同期されません。これは認識済みのバックログ項目です。
  • 回避策: アセットテーブルはパッケージ自身のツールで管理してください。ブリッジはそれらに触れません。

XLIFF と疑似ロケールは再実装されない

  • 何が: 同期されるテーブルは通常の Unity Localization テーブルであるため、パッケージ自身の XLIFF エクスポート/インポートや疑似ローカライズは、変更なしにそれらの上で機能します。SheetForge は二つ目の実装を追加しません。
  • 境界: それらのツールがテーブルの 中に 書き込んだ出力は、外部編集として扱われます(上の一つ目の項目)。シートを正規の情報源に保ち、翻訳ワークブックを経由して翻訳を運んでください。

言語固有の Smart Format ヘルパーは同梱されない

  • 何が: smart 列はエントリを Smart String としてマークしますが、SheetForge 自身の文法フォーマッターは提供されません——例えば韓国語の助詞選択は、意図的に含まれていません。
  • 回避策: パッケージの Smart Format 拡張ポイントは、あなたが書くフォーマッターのために完全に利用可能なままです。

キー定数は ASCII にサニタイズされる

  • 何が: 生成される {Tab}Keys 定数は、ASCII の英字・数字・_ 以外のすべての文字を _ に変換します(衝突には数値の接尾辞が付きます)。そのため非 ASCII のキーは、読みにくい定数名になります——キー自体はどこでも引き続き機能します。
  • 回避策: 定数を使うのであれば、キーは ASCII のままにしてください(ui.okdialog.intro)。シート定義の enum ファイルと同様に、定数ファイルは常に設定フォルダーに生成されます——同じアセンブリの注記が当てはまります。

ウェブアプリはローカライズシートを作成できるが、同期はエディターの仕事である

  • 何が: オーサリング、検証、カバレッジ、ミンティング、ロケールレンズ、翻訳ワークブックは、すべてブラウザーで動作します。StringTable への書き込みはそうではありません——ブラウザーには、書き込み先となる Unity プロジェクトがありません。
  • なぜ: これは欠けている機能ではなく、正直なスコープです: テーブルはプロジェクトの中に存在するものだからです。

関連ページ