タイルセット

タイルセットは、ゲームにグラフィック画像を提供します。各タイルセットは、画像スプライトが収められた1つ以上の「タイルシート」と、スプライトシートの内容をゲーム内の様々なエンティティ(実体)にどのように紐付けるかを記述した tile_config.json ファイルで構成されます。また、メタデータを提供する tileset.txt ファイルも含まれます。

コンポジット・タイルセット

2019年10月以前、タイルセットは各タイルシートが完全に結合された状態でリポジトリに提出され、 tile_config.json 内のスプライトのインデックス番号は手計算で割り振る必要がありました。2019年10月以降は、個別のスプライト画像ファイルと、それらのファイル名を使用したタイルエントリ用のJSONファイルを含むディレクトリとして提出できるようになりました。コンパイル時に実行されるPythonスクリプトが、個別の画像をタイルシートに統合し、ファイル名をスプライトのインデックス番号に変換した上で、各エントリを1つの tile_config.jsonに集約します。

本ドキュメントでは、統合済みのタイルシートで構成されるタイルセットを「レガシー・タイルセット」、個別のスプライト画像で構成されるものを「コンポジット・タイルセット」と呼びます。

tools/gfx_tools/decompose.py

レガシー・タイルセットをコンポジット・タイルセットに変換するPythonスクリプトです。tile_config.jsonを読み込み、各スプライトのインデックスに半自動的にファイル名を割り当てます。その後、インデックスによる参照をすべてファイル名に書き換えtile_config.json を多数の小さなタイルエントリJSONファイルに分割し、各スプライトを個別の画像ファイルとして書き出します。

画像処理のために pyvips が必要です。

必須の引数は1つで、タイルセットのディレクトリへのパスを指定します。 例えば、python tools/gfx_tools/decompose.py gfx/ChestHole16Tilesetと実行すると、レガシー形式の ChestHole16 タイルセットをコンポジット形式に変換します。

decompose.pyを実行すれば、コンポジット形式として機能するために必要なディレクトリ階層とファイル名は作成されます。しかし、それらはあくまで機械的に生成されたものであり、お世辞にも整理されているとは言えません。新しくコンポジット・タイルセットを作成する場合は、より意味の通じるファイル名を使用し、管理しやすい構成にすることを推奨します。

decompose.py を頻繁に実行する必要はないはずです。レガシー形式からコンポジット形式への変換が必要になるのは、通常は一度きりだからです。

tools/gfx_tools/compose.py

これは、コンポジット・タイルセット用のタイルシートを作成するPythonスクリプトです。このスクリプトは、タイルセットのディレクトリ内にあるpngs_ で始まる名前の全ディレクトリから、スプライト画像とtile_entry JSONファイルを読み込みます。そして、スプライトのファイル名とインデックス番号の対応表を作成し、個別の画像をタイルシートへと統合します。同時に、tile_entries 内のファイル名による参照をすべてインデックス番号へと書き換え、最終的にそれらを1つのtile_config.jsonにまとめ上げます。

decompose.py と同様に、画像処理を行うために pyvips が必要です。

なお、実行後も元のスプライト画像ファイルや tile_entry JSONファイルは上書きされず、そのまま保持されます。

ディレクトリ構造

各コンポジット・タイルセットには、pngs_tree_32x40pngs_overlay のように、名前が pngs_で始まるディレクトリが1つ以上含まれています。これらは画像ディレクトリです。画像ディレクトリ内のスプライトはすべて幅と高さが一致している必要があり、実行時に1つのタイルシートへと統合されます。

タイルセットの開発者は、画像ディレクトリ名にスプライトの寸法を含めることが推奨されていますが、これは必須ではありません。例えば pngs_overlay よりも pngs_overlay_24x24 という命名が好ましいですが、どちらも許容されます。画像ディレクトリごとに個別のタイルシートが作成されますが、パフォーマンスの観点からはタイルシートをなるべく大きく(数を少なく)した方がよいため、画像ディレクトリの数は最小限に抑えることが強く推奨されます。

各画像ディレクトリ内には、サブディレクトリ、tile_entry JSONファイル、およびスプライト画像ファイルを階層的に配置できます。これらのファイルの配置や命名に制限はありませんが、拡張タイルシート用の tile_entry JSONファイルだけは、必ず画像ディレクトリの直下に配置してください。サブディレクトリの使用は必須ではありませんが、管理を容易にするために活用することをお勧めします。

tile_entry JSON

tile_entry JSONは、1つ以上のゲーム内エンティティ(実体)を1つ以上のスプライトにどのように紐付けるかを記述した「辞書」形式のデータです。最もシンプルな構成では、単一のゲームエンティティに対して、単一の前景スプライト、任意の背景スプライト、および回転の有無を指定します。

例えば、以下のように記述します。

{                                           // これはオブジェクトであり、リスト化する必要はありません
    "id": "mon_cat",                        // このスプライトが表すゲーム内のID
    "fg": "mon_cat_black",                  // 前景スプライト名(ファイル名)
    "bg": "shadow_bg_1",                    // 背景スプライト名。常に単一の値です
    "rotates": false                        // 車両パーツのように回転する場合は true に設定します
}

"id""fg"、および "bg" に指定する値は、同一の画像ディレクトリ内、あるいは異なる画像ディレクトリ間で繰り返し使用することができます。"fg""bg" のスプライト画像は、別の画像ディレクトリから参照することも可能ですが、そのスプライト自体は、同じ幅と高さを持つスプライトが収められた画像ディレクトリ内に保存されていなければなりません。

"id" はリスト形式で記述することも可能で、"id": ["vp_door", "vp_hddoor"] のように、複数のゲームエンティティで同じスプライトを共有させることができます。"id" には、ゲーム内に存在するあらゆる車両パーツ、地形、家具、アイテム、モンスターを指定できます。

また、以下の特殊なIDが用意されています。

  • "player_female", "player_male": プレイヤーの自キャラ(アバター)用スプライト。
  • "npc_female", "npc_male": NPC用スプライト。
  • "unknown": スプライトが定義されていないエンティティが表示される際に、代わりに使用される既定のスプライト。

特殊なIDである "player_female""player_male""npc_female""npc_male" は、プレイヤーキャラクターやNPCのスプライトを特定するために使用されます。また、"unknown" という特殊なIDは、対応するスプライトが存在しないエンティティを表示する際の代用画像として機能します。

任意のIDに対して、特殊な接尾辞である _season_spring(春)、_season_summer(夏)、_season_autumn(秋)、_season_winter(冬)を付け加えることで、その季節に応じた外見のバリエーションを作成できます。この設定をすると、指定した季節においてその画像が優先的に表示されます。 例: "id": "mon_wolf_season_winter" (冬の装いになったオオカミ)

特殊な接頭辞である overlay_mutation_overlay_female_mutation_overlay_male_mutation_ を、ゲーム内の任意の特性やCBMのIDの前に付けることができます。これにより、プレイヤーやNPCのスプライトの上に重ねて表示される「オーバーレイ画像」を指定し、それらの変異や義体を有していることを視覚的に表現できます。

特殊な接頭辞である overlay_worn_overlay_female_worn_overlay_male_worn_ を、ゲーム内の任意のアイテムIDの前に付けることができます。これにより、プレイヤーやNPCのスプライトの上に重ねて表示されるオーバーレイ画像を指定し、そのアイテムを「着用」している状態を表現できます。

特殊な接頭辞である overlay_wielded_overlay_female_wielded_overlay_male_wielded_ を、ゲーム内の任意のアイテムIDの前に付けることができます。これにより、プレイヤーやNPCのスプライトの上に重ねて表示されるオーバーレイ画像を指定し、そのアイテムを「手に持っている(構えている)」状態を表現できます。

"fg"(前景)と "bg"(背景)には、あらかじめ回転させた2方向または4方向のバリエーションをリスト形式で指定することもできます。 例えば、"bg": ["t_wall_n", "t_wall_e", "t_wall_s", "t_wall_w"](北・東・南・西の壁)や、"fg": ["mon_dog_left", "mon_dog_right"](左向き・右向きの犬)といった指定が可能です。

"fg"(前景)と "bg"(背景)には、「重み付け(weight)」が設定された辞書のリストを指定することも可能です。これにより、複数の候補の中からランダムに画像を表示させることができます。また、それぞれの候補自体を、先ほど説明した「回転バリエーション用のリスト」にすることも可能です:

"fg": [
    { "weight": 50, "sprite": "t_dirt_brown"},       // 53個中50個のタイルで表示される(高確率)
    { "weight": 1, "sprite": "t_dirt_black_specks"}, // 53個中1個のタイルで表示される
    { "weight": 1, "sprite": "t_dirt_specks_gray"},
    { "weight": 1, "sprite": "t_patchy_grass"}       // ファイル名は自由に命名可能です
],

"multitile" は任意のフィールドです。この値が存在し、かつ true に設定されている場合、必ず "additional_tiles" というリストを記述する必要があります。このリストには、壁の接続部分やアイテムの破損したバージョンなど、そのタイルに関連付けられたエンティティやスプライトを定義する辞書を1つ以上含めます。リスト内の各辞書には、前述のルールと同様に "id" フィールドと "fg" フィールドを持たせます。この "fg" フィールドには、単一のファイル名、ファイル名のリスト、または先ほどのような重み付け設定の辞書リストを指定できます。

tile_entry.json ファイルの記述形式に関する具体的な例ですね。一つのファイルに複数の定義をまとめる方法は、タイルセットの管理効率を上げるための標準的な手法です:

[
    { "id": "mon_zombie", "fg": "mon_zombie", "bg": "mon_zombie_bg", "rotates": false },
    { "id": "corpse_mon_zombie", "fg": "mon_zombie_corpse", "bg": "mon_zombie_bg", "rotates": false },
    { "id": "overlay_wielding_corse_mon_zombie", "fg": "wielded_mon_zombie_corpse", "bg": [], "rotates": false }
]

ファイル内のタイルエントリをリスト化して管理することは、情報の整理に非常に役立ちますが、技術的な制約はありません。そのため、互いに全く関係のないエントリをすべて同じ一つのファイルに記述しても、動作上の問題(コンパイルエラーなど)が発生することはありません。

拡張タイルシート( tile_entry JSON)

タイルシートには、MODなどによって提供される「拡張タイルシート」を含めることができます。各拡張タイルシートの定義は、単一の "id" 値を持ち、"rotates": false、かつ "fg": 0 と指定されます。 拡張用の tile_entry JSONは、"fg" に整数値(数値の 0)を使用する唯一の例外であり、その値は必ず 0 でなければなりません。また、これら拡張用の tile_entry JSONファイルは、各画像ディレクトリ直下に配置する必要があります。

スプライト画像

一つの画像ディレクトリ内に含まれるすべてのスプライトは、そのディレクトリ内の他のすべてのスプライトと、高さおよび幅が同一でなければなりません。

画像ディレクトリ内のスプライトは、タイルセット制作者の好みに合わせて、自由にサブディレクトリに整理して配置することができます。スプライトのファイル名に制約は一切なく、制作者にとって分かりやすい命名規則を自由に採用してください。

タイルセットの読み込み後、config/debug.log には、そのタイルセット内でスプライトが不足しているすべてのエンティティが、スペース区切りのリストとして出力されます。なお、"looks_like" の定義によってスプライトが割り当てられているエンティティは、このリストには表示されません。

tile_info.json

各コンポジット・タイルセットには、必ず tile_info.json が含まれていなければなりません。その構成は以下の通りです:

[
  {
    "width": 32,
    "pixelscale": 1,
    "height": 32
  },
  {
    "1_tiles_32x32_0-5199.png": {}
  },
  {
    "2_expan_32x32_5200-5391.png": {}
  },
  {
    "3_tree_64x80_5392-5471.png": {
      "sprite_offset_x": -16,
      "sprite_offset_y": -48,
      "sprite_height": 80,
      "sprite_width": 64
    }
  },
  {
    "4_fallback_5472-9567.png": { "fallback": true }
  }
]

最初の辞書の記述は必須であり、タイルセット内のすべてのタイルシートにおけるデフォルトのスプライトの幅と高さを指定します。各画像ディレクトリに対しては、タイルシートのPNG名をキーとした個別の辞書を用意する必要があります。もしそのタイルシートがデフォルトのスプライトサイズを使用しており、特殊なオフセット(表示位置のズレ)も設定しない場合は、タイルシート名をキーとした値の中身は空の辞書( {} )で構いません。そうでない場合は、スプライトのオフセット、高さ、および幅を含む辞書を記述してください。

"fallback" は特殊なキーであり、指定する場合は値を true に設定します。あるタイルシートがフォールバックとして指定されると、それはASCII文字を補完するためのタイルシートとして扱われます。compose.py は、このフォールバック用タイルシートをタイルセットの最後に合成します。また、もし tile_info.json 内に "fallback" エントリが存在しない場合、スクリプトは自動的に tile_config.json"fallback.png" を追加します。

"filler" は特殊なキーであり、指定する場合は値を true に設定します。あるタイルシートがフィラーとして指定されると、そのディレクトリ内のエントリは、非フィラー(通常)のディレクトリで既に同じIDが定義されている場合には無視されます。また、同じフィラーディレクトリ内で既にIDが定義されている場合も同様に無視されます。さらに、フィラーディレクトリ内のPNGファイルが、非フィラーディレクトリ内のPNGファイルと同じ名前を持っている場合も無視されます。 フィラータイルシートは、タイルセットのアートワークをアップグレードする際に便利です。古くて低品質な画像をフィラーシートに置いておけば、より高品質な画像が非フィラーシートに追加されるたびに、古い画像は自動的に置き換えられます。

レガシー・タイルセット

タイルシート

各タイルシートには、幅と高さが統一されたスプライトが1つ以上含まれます。また、各タイルシートは、1行につき正確に16個のスプライトが並んだ構造を1行以上持っている必要があります。スプライトのインデックス 0 は特殊な扱いであり、タイルセット内の最初のタイルシートにある最初のスプライトは「空白」にする必要があります。インデックス番号は、各シートを通じて連続しており、新しいシートに移ってもリセットされずに増え続けます。そのため、インデックス 32 は最初のシートの「3行目・1番目」のスプライトを指します。仮に最初のシートに 320 個のスプライトが含まれている場合、インデックス 352 は「2枚目のシートの3行目・1番目」のスプライトを指すことになります。

tile_config

各レガシー・タイルセットには、スプライトシートの内容を各種タイルIDや異なる向きなどにどのように対応させるかを記述した tile_config.json が存在します。また、変異を表示する際に使用されるオーバーレイの表示順序を制御することも可能です。この順序指定を利用して、mutation_ordering.json で提供されているデフォルトの順序を上書きすることができます。例:

{ // ファイル全体が一つのオブジェクトです
  "tile_info": [ // tile_info は必須項目です
    {
      "height": 32,
      "width": 32,
      "iso": true, //  オプション。アイソメトリック形式であることを示します。既定値は false。
      "pixelscale": 2 //  オプション。タイルセットをリサイズするための倍率を設定します。既定値は 1。
    }
  ],
  "tiles-new": [ // tiles-new はタイルシートの配列です
    { //   または、単一の "tiles" 配列でも可能です
      "file": "tiles.png", // グリッド状にスプライトが並んだ画像ファイル名
      "tiles": [ // タイルごとの定義を格納する配列
        {
          "id": "10mm", // ID:ゲーム内の要素とスプライトを紐付けます
          "fg": 1, //   接頭辞がない場合は主にアイテムを示します
          "bg": 632, // fg(前景)と bg(背景)は画像内のスプライト番号です
          "rotates": false
        },
        {
          "id": "t_wall", // "t_" は地形(terrain)を意味します
          "fg": [2918, 2919, 2918, 2919], // 数値が2つまたは4つの場合は、回転済みのスプライト指定です
          "bg": 633,
          "rotates": true,
          "multitile": true,
          "additional_tiles": [ // 接続・合成されたバージョンのスプライト
            { //   またはバリエーション。詳細は後述。
              "id": "center",
              "fg": [2919, 2918, 2919, 2918]
            },
            {
              "id": "corner",
              "fg": [2924, 2922, 2922, 2923]
            },
            {
              "id": "end_piece",
              "fg": [2918, 2919, 2918, 2919]
            },
            {
              "id": "t_connection",
              "fg": [2919, 2918, 2919, 2918]
            },
            {
              "id": "unconnected",
              "fg": 2235
            }
          ]
        },
        {
          "id": "vp_atomic_lamp", // "vp_" 車両パーツ
          "fg": 3019,
          "bg": 632,
          "rotates": false,
          "multitile": true,
          "additional_tiles": [
            {
              "id": "broken", // バリアントスプライト
              "fg": 3021
            }
          ]
        },
        {
          "id": "t_dirt",
          "rotates": false,
          "fg": [
            { "weight": 50, "sprite": 640 }, // 重み付きランダム・バリエーション
            { "weight": 1, "sprite": 3620 },
            { "weight": 1, "sprite": 3621 },
            { "weight": 1, "sprite": 3622 }
          ]
        },
        {
          "id": [
            "overlay_mutation_GOURMAND", // 変異用オーバーレイ
            "overlay_mutation_male_GOURMAND", // 特定の性別用
            "overlay_mutation_active_GOURMAND" // 発動中の変異用
          ],
          "fg": 4040
        }
      ]
    },
    { // tiles-new 内の2番目のエントリ
      "file": "moretiles.png", // 別のスプライトシート
      "tiles": [
        {
          "id": ["xxx", "yyy"], // 2つのIDを一度に定義
          "fg": 1,
          "bg": 234
        }
      ]
    }
  ],
  "overlay_ordering": [
    {
      "id": "WINGS_BAT", //変異名。文字列、または文字列の配列で指定。
      "order": 1000 // 範囲は 0 - 9999。9999が最前面(一番上)に描画される。
    },
    {
      "id": ["PLANTSKIN", "BARK"], // 変異の名前。単一の文字列、または文字列の配列のいずれかで記述します。
      "order": 3500 // (指定された)順序(order)は、配列内のすべての項目に適用されます。
    },
    {
      "id": "bio_armor_torso", // 義体(CBM)の表示順序も同様に制御される。
      "order": 500
    }
  ]
}