複数アセットをまとめて扱う

ゲームが複雑になると、利用するアセットも多くなり、それを扱うコードも煩雑になりがちです。 Akashic Engine は、アセットを複数まとめて管理しやすくするためのいくつかの機能を提供しています。

# assets/ ディレクトリ

これまでの文書では、画像アセットは image/ 、オーディオアセットは audio/ など、アセットの種類別にフォルダが分かれていました。 これは akashic scan asset コマンドが、それらのディレクトリを検索する仕様になっているためです。 しかし複雑なゲームでは、アセットを種類ではなく意味で分類・整理したいかもしれません。 たとえばステージごとにマップデータと BGM と画像をまとめて保存する、という形にしたくなるかもしれません。

このような場合には、 assets/ ディレクトリが利用できます。 これは akashic scan asset コマンドがアセットを検索するディレクトリの一つです (akashic-cli v1.14.3 から) 。 ただし他のディレクトリと次の点で異なります:

  • アセットの種別が、ファイルの拡張子から自動的に決定される
    • image: 拡張子が png, jpg, jpeg のファイル
    • audio: 拡張子が ogg, m4a, aac のファイル
    • script: 拡張子が js のファイル
    • text: 上記以外の拡張子のファイル
  • アセット ID が不定になる

これによりたとえば次のようなフォルダ構造が可能になります:

  • assets/
    • stage1/
      • map.json
      • background.png
      • bgm.m4a
      • bgm.ogg
    • stage2/
      • map.json
      • background.png
      • bgm.m4a
      • bgm.ogg

assets/ 以外のディレクトリでは、アセット ID はファイル名の basename (拡張子を抜いた部分) になります。 しかしこれには「basename が重複するアセットを一切作れない」という制限がつきます。 たとえば上の例に似た audio/stage1/bgm.m4aaudio/stage2/bgm.m4a を置くと、アセット ID が bgm で重複してエラーになります。

assets/ ディレクトリは、この問題を回避するため、そもそもアセット ID を保証しません。 これにより basename の重複したアセットを許容します。ただしアセット ID でのアクセスはできなくなります (不定のため)。 Akashic Engine v3 以降では、後述のとおりアセットのファイルパスを使ってアセットを取得できるため、そちらを利用することになります。

# パスによるアセット読み込み

これまでの例では、 g.Scene の生成時に assetIds として利用するアセットの ID を指定していました。

const scene = new g.Scene({
  game: g.game,
  assetIds: ["character01", "bgm01"]
});

これの代わりに、 assetPaths を使うと、ファイルパスで読み込むアセットを指定することができます。 ここでファイルパスは、 game.json のあるディレクトリをルート (/) とする スラッシュ区切りの絶対パスです (以降これをアセットパス形式と呼びます)。

const scene = new g.Scene({
  game: g.game,
  assetPaths: ["/image/character01.png", "/assets/**/*"]
});

この例でも使っているように、アセットパス形式では glob のサブセット文法(**, *, ?) をサポートしています。

  • ** はあらゆるファイルや 0 個以上のディレクトリ、サブディレクトリにマッチします。
  • * は 0 文字以上任意の文字列にマッチします。
  • ? は任意の 1 文字にマッチします。

上記サンプルコードの assetPaths のグロブ指定("/assets/**/*")では、assets ディレクトリに下記のようなファイルが存在する場合、全てのファイルが対象となります。

  • /assets/scenarios/scenario1.txt
  • /assets/scenarios/scenario2.txt
  • /assets/map.json

(対応するアセットが game.json に登録されている必要は引き続きあります)

# パスによるアセット取得

ここまでの文書では、主に scene.asset.getImageById() などの関数を使い、アセット ID からアセットを取得していました。 シーン生成時の assetPaths 同様、アセットの取得でも ID ではなくパスを利用することができます。

これには scene.asset.getImage(), scene.asset.getAudio() などを利用します。

// 画像アセットの取得
const playerImage = scene.asset.getImage("/assets/player/image.png");
const player = new g.Sprite({
  scene: scene,
  src: playerImage,
  width: playerImage.width,
  height: playerImage.height
});

// オーディオアセットの取得
// (game.json での記述同様、オーディオアセットに限り、拡張子抜きで指定する必要があります)
const bgm = scene.asset.getAudio("/audio/bgm01");
bgm.play();

またこれも assetPaths 同様、アセットパスにはグロブを利用して複数のアセットを一括で取得することもできます。 これには getAllImages(), getAllAudios() などのメソッドを利用します。戻り値は配列になります。

// 複数の画像アセットの取得
const thumbnails = scene.asset.getAllImages("/assets/**/*.png");
for (let i = 0; i < thumbnails.length; i++) {
  const thumbnail = new g.Sprite({
    scene: scene,
    src: thumbnails[i],
    width: thumbnails[i].width,
    height: thumbnails[i].height
  });
  ...
}

// 複数のオーディオアセットの取得
const audios = scene.asset.getAllAudios("/audio/bgm*");
for (let i = 0; i < audios.length; i++) {
    audios[i].stop();
}