本文へスキップ
SheetForge

ソース・エクスポート・プッシュ

三つの書き込み経路が存在し、それぞれ異なる対象を持ちます:

  • 反映 はオーサリングのステージング内容をソースに書き込みます。
  • Export はベイクされた SO の値をシートファイルに書き戻します。
  • Push はベイクされた SO の値をセル単位で本番の Google シートに書き込みます。

インポートソース

インポートソースは、設定アセットにおける第一級の選択肢です。各ソースは、自身のオーサリング能力(CanAuthor)を宣言します。

ソース読み取る内容オーサリング(書き戻し)
LocalFile.tsv / .csv / .xlsx ファイルのフォルダー(直下の子のみ。一ファイル = 一タブ、xlsx のワークブックはその中のシートを提供します)フル対応 — 反映、構造編集、キー/タブの名前変更
GoogleSheet · SheetsApiサービスアカウントの JWT 認証(設定ガイド)による、プライベート/共有のスプレッドシートフル対応 — 精密なセル単位の書き込み、構造の書き換え、Push
GoogleSheet · ExportUrlエクスポート URL によるリンク共有シート — 認証不要読み取り専用(CanAuthor = false)— Push/反映/構造編集/削除は、説明とともに無効化されます
カスタムプロバイダープラグインが登録するあらゆるもの(ISheetSourceProvider — DB、REST、独自形式)プロバイダーの CanAuthor フラグによる選択

補足:

  • ExportUrl は gid マップを必要とします(タブ名 → #gid= の値)——gid のないエクスポート URL は、サイレントに最初のタブだけを返してしまうため、このマップは強制されます(GoogleSheetGidMapMissing、重複した gid は却下されます)。SheetsApi モードはタブを自動的に検出するため、マップは不要です。
  • 組み込みの xlsx リーダー/ライターは、自前で実装した OOXML です(System.IO.Compression + System.Xml のみ — NPOI や ClosedXML は使わず、サードパーティコードはゼロです)。そのため、プロジェクト内の他のアセットと衝突しうる DLL を一切追加しません。これは一つのコーデックを共有しています: 同じリーダーが Unity エディター上で、そして——WebAssembly にコンパイルされて——Web アプリ上でも動作するため、二つのホストがあるセルについて食い違うことはありません。これは意図的に最小限に留められており、そのことに正直です——値のみを扱い、再計算はしません:
    • 数式セルは、ファイルにキャッシュされた値をそのまま提供します。 キャッシュされた値を持たない数式と、エラーセル(#REF!#DIV/0!)は却下されます(UnsupportedXlsxCell)——Excel でワークブックを一度保存して値をキャッシュするか、数式を実体化してください。
    • 日付書式のセルは、その日付として読み取られ、yyyy-MM-dd として描画されます——ISO のセル型と、スタイルが日付書式であるプレーンな数値のどちらも対象で、1900年方式と1904年方式の両方の日付システムが尊重されます——ファイルが保存している生のシリアル番号の代わりにです。それ以外の数値書式、結合セル、チャートはインポートされません。
    • これらの解釈——キャッシュされた数式の値、日付を表示テキストとして扱うこと、書式を無視すること——は、両方のホストにおけるリーダーの固定されたポリシーであり、Web アプリのインポートダイアログはさらに、実際に発生したものだけを 「How this workbook was read」 という注記の中で名指しします。
    • セル内のタブ文字や改行は却下されます(UnsupportedCellCharacter)——リストには ; を使ってください。
    • リーダーが認識しないセルタイプコードは、却下されるのではなく、生の保存テキストとしてそのまま読み取られます。
  • ローカルファイルは Unicode である必要があります。 UTF-8 の BOM、または UTF-16 の BOM(LE または BE)は尊重されます。BOM がない場合、ファイルは厳密な UTF-8 としてデコードされます。CP949 や Shift-JIS のようなレガシーな単バイトエンコーディングは、推測されるのではなく 却下されます(UnsupportedEncoding)——推測してしまうと、マシンによって異なる結果にデコードされ、サイレントにデータが破損してしまいます。ファイルを UTF-8 として保存し直してください。
  • ソースは 部分的な出力 を返すことができます——一つの壊れたファイルが、読み取り可能な他のタブを道連れにすることはありません。問題は診断情報として届きます。
  • カスタムソースプロバイダーは自動的に検出され、同じ設定のドロップダウンに表示されます——プラグイン作成 を参照してください。
  • ソースがあなたの知らないうちに変わっていたら、スタジオが教えてくれます。 ウィンドウにフォーカスが当たったとき——または ⋯ メニューから要求したとき——スタジオはソースを再読み込みし、直前のインポートのスナップショットと比較して、データが実際に異なる場合にのみバッジを表示します:保存し直しただけ、あるいは書式が変わっただけのシートは静かなままです。比較の基準がタイムスタンプではなく内容だからです。バッジをクリックするとインポートの実行が提案されます。タイマーでポーリングするものは何もなく、自分でインポートするものも何もなく、オフラインまたは未認証の場合は単にバッジが表示されないだけです。これはすべての種類のソースで同じように機能します——ローカルファイル、エクスポート URL のシート、Sheets API のいずれも同様です。

Export — ラウンドトリップの復路

Data Studio ツールバーの ⋯ ▸ エクスポートを実行 は、ベイクされた SO の値をシートファイルに書き戻します。

  • 構造は baseline から、値は SO から取得されます。 Export は、シートの構造の baseline スナップショットに、現在の値を差し込みます——マーカー行、列の順序、コメント、手書きのテキストは 100% 保持されます。
  • 意味的な値のラウンドトリップ: 1.01 の正規化は許容されます(値が同一であるため)。浮動小数点数は最短のラウンドトリップ形式を使い、小数点は常に . です。
  • フォーマット: Tsv / Csv / Xlsx / Json / MatchSource(各タブは、インポート元だったフォーマットに戻ります。Google 由来または不明な場合は Tsv にフォールバックします)。Json は、スプレッドシートではなく機械のための、書き出し専用のフォーマットです:タブごとに一つのファイル、レコードはオブジェクトとして、int / float / bool は実際の JSON の数値と真偽値として、それ以外のすべての値——参照、リスト、色、カーブ、カスタム型——はシートが保持している正規のセルテキストそのままで出力されます。そのため、サーバーや外部ツールはシートのテキストをパースすることなくゲームデータを消費できます。JSON はインポートソースではなく、JSON ファイルはラウンドトリップ可能なシート構造を一切持ちません——シートが正規であり続けます。TSV と CSV はタブごとに一つのファイルを書き出します。Xlsx は、エクスポートされるすべてのタブを一つのワークブック(SheetForge.xlsx)にまとめ、各タブをタブの順序どおりに、それぞれ独自のシートとして書き出します——ワークブックは複数のシートを収めるためのフォーマットであり、それらを一つにまとめておくことは、参照ドロップダウンがシートをまたいで指し示せることの理由でもあります(下記参照)。MatchSource では、xlsx 由来のタブはその一つのワークブックに集められ、それ以外は自分自身のファイルに戻ります。ワークブックの規則が扱えないシート名(長すぎる、または禁止された文字を含む)は調整され、その名前とともに報告されます——サイレントに改名されることは決してありません。
  • 鮮度が強制されます: スキーマ変更の後、古いベイクのままエクスポートすると ExportSchemaMismatch で失敗します(ベイクされた SchemaFingerprint は baseline のものと一致していなければなりません)——まずインポートを実行してください。
  • アセット参照は、GUID としてではなく、常にシートが使うアドレステキストとしてエクスポートされます——キー、またはサブアセットの場合は parent[sub]。グループは列が決めます。型付き列(AssetRef@Group<Type>)も同じ方式でラウンドトリップします。ColorAnimationCurveGradient の値は、正規のテキスト形式で戻ります(シート構文 を参照)。キーを持たないカーブは空セルとしてエクスポートされ、カラーは 0…1 にクランプされます(HDR なし)。

Push — Googleシートへのセル単位の書き戻し

Data Studio ツールバーの ⋯ ▸ Googleシートへプッシュ は、ベイクされた SO の値を、セル単位で本番のシートに送信します。アクティブなソースが API モードの Google Sheets でない限り、この項目は無効化され、その理由が明示されます。これは、他の誰かが編集中の本番シートを決して壊さないように設計されています。

この連鎖からは三つの保証が導かれます:

  • セル単位のプランをあなたが承認しない限り、何も送信されません。
  • あなたのインポート後に本番シート上で変更されたセルはスキップされ、決して上書きされません。
  • 行の削除は、本番シートがそのキーをまさにその行にまだ表示している場合にのみ送信されます——変化してしまったものは何であれ通知とともにスキップされ、決して推測で処理されることはありません。

安全性のチェーンは、順に次のとおりです。

  1. SheetsApi の認証情報が必要です — ExportUrl モードでの Push は、いかなるネットワーク呼び出しの前にも拒否されます(GooglePushRequiresSheetsApi)。
  2. プッシュされる各タブにキー列が必要です — Push は本番シート内で各行を キーによって 再特定するため、行が移動したことを検知し、その書き込みを安全にスキップできます(誤った行へ送信されることは決してありません)。キーのないタブに変更があると拒否されます(PushKeylessTabUnsupported)。
  3. プラン + 承認: セル単位の差分(baseline と現在の SO の比較)が、プランとして計算されます——書き込み、追加、行の削除——そして何かが送信される前に、明示的な承認 のために表示されます。削除はそれ自身の区画に独立して並び、それぞれに消えることになるキーの名前が付きます。却下 = 送信されるセルはゼロです。
  4. 送信前のライブ再取得: 送信の直前に、本番シートが再取得され、比較されます。競合するセルは、上書きされるのではなく、スキップされます(警告として報告されます):
    • PushConflictCellChanged — 第三者がそのセルを編集していた。
    • PushConflictRowMoved — キーが、あなたのインポート時に見えていたのとは別の行で見つかったため、書き込みは常にスキップされます(誤った行に送信されることは決してありません)。再インポートして再同期し、もう一度 Push してください。
    • PushConflictRowMissing — 行が外部で削除されていた。
    • PushConflictDuplicateLiveKey / PushConflictAppendKeyExists — 対象があいまいである。
  5. 行の削除は、送信される前にキーで照合されます。 あなたが削除したレコードは、送信前の再取得がそのキーが依然としてインポート時に見えていたまさにその行にあることを確認したあとにだけ、本番シートから削除されます:すでになくなっている行は完了として扱われます(再度 Push しても二重に削除されることはありません)。別の行でそのキーが見つかった場合——シートが変化してしまったということです——は通知とともにスキップされ、位置によって削除されることは決してありません。削除は各タブの中で下から上へ、最後に送信されます——そのため、先に行われた削除が、後から行われる削除の座標をずらしてしまうことはありません。行を削除できないソース(その能力を持たないカスタムプロバイダー)は、正直に以前の挙動へフォールバックします:削除は報告され、本番シート上の行はあなたが対処できるよう残されます。

Push の後は、レポートの適用/スキップ件数を確認してください。セルがスキップされた場合は、再インポートして整合性を取り、もう一度 Push してください。

Google への構造変更

Google ソースに対する構造編集(列、マーカー、並べ替え、名前変更)は、対象タブ全体 を書き換えます——まずライブ差分のチェックが行われ、前回のインポート以降にシート上で変更された内容を上書きする前には、明示的な承認が必要です。値の編集は(セル単位の)精密な方式のままです。書き換え方式を使うのは構造だけです。

反映によって行われる Addressables の登録

Data Studio で AssetRef@Group セルにアセットをドロップする、あるいはプロジェクトから一つ選ぶと、シートだけでなく プロジェクト への変更もステージングされることがあります: アセットをグループに追加する、別のグループから移動する、あるいはグループそのものを作成する、のいずれかです。これらの登録は反映の一部であり、チェーンの中の決まった位置で実行されます——ローカルフォルダー、Google シート、カスタムソースプロバイダーのどれであっても同じ位置です:

  1. 事前検証 は、ステージングされた登録を存在するものとしてカウントしたうえで、投影された状態全体を検証します。そのため、まだ登録されていないアセットを指すセルもエラーにはなりません。
  2. シートが書き込まれます。 書き込みがキャンセルされるか失敗した場合、これ以降は何も実行されません: Addressables の設定には触れられず、登録は次回の試行に向けてステージングされたままになります。触れたタブがすべてスキップされたためにどのタブも書き込めなかった反映(例えばワークブック由来のタブしか触れなかった場合)でも、登録は実行されません。一方、シートに書き込むものが何もない反映——ステージングされた変更が登録だけである場合——では、登録は実行され、再インポートが行われます。そのパスでは他のステージングされた編集は一切コミットされないため、引き続き取り消し(undo)可能なままです。
  3. 登録が実行されます。順序は: まずグループが作成され(デフォルトの BundledAssetGroupSchemaContentUpdateGroupSchema を使用)、次にエントリが追加または移動されてアドレスが与えられ、設定が一度だけ保存されます。各項目は実行の直前に再チェックされ、強行されるのではなくスキップされます——アセットがその後削除されていた場合、アドレスがそのグループ内の別のアセットにすでに使われている場合、グループを作成または発見できなかった場合、そしてもうどのセルもそのアドレスを参照していない場合です(登録が、どこからも指されないエントリを作成することは決してなく、すべてのエントリがスキップされたグループも作成されません)。プロジェクトにまだ Addressables の設定アセットがない場合は、そのために一つ作成されます。
  4. ステージングされたリストはクリアされます——適用されたものもスキップされたものも同様に——そして自動的な 再インポート が続くため、ベイクは新しいエントリを認識します。そのため、スキップされた登録は、その再インポートの時点で、それを必要としていたセルに UnknownAssetKey として正直に報告されます。

Console には、結果ごとに一行が記録されます——適用された項目ごとに Addressables: 'address' → group 'Group'、スキップされた項目ごとに警告として Addressables: skipped 'address' (reason)——そしてサマリー行として Addressables: N registered, M skipped です。ローカルフォルダーソースの場合、反映の完了ダイアログの末尾にも、この同じサマリー行が表示されます。

シートに書き込まれるドロップダウン

選択肢が有限である列には、シートに データ検証ルール が付与されるため、Google Sheets や Excel で編集する人は、綴りを覚える代わりにリストから選ぶことができます。何かをオンにする必要はありません: このルールは、Export・Push・オーサリングの書き戻しのたびに計算され、対象がそれを保持できる場所であればどこにでも適用されます。

ルール
Enum<T> スカラーその enum のメンバーの 固定リスト
参照スカラー(RecordId@Tab、および参照整合性を持つカスタム型 — §4.4a)対象タブのキー列に対する 範囲 で、終端を開けたままにするため、対象タブに追加されたレコードは自動的にリストに加わります。
List<>、ラッパー列、キー列自体ルールなし — 一つのセルに複数の値が入っているか、リスト化する対象が存在しません。
  • あくまで案内であり、強制ではありません。 すべてのルールは非厳格です(Google の strict:false、xlsx の showErrorMessage="0"): リストにない値も、警告マーカーが付きますが、引き続き受け入れられます。強制的な拒否は、「参照を先に書いて、レコードは後で定義する」という通常のワークフローを壊してしまい、インポート自身の最も近い候補の提案とも衝突してしまいます。
  • ルールは表示用のメタデータであり、値ではありません。 セルの中に現れることは決してないため、ラウンドトリップには影響せず、ルールのないエクスポートは、この機能が存在する前に生成されたものとバイト単位で同一です。
  • 値とは独立に適用されます。 ルールの付与は、セルを書き込む副作用ではなく、それ自体が独立したステップです——最もよくある流れ(enum メンバーを追加し、データは変更しない)ではゼロ件のセルしか送信されないため、副作用としては決して実行されません。べき等なので、再実行しても何も変わりません。
  • 失敗しても警告であり、push の失敗にはなりません。 値の送信は成功したもののルールだけが付与できなかった場合でも、push 自体は成功したことになります。もう一度実行すれば、ルールだけが再適用されます。

各フォーマットが保持できるもの:

対象メカニズム備考
Google Sheets(Push / 書き戻し)setDataValidation、一つのリクエストにまとめて送信両方のルール種別に対応。参照範囲は終了行を省略するため、対象タブが増えてもそれに追従します。
xlsx(Export)シートデータの後に続く dataValidations両方のルール種別に対応。エクスポートが一つのワークブックであるため、参照範囲は同じファイル内の対象シートのキー列を指し、シートの下方向へ終端を開けたままにします——これは Google の範囲が持つのと同じ意味です。それでもルールは、次の三つの正直なケースでスキップされ、警告の中でその名が挙げられます: リストのメンバーにカンマが含まれる場合(インラインの区切り文字がそれを分割してしまう)、インラインリストが 255 文字 の仕様上限(引用符付きのリスト全体に対してこのフォーマットが課す上限)を超える場合、そして規則が範囲であり、その対象タブがワークブック内にない場合です。
TSV / CSV(Export)プレーンテキストには、これらを置く場所がありません。
JSON(Export)データファイルであり、スプレッドシートではありません——ドロップダウンを付けるべきセルがそもそも存在しません。

対象外になったものは、実行ごとに一つの DropdownNotSupportedByFormat 警告 として正直に報告されます——影響を受けたすべての列が名指しされるため、「なぜ Google にはドロップダウンがあるのに、自分のファイルにはないのか」という疑問への答えは、謎のままにされることなくレポートの中にあります。値そのものは完全にエクスポートされているため、これはエラーではなく警告です。失われているのは編集時の利便性だけです。

gid マップ

ExportUrl モードでのみ使用されます。各エントリは、タブ名をシートの #gid= の値(タブを選択したときにブラウザーの URL に表示されます)に対応付けます。設定のインスペクターは、関係があるときにのみこのマップを表示します。

それらの数値を、ブラウザーから一つずつコピーしてくる必要はありません。設定アセットのインスペクターには、マップを自動的に埋めてくれる Google Sheets セクションがあります。

  • ExportUrl モードでは、ライブからgidを自動入力 が、ライブのスプレッドシートのタブ一覧を読み取ってマップ全体を書き換え、その後設定アセットを保存します。
  • SheetsApi モードでは、同じパネルが代わりに ライブタブ一覧を取得 を提供します。これは、そのシートが現在持っているタブを表示するだけです。このモードは gid を自力で検出するため、マップは一切不要です。

一つ注意点があります: 自動入力は Sheets API と通信するため、ExportUrl 自体のインポートには不要であっても、サービスアカウントキーの設定が必要です。キーがない場合は、中途半端に埋まったマップを書き込む代わりに、停止してその旨を伝えます。

ビルド鮮度チェックフック — 古いベイクはビルドを失敗させる

すべてのビルドの前に、ビルド前フックが、コミットされている生成 Database 型ごとに次の三点を確認します:

  • (i) ベイクされた SO が存在するか
  • (ii) そのスキーマフィンガープリントが baseline と一致するか
  • (iii) その Addressables への登録が存在するか

何か一つでも失敗すると、実行可能な文とともにビルドは中断されます(例:「Tools/SheetForge/Data Studio を開き、↓ Pull from source を押してからビルドしてください」)。これこそが、「ベイクされた SO は gitignore 対象である」ことを安全にしている理由です。クローンした環境や CI マシンは、物理的に空のキャッシュを出荷できません。

関連ページ