功能與限制
本頁完整列出 SheetForge 不做的事、目前尚無法做到的事,或是與你預期不同的行為——並附上原因、替代方案,以及未來是否有調整空間。
每個條目的格式為:現況/原因/替代方案(若有意義,會再加上未來調整空間)。
1. 平台與相依套件
Addressables 為必要套件——沒有它管線就會鎖定
- 現況: 位址載入是執行期路徑,且
AssetRef@Group型別需要com.unity.addressables,因此必須安裝該套件才能使用此素材。此素材本身即使沒有它也能編譯——所有使用 Addressables 的程式碼,都位於一個SHEETFORGE_ADDRESSABLES版本定義(version-define)之後,只有在偵測到該套件存在時才會開啟。 - 缺少時的行為: 整條管線(匯入.匯出.推送.工作台反映)會鎖定,而非降級——執行任何進入點都會顯示安裝提示並停止。不存在任何部分執行或靜默略過的路徑(例如「略過資源鍵值驗證」這種選項並不存在)。因為 Editor 即使沒有該套件也能編譯,它絕不會落入 Safe Mode。入門視窗會正常開啟,其 Addressables 列會顯示 ✗,並附上一個開啟 Package Manager按鈕。不具相依性的
SheetForge.Setup啟動組件,則作為應付其他不相關編譯失敗情況的引導安全網而存在。 - 在沒有安裝套件的 Data Studio 中: 資源儲存格的 ⊙ 選取器按鈕,以及它的拖放目標,都會被停用,並以工具提示說明原因;直接在儲存格中輸入位址則依然可行。
- 為什麼不做 Resources/Addressables 雙軌抽象化: 刻意不予建構——被判定為過度工程。
- 替代方案: 安裝 Addressables。Asset Store 的匯入提示視窗會在編譯之前處理好這件事;若你當時跳過了,能夠正常編譯的 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 組件會在每個建置目標上自我註冊一個
SHEETFORGEscripting-define 符號,讓其他素材能夠在編譯期偵測到 SheetForge 已安裝(見外掛開發 ▸ 從另一個素材偵測 SheetForge)。這項註冊是冪等的(只有在缺少時才會新增——一旦存在就不會引發反覆重新編譯)。 - 限制: 如果你之後刪除了此素材,該 define 仍會保留——原本會察覺到它被移除的程式碼,也隨之一起消失了。
- 替代方案: 在 Project Settings ▸ Player ▸ Scripting Define Symbols 中手動移除(依平台分別處理)。我們刻意不為了清理一個符號,而讓一個背景監看程式持續運行。這與
SHEETFORGE_ADDRESSABLES是兩回事——後者是一個內部版本定義,只反映 Addressables 套件是否存在。
2. Google 試算表來源
ExportUrl 模式為唯讀
- 現況: 在 ExportUrl 模式下,推送、反映、結構編輯與刪除全部都會被停用。
- 原因: 這是不需驗證、透過連結分享的匯出路徑——本質上就是唯讀的。推送永遠需要 SheetsApi 憑證,且此規則會在任何網路呼叫之前就被強制執行。
- 替代方案: 若需要任何寫回操作,請使用 SheetsApi 模式(服務帳戶——請參閱Google 試算表設定)。
ExportUrl 需要 gid 對照表
- 現況: 空白的 gid 對照表會導致匯入失敗;重複的 gid 會被拒絕。
- 原因: 沒有 gid 的匯出網址會靜默地只回傳第一個分頁——這是一個靜默損毀的陷阱,因此匯入會拒絕它。SheetsApi 會自動偵測分頁。
- 替代方案: 為每個分頁註冊其
#gid=值,或改用 SheetsApi。
推送依鍵值刪除列,且只刪除已驗證的列
- 現況: 本機刪除的一筆記錄,會在推送時——於傳送前的重新擷取確認其鍵值仍位於你匯入時所見的那一列之後——從正式試算表中移除。已經不存在的列會被視為已完成(具冪等性的重新推送);若在另一列發現該鍵值,就會附上通知被略過,絕不會依位置刪除。刪除項目會列在核准摘要中自己的一個區塊裡,並在每個分頁內由下往上、最後傳送。
- 原因: 依鍵值與正式試算表比對,正是讓刪除在一份可能已經產生偏移的試算表上維持安全的方法;任何比對無法確認的內容都會維持原狀。
- 限制: 不具備列刪除能力的來源(一個從未取得該能力的自訂提供者)會回退到舊有行為——刪除會被回報,正式試算表中的那一列則留給你自行移除。
推送會略過發生衝突的儲存格(此為設計考量)
- 現況: 自你匯入之後被第三方編輯過的儲存格、鍵值移動位置不明確的列、遺失的列,或正式試算表中重複的鍵值,都會被略過並附上警告——而不會被覆寫。
- 原因: 這正是安全網發揮作用的結果:傳送出去的儲存格都是有效的;略過的動作能保護其他人的變更,並防止寫入到錯誤的列。
- 替代方案: 檢查報告中已套用/已略過的數量;重新匯入以進行調解,然後再次推送。(衝突解決 UI 會是另一項獨立功能——目前並無規劃。)
推送需要一個鍵值欄
- 現況: 沒有
RecordId鍵值欄卻存在變更的分頁,無法被推送——這是一項計畫錯誤,會封鎖整個推送(每個分頁都不會傳送任何內容;沒有部分傳送這回事)。 - 原因: 推送會在正式試算表中依鍵值重新定位列;沒有鍵值的話,防止寫入錯誤列的防護機制就無法成立。
- 替代方案: 新增一個鍵值欄,或匯出為檔案後手動貼上。
3. xlsx 來源
-
分頁重新命名不支援來源為 xlsx 的分頁——這是多工作表活頁簿的保護機制。請在活頁簿中重新命名,然後重新匯入。
-
鍵值重新命名的傳播若涉及來源為 xlsx 的分頁,會封鎖整個批次——xlsx 路徑無法安全地進行精準的儲存格更新,而部分反映絕不被允許。請直接編輯該分頁,然後重新匯入。
-
無法表示的儲存格會被拒絕——沒有快取數值的公式儲存格、錯誤儲存格,以及儲存格內含定位字元/換行字元的情況。內建的 OOXML 讀取器是刻意精簡設計的(零第三方程式碼)。請將公式實體化,並使用
;來表示清單。 -
只處理數值——公式、日期與格式皆會被誠實地判讀——公式儲存格會提供其快取的數值(絕不會重新計算),日期格式的儲存格會被讀取為
yyyy-MM-dd顯示文字,數值格式、合併儲存格與圖表則不會被匯入。網頁應用的匯入對話框會在一則「本活頁簿的讀取方式」說明中具名列出實際發生過的判讀;在編輯器中則是同一套政策逐儲存格靜默套用(上述的拒絕情況仍會逐儲存格回報)。 -
部分匯出的下拉選單規則無法被承載——匯出結果是同一本活頁簿,因此參照欄的下拉選單會被寫成一個指向目標工作表鍵值欄的真正範圍,其意義與 Google 規則相同。以下三種情況仍會被省略,並統一以一則
DropdownNotSupportedByFormat警告列出:- 含有逗號的清單成員(內嵌分隔符號會將其拆開);
- 超過格式 255 字元上限(含引號)的內嵌清單;
- 規則屬於範圍型、但目標分頁不在該活頁簿中。
無論如何,數值本身都會完整匯出。請參閱來源、匯出與推送。
4. 編寫——Data Studio
沒有鍵值欄的分頁無法新增記錄,其數值編輯也會失去錨點
-
現況: 沒有
RecordId鍵值欄的分頁,能夠正常匯入並正常顯示,且結構編輯功能完全可用——新增、移除、重新命名、重新排序欄與標記,加上試算表層級的重新命名與刪除。它唯一無法獲得的是新記錄,因為沒有鍵值的記錄無法被命名或參照:- 新增列的控制項會被停用;
- 參照選取器會拒絕在那裡建立記錄(「……沒有鍵值欄,因此無法在此建立新記錄」);
- 試圖寫入其中的畫布或檢閱器動作也不會有任何作用。
數值儲存格是可以編輯的——但由於沒有鍵值可以用來定址該列,此編輯只會依該列的位置暫存。
-
原因: 一般而言,每一項暫存編輯都是以邏輯方式定址的,即
(tab, record key, field),並會在寫入之前重新對照試算表解析。這正是讓一項編輯能夠在重新匯入、列重新排序,或有人在其上方插入新列後依然存續的原因。沒有鍵值欄的話,就不存在這樣的位址,因此編輯會改為固定在一個列號上通過——處於這道安全網之外。所以,如果試算表的列在你寫回之前於底下發生了移動(一次重新匯入,或有人直接編輯了來源),一項以位置為錨點的編輯就可能落在錯誤的列上。請以小批次暫存並反映這類編輯。
-
替代方案: 新增一個
RecordId欄(結構編輯功能可用,因此你可以在同一個視窗中完成),反映後,該分頁就會恢復邏輯錨點,成為完全可編寫的分頁。沒有鍵值的分頁依然完全可以正常匯入——這是一項編寫上的限制,而非結構描述上的限制。
欄重新命名/@type 變更會破壞參照它的遊戲程式碼;反映後即無法復原
- 現況: 產生欄位的名稱/型別會發生變更;參照該欄位的遊戲程式碼必須手動更新。Ctrl+Z 只有在反映之前才有效。
- 原因: 強型別化——該欄位是產生的結構描述的一部分。編譯錯誤會被自動連鎖流程的安全中止機制攔截,並提供可據以行動的說明。欄的數值則會被完整保留(只有標記儲存格會變動)。
- 替代方案: 確認對話框會先提出警告;更新你的程式碼,並讓下一次匯入自行接續完成。
分頁重新命名會破壞參照它的遊戲程式碼;反映後即無法復原
機制與上述相同——產生的類別名稱會發生變更(FooDatabase → BarDatabase);重新匯入會自動處理所有資源端的清理工作(舊類別、SO、位址)。
支援互換與循環式的分頁重新命名
- 現況:
Alpha→Beta+Beta→Alpha(一次互換),以及更長的循環(A→B→C→A),都可以在同一批次中暫存並反映。無論先暫存哪一半都可行,且分頁列會立即顯示交換後的名稱(所見即所得,且可復原)。UI 層的檢查採用最終名稱集合的唯一性——只有真正的衝突,也就是兩個重新命名指向同一個名稱,才會被拒絕。反映階段則會嚴格執行此規則。 - 參照跟隨的是資料(分頁的身分),而非名稱: 在 A↔B 互換之後,
RecordId@A會被原子性地改寫為RecordId@B(單次處理——絕不會被重複套用兩次),因此它仍會指向同一份資料,而該資料現在已移至 B。 - 本機: 互換會在一次寫入中交換兩個檔案的內容。若連鎖重新命名在不同副檔名之間重複使用同一個名稱,過時的舊副檔名檔案會被刪除(採路徑式的刪除防護),因此重新匯入絕不會看到重複的分頁。
- Google: 標題變更會採拓撲排序,並以暫時標題打破任何循環(
A→tmp, B→A, tmp→B),因此正式試算表絕不會出現瞬間的重複標題。如果標題變更在過程中途失敗,留在暫時名稱下的分頁會被回報,並附上復原指引。
僅限 Google 的邊界情況:互相參照的互換分頁不會被重新指向
- 現況: 當兩個互換的分頁互相參照時——分頁
A有一個RecordId@B欄,分頁B有一個RecordId@A欄——Google 路徑會透過原地的標題變更來保留其內容,但不會改寫它們自己的@type儲存格。因此,這種互相自我參照的情況,在 Google 上不會被重新指向。 - 原因: Google 是透過變更標題來重新命名分頁的(依設計,內容不受影響);若改寫被重新命名分頁自己的表格內容,就會違背這個設計。本機來源會改寫被重新命名分頁的投影內容,因此本機端能完全處理這種情況。至於來自第三個分頁的參照,則在兩種路徑下都會被重新指向。
- 替代方案: 在 Google 上,可將這種互相參照透過第三個分頁繞道處理,或透過一個中介名稱來反映這次互換。
暫存數值 SO 覆蓋層已不再有按鈕
- 現況: 「在 SO 上預覽暫存數值」的覆蓋層(
EphemeralSoApply)過去是由 Workbench 的一個按鈕驅動的,而那個視窗已經消失了。這個型別仍然是公開 API,供想要使用它的工具呼叫;SO 檢閱器的測試編輯切換開關,則涵蓋了試驗執行期數字這種日常情境。 - 若你確實呼叫它,這項限制存在: 此覆蓋層會拒絕顯示——並附上徽章——(a) 待處理/新增的欄,以及 (b) 剖析失敗的儲存格。新增的列則有支援。它重複使用真正的剖析+烘焙路徑,因此對於無法真實計算出的內容,它會拒絕顯示,而不是偽造數值。
- 替代方案: 這向來只是一個預覽功能;請正常執行反映以套用真正的變更。重新匯入永遠會還原真實狀態。
當所在列被外部重新命名、刪除或發生鍵值衝突時,暫存編輯會被隔離
- 現況: 若某筆暫存編輯所在的列,在暫存與反映之間被外部重新命名、外部刪除,或發生鍵值衝突,該筆編輯就會從反映中被排除,並標示為「隔離」。
- 原因: 它的邏輯位址無法被重新解析——但它既不會被靜默捨棄,也不會被允許阻擋工作階段。
- 替代方案: 個別捨棄它(需經確認),然後重新暫存。
鍵值重新命名的傳播僅涵蓋 baseline 儲存格
- 現況: 你在同一批次中剛剛輸入、參照舊鍵值的文字,不會被自動改寫。
- 原因: 靜默改寫使用者剛輸入的內容是被禁止的;預檢會轉而攔截這個失效的參照。
- 替代方案: 自行修正暫存的參照,或先反映該次重新命名。
以下條目全都與**Data Studio**這唯一的編寫視窗有關。較舊的 Workbench 視窗已被移除;它唯一提供的三項功能,已優先遷移進 Studio 與設定檢閱器——請參閱工作台去哪了。
一份驗證失敗的試算表依然能開啟編輯——但匯出、推送與建置皆會被封鎖
- 現況: 只要來源被完整讀取,即使驗證失敗,其試算表仍會被儲存為 baseline,因此 Studio 能夠開啟它們,讓你就地修正錯誤。程式碼產生與烘焙要到錯誤數歸零才會執行,而在試算表處於這種狀態期間,匯出、推送到正式試算表,以及玩家建置皆會被拒絕,且各自會說明原因。
- 原因: 這三個出口都會把最後一次成功烘焙的數值,與較新的試算表合併。此時執行其中任何一個,都會把過時的數值拼接到某人已經修正過的儲存格上——形同一次靜默的回滾。封鎖出口,正是為了讓入口能夠保持開放。
- 反映一項修正只會詢問一次: 在一份隔離中的試算表上,寫回動作會顯示一次額外的確認,因為預檢在此無法作為硬性關卡(該試算表本來就已經有錯誤)。所發現的一切都會以警告的形式列在該次反映的報告中,之後自動重新匯入會重新驗證整份試算表。健全的試算表則不受影響——預檢依然會拒絕寫入。
- 替代方案: 修正每一個回報的錯誤,然後再次拉取。這項封鎖只會在唯一一個能夠清除它的地方自行解除:一次完整跑完烘焙的執行。
Data Studio 的排序與篩選僅影響顯示——啟用時會停用列重新排序
- 現況: Studio 的逐試算表排序(任意欄、遞增/遞減、依專案保存)與文字篩選,只會改變顯示順序。列號槽仍保留真正的試算表列號,兩者皆不會影響暫存、反映、推送或匯出。當排序或篩選啟用時,列的 ▲▼ 重新排序工具會被停用,並附上工具提示。
- 原因: 在檢視畫面經過排序或篩選時,依「可見的相鄰列」進行重新排序,會靜默地將列移動到使用者看不見的行旁邊。真正的列順序變更屬於結構操作——請先清除排序/篩選。
- 附註:「依最新排序」只有在你的試算表擁有能夠編碼此資訊的欄位時才存在(例如
IntId或類似日期的字串欄)——試算表本身並不儲存任何時間戳記。
當鍵值重新命名處於暫存狀態時,Data Studio 的 Problems 面板內容為草稿
- 現況: 當一個鍵值(
RecordId)儲存格帶有暫存編輯時,Problems 面板會附上一個草稿徽章,其中未解析參照的條目可能是假警報。 - 原因: 記憶體中的預覽並不會套用鍵值重新命名的傳播——傳播是在反映時才會跨所有分頁執行的。與其隱藏診斷資訊或偽造傳播結果,此視窗會告知你:在該次重新命名被寫入之前,這份清單只是草稿。
- 替代方案: 反映該次重新命名(傳播會伴隨自己的確認流程執行),然後再讀取重新整理後的清單。
表格在超過 200 列時會採用列虛擬化——有兩個值得了解的邊界情況
-
現況: 超過 200 列之後,表格只會為可視範圍建立列元素(外加十二列的預先建置範圍),並在上下方各留一個間隔元素,撐出真正的總高度,讓捲動軸不會說謊。跨越可視範圍邊界捲動時,會重複使用仍然存活的列,只建立新進入範圍的列。瀏覽器版的網格在相同的門檻下採用相同做法。
仍有兩種情況會建立全部內容。在 200 列以下(含)時,每一列都會如同以往一樣完整建立,一模一樣。一份完全無法取得可視範圍高度的表格也是如此——例如站在一個版面配置永遠不會抵達的視窗之外——因為在那裡,誠實的後備方案就是「全部建立」。一份剛好尚未完成版面配置的大型表格,則會多等一個影格,因此它會從第一次繪製起就採用視窗化,而不是先全部建立再丟棄。
-
你正在編輯的那一列會保持存活,即使它已捲出可視範圍,因此游標、焦點與你已輸入的內容都能存續。這項存活保留有一個距離上限,超過之後,開啟中的編輯器會提交並失去焦點,而不會被無限期地保留下去。發生這種情況時不會遺失任何內容——該數值早已存在暫存工作階段中。
-
只有元素的建立會被視窗化。 欄寬取樣、搜尋、排序、座標,以及暫存疊加層,仍然會考量每一列,因為只要其中任何一項只看畫面上的內容,就會得出不同的答案。因此切換到一份非常大的試算表,其工作量依然與試算表規模成正比;不再發生的,只是建立成千上萬個元件。
-
在瀏覽器中,視窗化模式會改為自行量測欄寬,而不是交給版面配置引擎處理。 自動版面配置的欄寬,會依目前恰好落在可視範圍內的列來計算,因此欄寬會在你捲動時抽動。視窗化模式下,欄寬則來自對所有列的資料驅動估算,並在之後固定下來。完整渲染模式(≤ 200 列)則仍然採用自動版面配置,不受影響。
畫布只能在其捲動範圍內平移,且標示循環的虛線連線在拉長時會變得更粗略
-
現況:
- Ctrl/Cmd +滑鼠滾輪可縮放記錄畫布,範圍介於 25% 到 200% 之間,並讓游標所在的那一點保持靜止不動——若採用置中錨定的縮放方式,你原本正在檢視的卡片會被擠出畫面。畫布標頭中的百分比則是一個按鈕,點擊可返回 100%。單純滾動滾輪仍會捲動畫面。
- 以滑鼠中鍵拖曳——或在沒有中鍵的硬體上使用 Alt +左鍵——可進行平移,按住期間游標會標示出抓取狀態。左鍵拖曳則保留給選取與連結使用,因此它無法同時代表「移動檢視畫面」。
- 此面板是一個捲動檢視,因此平移範圍即是捲動範圍:它會在內容邊緣處停止,而不會漂移到空白區域,當內容小於可視範圍時則完全不會移動。這並非一個無限延伸的畫布。
- 標示循環的虛線連線會限制自己繪製的虛線段數量上限,並在路徑拉長時將虛線週期加倍,因此非常長的迴圈讀起來會比較粗略,而非更加清晰。
-
原因: Unity 會依每次繪製呼叫配置網格頂點,且有一道 65,535 的硬性上限,一旦超過,繪製內容就會完全消失,同時仍須付出三角化運算的代價。虛線段上限就是刻意設計來讓單一
Stroke保持在這個預算之內。背景圓點網格過去也曾面臨同樣的懸崖式限制,但現在已不再如此。它現在是一個會重複排列的小型背景圖磚,其頂點成本為零,且無論畫布擴展到多大,重繪成本都維持在常數時間。(以路徑方式繪製的單一圓點,經實測成本為 28 個頂點,而非它的四個角落。這正是繪製後備方案 1,800 點預算背後的運算依據,也是為什麼圖磚才是最終出貨採用的做法。)
-
替代方案: 網格本身不需要任何替代方案。若面對範圍龐大的鄰近關係,請縮小畫面、限縮方向區段,或是將某個鄰居開啟為新的終點,而不要試圖把所有內容硬塞進同一個畫面。
一份尚無資料表的試算表會被略過,而不是被匯入
- 現況: 一個三個必要標記皆缺、且沒有任何資料列的分頁——例如一份剛建立、只帶有註解或一行
@style的全新試算表——會附上EmptyTabSkipped警告並被略過。它不會因為缺少三個必要標記而匯入失敗。它已經產生的程式碼、烘焙後的資源與位址都會被保留,而不會像該分頁已被刪除一樣被清理掉。匯出與推送同樣會對稱地略過它,因為三者所依據的判斷條件完全相同。 - 原因: 一份尚未完成的試算表,絕不能因此阻擋所有其他分頁的匯入,而作者通常會先建立試算表,之後才加上標頭列。
- 邊界: 一份寫到一半的試算表(存在任何一個必要標記)不會被略過——它會誠實地匯入失敗,因為靜默略過它會掩蓋掉真正已完成的工作。從 A 欄而非 B 欄開始輸入型別的試算表,同樣會被交給剖析器處理,因此它真正的診斷訊息(「A 欄是標記欄,資料從 B 欄開始」)依然會出現。
由外掛 C# 註冊的 enum,無法透過試算表獲得新成員
- 現況: 一個
Enum<T>若其T是由外掛透過enums.Register<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 變更(新增.移動.建立群組);該變更只會在試算表寫入成功之後才執行——或者,當暫存的內容只有註冊時,會單獨執行,接著進行自動重新匯入;若一次反映因其分頁為活頁簿型而無法寫入,這些註冊則會繼續保持暫存狀態。若重新輸入儲存格內容導致再也沒有任何內容參照某項註冊,該項註冊就會被撤銷;任何存留到反映時刻、卻沒有任何內容參照它的註冊,會以未參照的狀態被略過;已在該群組中的資源會保留其既有位址;位於另一個群組中的資源,只有在經過一次列出所有參照它的其他儲存格的確認之後,才會被移動。自動位址為不含副檔名的檔案名稱,若該位址已被該群組中另一項資源使用,則會被拒絕,而不會被重新命名。新群組會取得預設的
BundledAssetGroupSchema與ContentUpdateGroupSchema。已套用與已略過的項目會連同其原因一併被記錄到 Console,而對於本機資料夾來源,反映的完成對話框最後也會重複顯示這行摘要(Addressables: N registered, M skipped)。 - 原因: 試算表才是權威來源——對於一次沒有抵達試算表的反映,專案絕不能發生變動;而一個沒有任何儲存格指向的項目,會成為試算表無法解釋的孤兒。
- 替代方案: 若某項註冊被略過,下一次重新匯入會將該儲存格回報為
UnknownAssetKey;修正原因後再次反映即可。註冊永遠需要 Addressables 套件。
子資源以 parent[sub] 定址,Sprite 模式下的紋理能通過 <Sprite>
- 現況: 一個子物件項目(紋理中的一個 Sprite、字型中的一個材質)會依 Addressables 為其命名的方式定址——
parent[sub]——而該鍵值只會依子物件自身的型別受到檢查。上層資源的位址同時滿足自身的型別,以及其所包含的每一個子資源型別——這正是為什麼以 Sprite 模式匯入的紋理能通過<Sprite>欄的原因。拖放一個子資源,會暫存其上層資源以待註冊,並將parent[sub]寫入儲存格。 - 邊界: 一個子物件項目必須已存在於 Addressables 目錄中,
parent[sub]才能通過驗證;選取器會在上層資源之後,列出它所知道的子鍵值。
顏色不支援 HDR、曲線切線遵循其模式、漸層會被量化——這些皆為刻意設計
- 現況:
Color是四個位元組——超過 1 的色版(HDR)會在匯出時被鉗制到 0…1 之間。一個AnimationCurve切線,若其所在的一側是Auto、Linear、Constant或ClampedAuto,就會在匯入時依模式重新計算,因此一個與其模式矛盾的手動輸入數值會被取代(與模式套用時 Unity 所執行的重新計算完全相同);Once會被讀取為ClampForever,且絕不會被寫回;沒有任何關鍵影格的曲線沒有文字形式,只會以選填欄的空白儲存格形式存在。Gradient的關鍵影格時間會在匯入時被量化為 16 位元(與 Unity 儲存的方式完全相同),只帶一個關鍵影格的漸層,經過 Unity 之後會變回兩個相同的關鍵影格,而色彩空間只有在曾經被設定過時才會被寫入。 - 原因: 試算表所顯示的數值,必須就是引擎所持有的數值,因此 Unity 之後才會執行的正規化,改為在入口處就執行一次,讓每一個介面——試算表、編輯器欄位、網頁預覽、烘焙後的資源——都顯示同一條曲線與同一個漸層。
- 替代方案: 如果你希望切線數值被逐字採用,請使用
Free/Free;HDR 強度請改存放在另一個獨立的float欄中。
晶片編輯器與原生欄位僅存在於這三種視覺型別
- 現況: Data Studio 會將
Color、AnimationCurve與Gradient純量顯示為 Unity 自己的欄位,其List<>則顯示為晶片編輯器;網頁應用則以自己的編輯器與晶片清單顯示預覽。其他所有清單欄——List<int>、List<Enum<…>>、wrapper 清單——在兩個主機中都維持標準文字,一個包含這三者之一的 wrapper(Pair<Color>)同樣也是文字。 - 原因: 只有這三種型別才擁有逐元素的圖像呈現;至於其餘型別,單一行標準文字早已是最精確的呈現方式,而 wrapper 的外層記法則歸其外掛所有。
- 空間: 一個儲存這三種數值之一的外掛型別,可以透過宣告對應的
StudioCellEditorHint原型,採用相同的編輯器(請參閱外掛開發 §4.16)。
歸屬著色僅區分「試算表」與「程式碼」兩種來源
- 現況: 側邊欄/圖例只會區分恰好兩種來源——真正的試算表分頁,以及程式碼註冊表的虛擬分頁。並不存在第三種「已產生」的分類,也沒有逐欄的歸屬著色。
- 原因: 來源是從分頁本身推導出來的,這是視窗本來就能確定得知的資訊;逐欄分類若要做到誠實,就需要另一個全新的擴充點,且目前並無任何使用端提出此需求。
- 這與試算表本身的顏色是兩回事:
@style讓一份試算表能夠指定自己的顏色,而那個顏色是作者所選擇的顯示中繼資料——它完全不代表這份試算表的來源。這兩種著色是從不同的地方讀取的,並且絕不會互相合併。
參照下拉選單回答的是「成員關係」,而非「順序」
- 現況: 一個
RecordId@Tab儲存格現在具有可搜尋的下拉選單(List<>則是核取清單),但清單儲存格的下拉選單只能新增與移除元素——無法移動元素。重新排序清單的動作,是在畫布上完成的,那裡每個元素都有自己的一列。 - 原因: 下拉選單回答的是「這裡面有什麼」;「排在哪個位置」則需要一個能夠將項目排列出來的介面,而畫布正是這樣的介面。若在兩個地方重複實作,就會產生兩套需要分別維護的答案。
- 另外: 此下拉選單只會附加在一般、非 wrapper 的參照欄上。wrapper 儲存格的文字帶有 wrapper 自己的記法,若直接貼上一個裸鍵值就會破壞該數值——深入 wrapper 內部進行編輯,是已註冊的儲存格小工具的職責(請參閱外掛開發)。
All 搜尋在編輯器中讀取烘焙後的資料,在瀏覽器中讀取即時工作階段
- 現況: Data Studio 的 All 項目會搜尋烘焙後的資料庫,因此需要先有一次成功的匯入,並在匯入完成或使用中設定發生變更時自動重新整理。網頁應用的 All 項目搜尋的則是工作階段目前顯示的值,暫存編輯也包含在內。兩者的比對方式、結果順序與 50 列分頁,皆為相同的程式碼,而在兩者之中,雙擊一筆結果(或在選取該列後按 Enter)都能開啟該試算表並選取相符的儲存格。
- 原因: 瀏覽器沒有烘焙後的 ScriptableObject;它擁有的是即時工作階段——而以畫面上的數值作答,正是瀏覽器工作階段存在的意義。編輯器則持續讀取它早已擁有的烘焙後真相。
- 邊界: 在編輯器中,一項已暫存但尚未反映的編輯,在匯入執行之前不會被 All 找到,而來自工作階段尚未載入之試算表的結果也不會進行導覽——清單下方會有一則說明告訴你原因。在瀏覽器中,暫存的編輯會立即被找到,且每一筆結果都可以被跳轉過去。
5. 效能
- 正常路徑是線性且快速的:50,000 列 × 20 欄約為 628 ms(實際編輯器,Mono;無頭模式下為 144 ms);50 個分頁 × 2,000 列、180k 個參照儲存格約為 294 ms。一般專案規模完全不成問題。
- 記憶體用量為線性成長,但裝箱(boxing)頗為頻繁:每個儲存格約保留 59 bytes(匯入期間尖峰約 138)。以 Google 試算表的儲存格上限(~10M 個儲存格)推算,匯入時間為 ~6.3 s、保留量為 ~590 MB、尖峰為 ~1.4 GB——在極端規模下,請留意低規格/32 位元的執行環境。(欄導向的 IR 是已知的待辦事項。)
- 編寫表格在超過 200 列時會採用列虛擬化,編輯器與瀏覽器皆然,因此開啟一份大型試算表不再需要為每一列建立一個元件。不會被視窗化的是那些若只看畫面上內容就會得出不同答案的逐列邏輯——欄寬取樣、搜尋、排序、座標、暫存疊加層。詳情與兩個邊界情況請見 §4。
- 錯誤路徑同樣是線性的:即使參照大量同時失效,最接近候選建議的計算也維持在有限範圍內。每個欄位皆設有建議預算,並搭配長度預篩選、提早終止的編輯距離計算,讓計算量大致與失效參照的數量成線性關係(4,000 筆失效參照時,無頭模式下約為 45 ms;同樣規模的有效資料約為 2.7 ms)。重新命名一個被參照的分頁,本來就不會導致參照大量失效:這次重新命名會改寫參照它的
@type儲存格。
6. 示範場景
- 範例皆為可選擇匯入的套件——兩個示範範例及其場景預設並不存在。它們以 Unity 套件的形式提供(
Assets/SheetForge/Examples/SheetForgePluginDemo.unitypackage與SheetForgeCoreDemo.unitypackage)。匯入其中一個——雙擊,或使用入門視窗的匯入 Plugin/Core Demo按鈕——即可還原Assets/SheetForge.PluginDemo/…或Assets/SheetForge.CoreDemo/…。在那之前,範例根本不存在於你的專案中——它們僅以那些套件的形式提供——因此它們絕不會與你的專案發生衝突。核心產品完全不需要依賴它們就能自給自足。 - 需要先執行一次匯入(依機器而定)——它所載入的 Addressables 位址屬於未提交的快取。在那之前,它只會顯示一則指引訊息。
- 示範不需要設定命名空間——已提交的範例型別使用預設的
SheetForge.Generated命名空間,並搭配Example*類別前綴,因此重新匯入示範會原地重新產生它們,完全不需要generatedNamespace設定。
7. 外掛擴充——已推出的介面縫隙(附邊界說明)與仍然保留的部分
共有十六種擴充合約已推出,每一種都只需零 Core 修改即可加入——完整清單請見外掛開發。這十六種合約全都是透過發現機制被偵測到的(Unity 端為 TypeCache;瀏覽器端為已上傳組件掃描),不需要組件參照,也不需要編輯任何清單檔。其中十一種 Core 合約會取得一個註冊表,用來註冊它們所要新增的內容,而五種 Editor 合約——圖形小工具、檢閱器動作、儲存格編輯器提供者、面板提供者,以及來源提供者——則單純地被偵測到並直接使用。
已推出的介面縫隙中,有五種帶有值得在此明確說明、而非留給你自行發現的邊界:
參照式自訂儲存格型別——讓你自己的記法擁有完整的 RecordId@Tab 對等性
-
現況: 一個已註冊的儲存格剖析器,若同時實作了
IReferencingCellType(且其數值實作了IRefBearingValue),就等於告訴 Core 該如何讀取並改寫深埋在自己記法中的鍵值。該欄位隨即能獲得內建參照所擁有的一切:- 附最接近候選建議的完整性檢查;
- 能保留負載內容的鍵值重新命名傳播(
attack:add:10→power:add:10); - 圖形邊與連接埠、
▾選取器、反向參照計數; - 孤兒偵測,以及匯出下拉選單規則。
這項偵測是透過轉型已註冊的剖析器來完成的——並沒有另外開一條新的註冊管道,而未實作它的自訂型別則會維持原樣、位元不差。請參閱外掛開發 §4.4a。
-
邊界: 負載內容不得包含
;——Core 會在你的剖析器看到文字之前,就先將清單儲存格拆分成多個元素,因此值裡面的分號會被拆碎成兩個元素(wrapper 型別也帶有相同的限制)。而且@target必須指向一個真正的試算表分頁:程式碼註冊表的虛擬分頁會被以UnknownTargetTab拒絕,與RecordId@Tab的規則完全相同。正是這項限制,才讓 Core 自身的未解析參照回報與重新命名傳播機制,得以不加修改地直接套用。
<> wrapper 型別(MyWrapper<T>)——附拒絕規則
-
現況: 外掛透過
ICellWrapperType註冊一種泛型數值結構(例如Pair<int>=1~2);Core 會遞迴解析內部型別(請參閱試算表語法)。 -
拒絕規則:
Pair<List<T>>會被拒絕——清單不能位於 wrapper 內部,List必須維持扁平且位於最外層。Pair<int>@Tab會被拒絕——請將@放在內部葉節點上:Pair<RecordId@Tab>。Pair<int?>/Pair<int=1>會被拒絕——選填性/預設值屬於欄位層級,並非內部型別的一部分。
List<Pair<T>>是被允許的,但 wrapper 自身的分隔符必須與;(清單分隔符)不同——這是 Core 無法強制執行、屬於外掛開發者的責任。
自訂結構標記(@yourMarker)——僅限逐欄中繼資料
- 現況: 外掛透過
IStructuralMarkerDefinition/ISheetForgeMarkerPlugin註冊一個@marker列,將@overlap的逐欄驗證一般化。此值會被儲存為FieldSchema.MarkerValues中繼資料。 - 邊界: 一個標記只負責自己的逐欄數值驗證——它並不會接管整列資料結構形態的剖析工作(正規化仍然是表達資料結構形態的方式)。而且程式碼產生器不會將標記值烘焙進去:如同
@overlap,它們僅是驗證/顯示用的中繼資料,對結構描述指紋而言是不可見的——因此完全不會有任何與標記相關的內容,進入產生的程式碼或烘焙後的 SO 中。
色彩預設集——我們自行繪製的介面,而非 Unity 的原生小工具
- 現況: 外掛可以透過
ISheetForgeThemePlugin/ThemeRegistry註冊一個色彩預設集。它會出現在Preferences ▸ SheetForge ▸ Theme中,與內建的 Default 及 High contrast 預設集並列,並且只有在使用者實際選取它時才會套用(註冊本身絕不會直接霸佔畫面)。一個預設集只會覆寫它自己指名的插槽——其餘所有插槽都會維持產品預設值,因此即使日後新增更多插槽,既有的預設集依然有效。 - 邊界——介面風格混搭是預期中的情況: 主題涵蓋的是 SheetForge 自行繪製的部分(視窗背景、標頭、文字、強調色,以及網格與暫存用的顏色)。繪製於這些視窗內部的原生 Unity 小工具——按鈕外框、欄位邊框、彈出選單箭頭——則會持續依循編輯器外觀主題,而 Unity 並不允許套件重新設計這部分的樣式。因此,若在編輯器執行深色外觀主題時選擇 Always light,就會得到一個淺色的 SheetForge 介面、搭配深色的原生小工具。如果你想要一致的視覺風格,請將編輯器外觀主題設為相符的模式。
- 邊界——僅限顏色: 預設集所帶有的是顏色(每個插槽一個
0xRRGGBB)。間距、字型大小與版面配置皆無法佈景化,而半透明的填色(徽章背景、對話框遮罩)則是由插槽顏色加上一個固定的透明度推導而來,並非可以另外個別設定的項目。
宣告式編寫介面——刻意設有邊界的詞彙表
- 現況:
ISheetForgeStudioPlugin讓一個套件能夠以資料的形式描述動作、面板、欄徽章與儲存格元件外觀,因此一次註冊就能同時由編輯器與瀏覽器共同繪製。這份詞彙表是固定的,只會透過附加成長:五種動作放置位置、十三種節點種類、七種儲存格元件原型(其中最新的兩種——CurveEditor與GradientEditor——正是內建曲線與漸層型別所使用的)。 - 邊界——這並非一套 UI 框架。 任意繪製、複合輸入與多步驟流程,在這裡沒有對應的詞彙,新增這些內容就意味著要永遠維護一個迷你版的 UI 工具集。這正是
IStudioPanelProvider的用途:以與一個描述式面板相同的 id 註冊它,編輯器就會繪製豐富版本,瀏覽器則繪製描述版本。這裡沒有僅限網頁的逃生艙口——瀏覽器無法載入 UIToolkit 型別,若假裝有這種東西,就會讓一個外掛的擴充功能只存在於單一畫面上。 - 邊界——一個動作恰好只有四項能力:暫存一個儲存格、以一個 Undo 步驟暫存多個儲存格、聚焦一筆記錄、請求重繪。因此一個外掛的動作是一項普通的暫存編輯,會通過與手動輸入完全相同的把關機制、預檢與推送。編寫工作階段本身則刻意不被公開。
- 對觀察者而言,是零觸發,絕不會錯誤觸發:
IPipelineObserver會在一次明確的匯入循環結束時觸發。有兩條路徑完全不會抵達那個時間點——一次在管線啟動之前就中止的執行(沒有使用中設定;未安裝 Addressables),以及被一次編譯錯誤中斷的程式碼產生→編譯階段。如果你需要的是「曾經嘗試過一次匯入」,請將它與編輯器端的ImportEvents匯流排搭配使用。
已設計但尚未建構(目前尚無使用端)
- 整列資料結構形態標記(例如一個能將 2D 矩陣讀取為單一欄位的標記)——刻意尚未建構:標記負責的是逐欄驗證,而非整列剖析,而正規化(參照 +
type欄 +List<T>)在表達能力上已經完備。MarkerRegistry這個註冊介面縫隙本身已經推出;只有這種結構剖析的詮釋方式仍被保留、尚未實作。 - 將自訂標記值烘焙進產生的程式碼中——在有使用端真正需要將標記中繼資料作為程式碼產生的常數/屬性之前,這不在範圍之內。
- 逐筆記錄/延遲載入的 SO 容器——設計已完成,但尚未建構;目前的程式碼產生器只會產生逐分頁、整批載入的 Database SO。
8. 結構描述演進
- 結構描述變更後,匯出/推送需要一次全新的烘焙——過時的烘焙結果會因
ExportSchemaMismatch(指紋不符)而失敗。現在,這個拒絕畫面會主動提議代你執行那次匯入:只需一次確認即可開始,之後不會自動執行任何匯出或推送——等匯入完成後,你只需再次按下原本想執行的那個動作即可。 - 結構描述變更後的第一次匯入,在內部會分成兩個階段(程式碼產生 → 編譯 → 烘焙)——這是自動完成的,只需使用者執行一次動作;只有編譯失敗才會使其停止(安全中止、提供可據以行動的說明、重試上限為 3 次)。
9. 在地化範圍
報告中的細節片段(有問題的值、候選建議)、低階例外狀況,以及開發者紀錄,會以英文內嵌在已在地化的報告骨架架構中——執行期才內插的內容無法作為語言對照表的鍵值(這是業界標準的邊界)。所有編輯器繪製的內容,加上報告骨架架構與為什麼/怎麼修的句子,都在全部 10 種語言中完全在地化。
全部十種語言都已完整翻譯。 每種語言對照表中的每一個鍵值都帶有真正的翻譯——包括 Data Studio、主題偏好設定、對話框與紀錄文字在內。十個檔案之間的鍵值對等性會由測試強制驗證,因此絕不會退回顯示原始鍵值,也不會導致任何佔位符出錯。
每種語言中都有少數條目讀起來與英文版一模一樣,這屬於翻譯層面的取捨,而非尚待完成的翻譯:它們是符號與僅作佔位符用途的字串(—、+)、專有名詞與格式名稱(Google Sheets、SHA-256),以及某些語言本身確實採用與英文相同拼法的詞(OK、Alpha)。
外掛自己的標籤完全不存在於 Core 對照表中:透過 ISheetForgeStringsPlugin 註冊它們,就能讓它們跟隨使用者的語言;若不註冊,則會逐字顯示。
10. 編輯器互動
- Ctrl+Z 的作用範圍:取得焦點的文字欄位會優先攔截 Ctrl+Z(作業系統的標準行為);成功反映之後,暫存歷史記錄會被清除——復原動作絕不會觸及已經寫入試算表的內容(因為試算表才是權威來源)。
- 在匯入/匯出/推送期間,語言變更會被鎖定(因為變更語言會觸發選單重新產生所需的重新編譯)。主題變更則不會被鎖定——它們絕不會觸發重新編譯,因此亮度與預設集可以隨時切換,即使在執行過程中也一樣。
- 主題與語言皆依使用者各別設定(EditorPrefs),而非依專案設定——每位隊友各自保留自己的設定,兩者皆不會出現在版本控制中。選擇一個非預設的色彩預設集,會在
Assets/SheetForge/Editor/Generated/底下產生一份樣式表(已加入 gitignore,可自我修復);使用預設集則不會產生任何內容,且會將其刪除。 - 產生的程式碼 + 烘焙後的 SO + Addressables 群組,都是依機器而異、已加入 gitignore 的快取——每台機器只需執行一次執行匯入;遊戲程式碼一律依位址載入,絕不會使用直接的場景參照。
11. 授權
儲存庫隨附一份 LICENSE 聲明:Unity Asset Store 的 EULA 才是具有約束力的正式合約,並附上一份儲存庫檢視聲明(原始碼公開可見,供參考及已授權的購買者使用;未經書面許可,不得在 EULA 範圍之外進行重新散布/轉售)。第三方程式碼:完全沒有——包括那個手寫的 OOXML xlsx 讀寫器也不例外。
12. 已驗證狀態(發布時)
- 雙重測試框架:2,150 個無頭 .NET 測試(2,150 個通過)+ 3,021 個 EditMode 測試(3,021 個通過,0 個失敗,4 個略過)。這兩個數字是唯一陳述測試總數的地方;其他每一頁都會連結回此處。
- 四個略過項目是即時 Google 端對端往返測試,只有在環境中具備服務帳戶憑證時才會執行,而在此次執行中被略過。在具備憑證的情況下,它已針對一份正式試算表反覆執行過——擷取 → 推送,包括列位移動的衝突偵測、與地區設定無關的浮點數處理、跨分頁混合批次,以及一次完整的調度器執行,驗證每次傳送恰好產生一份合併報告與一次自動重新匯入。結果:4/4 全數通過。
- 網頁應用擁有自己的一套關卡,且全數通過:型別檢查、lint、414 個單元測試、一次 WebAssembly 發布+煙霧測試(會載入一個真正的外掛 DLL)、一次正式環境建置、56 個端對端瀏覽器測試,以及一項跨語言常數檢查(六組 Unity↔網頁配對:主機版本、外掛格式、登錄庫結構描述版本、OAuth 範圍、主機組件名稱、列視窗化門檻)。
- 所有已編譯的產品 asmdef:0 個錯誤,0 個警告。(範例 asmdef——每個示範各有兩個——只有在你匯入示範套件之後才會出現,在那之前不會被編譯。)
- 防護測試全數通過:產品原始碼中零韓文字串常值、核心介面縫隙中零領域詞彙(兩者皆略過
/Samples~/——範例本身就是領域內容)、10 種語言的鍵值對等性。 - 不具 IVT 的消費端模擬組件,僅依賴公開 API 即可編譯(這是由編譯器強制證明的主張)。其中一個不具 IVT 的迷你外掛探針,也僅使用公開介面就實作了十六種擴充合約中的十五種(
ISheetForgePlugin/ 驗證器 / 邊 / 標記 / 範本 / 畫布 / 程式碼註冊表 / 主題 / studio UI / 字串 / 管線 /ISheetSourceProvider/ Studio 小工具 / Studio 檢閱器動作 / Studio 儲存格編輯器)——因此,即使 Plugin Demo 範例不再被編譯,合約的公開性依然得到證明。第十六種——IStudioPanelProvider——會回傳一個VisualElement,因此改由一項編輯器端測試來驗證它。同一個探針也實作了選用的能力介面,包括IReferencingCellType/IRefBearingValue,並透過公開介面實際運作它們(以轉型方式偵測、全部五個掛勾、殘留內容的保留)。
只有在 Asset Store 送審時才能驗證的項目(不在儲存庫範圍內):.unitypackage 安裝提示的行為、Portal 相依性宣告、將測試組件排除在發布套件之外,以及在乾淨專案中重新檢查 0 警告。
13. 在地化試算表(遊戲文字)
誠實說明在地化試算表與 Unity Localization 橋接的邊界。(套件本身是選用的——請參閱§1。)
橋接是單向的,外部表格編輯一律會被詢問——絕不合併
- 現況: 同步只走試算表 → StringTable這一個方向。橋接會為自己擁有的表格蓋上戳記,並為每一次同步建立指紋;一個表格自那之後被別的東西編輯過——Localization Tables 視窗、Unity 自己的 Google Sheets 擴充功能、一次 XLIFF 匯入——都會讓下一次同步停下來詢問:從試算表覆寫,還是連同差異報告一起中止。
- 原因: 兩個都能寫入同一份資料的駕駛座,終究會走向靜默覆寫。試算表是權威來源,因此另一個駕駛座必須是明確的,而不是靜默的。
- 替代方案: 讓翻譯透過試算表繞道而行——翻譯活頁簿(xlsx 匯出+僅語言欄的部分重新匯入)正是為此而存在。
在 Studio 之外重新命名鍵值,等同於刪除再新增
- 現況: 在 Data Studio 中重新命名一個鍵值,會就地重新命名表格條目,保留
LocalizedString參照所綁定的內部 id——場景參照能存活。直接在試算表來源(Google 試算表、Excel)中重新命名鍵值,則與刪除一個鍵值再新增另一個是無法區分的:橋接會建立一個全新的條目,舊的那個則變成孤兒,而場景參照仍然指向那個孤兒。 - 原因: 純文字層級的差異比對,在不用猜測的情況下無法分辨重新命名與刪除再新增,而猜錯就會靜默地重新綁定參照。
- 替代方案: 在 Data Studio(任一主機)中重新命名鍵值;孤兒報告會抓到外部重新命名之後的殘局。
孤兒表格鍵值預設會被保留
- 現況: 存在於表格中、卻已不在試算表裡的鍵值會被保留、回報為孤兒,並只透過明確的清理動作移除——或者,若你選用同步時刪除設定,則會自動移除。沒有任何東西會作為副作用被刪除。
- 原因: 一列缺失的試算表列,可能只是編輯到一半的失誤;為此摧毀翻譯內容將無法挽回。
只涵蓋字串表格——資源表格未被涵蓋
- 現況: 橋接只會填入
StringTable集合。Unity Localization 的AssetTable軸(在地化的 Sprite、音訊、預製件)不會從試算表同步。這是一項已承認的待辦事項。 - 替代方案: 用套件自己的工具管理資源表格;橋接不會碰它們。
XLIFF 與偽語言不會被重新實作
- 現況: 同步後的表格是普通的 Unity Localization 表格,因此套件自己的 XLIFF 匯出/匯入與偽在地化工具在其上運作不受影響。SheetForge 不新增第二套實作。
- 邊界: 那些工具寫進表格的輸出,算是外部編輯(見上方第一項)。請讓試算表維持唯一真實來源,並透過翻譯活頁簿承載翻譯內容。
不隨附語言專屬的 Smart Format 輔助工具
- 現況:
smart欄會把一個條目標記為 Smart String,但 SheetForge 並不提供自己的文法格式化工具——例如韓文的助詞選擇,就是刻意沒有包含在內。 - 替代方案: 套件的 Smart Format 擴充點仍然完全開放給你自己撰寫的格式化工具。
鍵值常數會經過 ASCII 淨化
- 現況: 產生的
{Tab}Keys常數,會把 ASCII 字母、數字與_以外的每一個字元都變成_(碰撞會得到一個數字後綴),所以一個非 ASCII 的鍵值會產生一個難以閱讀的常數名稱——鍵值本身在任何地方依然照常運作。 - 替代方案: 若你會用到這些常數,請讓鍵值保持 ASCII(
ui.ok、dialog.intro)。如同試算表定義的 enum 檔案,常數檔案永遠會產生到設定資料夾中——相同的組件備註同樣適用。
網頁應用可以編寫在地化試算表;同步是編輯器的事
- 現況: 編寫、驗證、涵蓋率、鍵值生成、語言檢視鏡與翻譯活頁簿,在瀏覽器中全都能運作。寫入 StringTable 則不行——瀏覽器沒有 Unity 專案可以寫入。
- 原因: 這是誠實的範圍界定,而不是缺失的功能:表格活在專案裡。
相關頁面
- 常見問題與疑難排解——本頁許多條目以症狀為出發點的對照視角
- 來源、匯出與推送——Google/xlsx 邊界的完整脈絡
- Data Studio——編寫邊界的完整脈絡