本文へスキップ
ドキュメント
このページの内容

レベルフォーマット

レベルはフォルダーです:中身は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は、level.blobとmetadata.jsonを並べて持っています

metadata.jsonはあえて別ファイルにしています。カタログは1000個のレベルを、1000個の小さなメタデータファイルから表示します。そのためにレベルを1つも開く必要はありません

リソース

リソースはuriとuri_type(ResourceUriType)の組で指定します:

値種類ファイルの場所
1LevelPathレベルフォルダーの中。この種類だけがレベルを持ち運び可能にする
2AbsolutePathこのデバイス上の別の場所
3DirectUrlネットワークからダウンロード
4StreamingAssetsレベルではなくゲームに同梱

AbsolutePathは、曲を元にレベルを作成したときに生じます。アーカイブをエクスポートすると、そのようなファイルは中にコピーされます。詳細:アーカイブと保護

カバーは、他のリソースと同じくLevelMeta.LevelLogoから取得できます。logo.pngになるかlogo.jpgになるかは、元の画像の名前ではなく、ファイル自体のバイトで決まります

カバーの作者情報の記録は、resource_typeが9(LevelLogo)のResourceMetaです。resource_idもresource_guidも入っていません。レベルのカバーは1つなので、種類だけでアドレスが完全に決まります

AIに関する申告

LevelMetaと各ResourceMetaは、AiGeneration型のai_generatedを持ちます:

値名前意味
0NotSpecified何も申告されておらず不明。デフォルト値で、キーがない場合もこの値として読まれる
1NoAI生成を使わずに作ったと申告
2YesAI生成だと申告

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は常にインデントなしのコンパクトな形で書かれます。目で読むときは、テキストエディターで整形してください

キーの長さは、そのキーがレベル内に何回現れるかで決まります:

  • ファイルに1回だけ現れるキーは、snake_caseの完全な単語で書きます:level_id、min_generation
  • 何千回も繰り返されるキーは数文字に短縮します:objs、f、e、v

キーはレベルファイルのバイト数の52%を占めるので、これは好みの問題ではなくルールです。すべてのキーはNames.csで宣言されています

ポリモーフィックな値は[tag, data]として書かれます。プレーンな文字列は[0,{"v":"Author"}]、ローカライズされた文字列は[1,{"strs":[...]}]です

ObjectIdのようなIDのラッパーは、そのままの数値または文字列として書かれます

ID

整数IDの符号には意味があります。0は常に「未設定」を意味します:

IDJSONのキー正負
ObjectIdid、親はpidレベルのオブジェクトゲームのオブジェクト:-1はカメラ、-2はローカルプレイヤー、-3はプレハブのテンプレートのルート
リソースIDtxid、fnid、auid、byid、ttidゲームに同梱されたリソースレベル独自のリソース
オーディオトラックのAudioIdaidこのレベルのトラック使われない

pidが0なら、オブジェクトに親はありません。-3より小さい数はゲームの実行中にだけ存在し、ファイルには書かれません

Marker、Checkpoint、BeatSegmentにはIDがありません。そのアドレスは時間上の位置です。マーカーとチェックポイントはフレームf、ビートセグメントはそのスパンspの最初のフレームです(セグメントは重なりません)。こうしたオブジェクトを動かすと、アドレスも変わります

プレハブのオーバーライドは、フィールドをJSONのキーではなく番号で指定します。配置はオーバーライドをmodリストに持ちます。各オーバーライドのkeyは3つの数を持ちます:

  • id:レベル内のコピーではなく、テンプレート内のオブジェクト
  • f:フィールドの恒久的な番号
  • i:リストフィールドの要素、またはフィールド全体なら-1

番号はフィールドに永久に結び付いています。そのフィールドのJSONキーを変更しても、すでにレベルに保存されたオーバーライドは壊れません

外側の「g」

シリアライズのルートはすべて、2つのキーを持つエンベロープで包まれています:

{"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を持つファイルは、まったく読み込まれません