跳到正文
文件
本頁內容

關卡格式

關卡是一個資料夾:內容在level.json或level.blob中,後設資料在metadata.json或metadata.blob中,另有封面和媒體檔案。備份、記錄和玩家設定留在裝置上

關卡資料夾

關卡資料夾中的每個檔名都固定在FileNames中:

檔案模型內容
level.json或level.blobLevel內容:設定、物體和事件、音訊、資源、提示
metadata.json或metadata.blobLevelMeta名稱、描述、作者、許可證、每個資源的記錄、對封面的引用
logo.png或logo.jpg-封面
音軌、圖片、字型-關卡引用的媒體檔案

副檔名就是格式。檔案內部的任何地方都沒有寫它。讀取方看哪個檔案存在:level.json還是level.blob

關卡和後設資料各自獨立選擇格式。例如,內建關卡new-zero-demo在metadata.json旁邊存放的是level.blob

metadata.json特意做成單獨的檔案。目錄用一千個小後設資料檔案展示一千個關卡,期間一個關卡也不開啟

資源

資源通過uri和uri_type(ResourceUriType)這一對值定址:

值型別檔案位置
1LevelPath關卡資料夾內。只有這種型別能讓關卡可移植
2AbsolutePath本裝置上的其他位置
3DirectUrl通過網路下載
4StreamingAssets隨遊戲提供,而不是隨關卡

圍繞一首歌建立關卡時會出現AbsolutePath。匯出歸檔包時會把這樣的檔案複製進去。更多:歸檔包與保護

封面和其他資源一樣,通過LevelMeta.LevelLogo訪問。是logo.png還是logo.jpg,由檔案本身的位元組決定,而不是由源圖片的名稱決定

封面的作者記錄是一個resource_type為9(LevelLogo)的ResourceMeta。其中resource_id和resource_guid都不填:關卡只有一個封面,所以型別就是完整的地址

AI宣告

LevelMeta和每個ResourceMeta都帶有AiGeneration型別的ai_generated:

值名稱含義
0NotSpecified未宣告,未知。這是預設值,缺少該鍵時也按此讀取
1No宣告為未使用AI生成
2Yes宣告為AI生成

在LevelMeta中,它只針對關卡自己的內容。每個資源和封面在各自的記錄中宣告自己。關卡中是否有AI內容並不儲存:由LevelMeta.ContainsAiContent()回答,只有Yes才算

不隨關卡一起走的內容

這些檔案位於levels資料夾旁邊,從不在關卡資料夾裡。打包、傳送或刪除關卡都不會動它們:

資料夾或檔案是什麼為什麼留下
backups/編輯器自動儲存,每個關卡ID一個資料夾,backup_level_<time>.<extension>放在它所保護的資料夾裡的副本會隨該資料夾一起被刪除
stats/按LevelId記錄的成績和進度,以及statistics.json否則別人發來的關卡到手時就已經通關了
settings.jsonUserSettings,玩家對整個裝置的設定它們屬於玩家,而不屬於關卡
resources/themes、effects、shapes、prefabs本裝置的共享庫它們由裝置上的所有關卡共用
reports/診斷報告-

JSON鍵

JSON總是緊湊寫入,沒有縮排。想用眼睛閱讀,請在文本編輯器中格式化

鍵的長度取決於它在關卡中出現的次數:

  • 每個檔案只出現一次的鍵用完整單詞以snake_case書寫:level_id、min_generation
  • 重複上千次的鍵縮短為幾個字母:objs、f、e、v

鍵佔關卡檔案位元組數的52%,所以這是規則,而不是個人口味。所有鍵都宣告在Names.cs中

多型值寫作[標籤, 資料]。普通字串是[0,{"v":"Author"}],本地化字串是[1,{"strs":[...]}]

ObjectId之類的識別符號包裝寫作裸數字或裸字串

識別符號

整數識別符號的符號有含義。0始終表示“未設定”:

識別符號JSON鍵正數負數
ObjectIdid,父級用pid關卡物體遊戲物體:-1相機,-2本地玩家,-3預製件模板的根
資源識別符號txid、fnid、auid、byid、ttid隨遊戲提供的資源關卡自己的資源
音訊軌道的AudioIdaid本關卡的軌道不使用

pid等於0表示物體沒有父級。小於-3的數字只在遊戲執行時存在,不會寫入檔案

Marker、Checkpoint和BeatSegment沒有識別符號。它們的地址是時間上的位置:標記和檢查點是幀f,節拍段是其時段sp的第一幀(節拍段互不重疊)。移動這樣的物件,它的地址就會改變

預製件覆蓋用編號而不是JSON鍵來指明欄位。放置把自己的覆蓋存放在mod列表中。每個覆蓋的key包含三個數字:

  • id:模板內部的物體,而不是它在關卡中的副本
  • f:欄位的永久編號
  • i:列表欄位的元素,整個欄位則為-1

編號永久繫結到欄位。重新命名該欄位的JSON鍵不會破壞已經儲存在關卡中的覆蓋

外層的“g”

每個序列化根都包在一個帶兩個鍵的封套裡:

{"g":2,"v":{"level_id":"18df5f61-3aa4-4812-bf69-d357f2201bc3","vrs":"1.0", ... }}

g是模型的代(Names.Generation),v是資料

封套可以巢狀。在Level內部,LevelSettings、GameLevel、AudioLevel、LevelResources和LevelHints都有自己的封套。代如何運作:版本與遷移

注意

g和vrs是不同的數字。vrs是作者給關卡定的版本(LevelMeta.LevelVersion),與格式無關。不要手動修改g:g高於構建所知的檔案完全無法讀取