akashic-games / akashic-engine

The core library of Akashic Engine
MIT License
88 stars 9 forks source link

再生中のAudioAssetを別Sceneでロードできない #329

Closed asmka closed 2 years ago

asmka commented 2 years ago

一般ユーザによる問い合わせ先がこちらでなかったらすみません。 掲題は大分かい摘んでいるので以下に詳細を記します。 推奨する対処方法があれば教えていただきたいです。

環境

エラーメッセージ(Chrome)

Uncaught AssertionError: AssetManager#peekLiveAssetByAccessorPath(): No audio asset for /assets/audio/se
    at Object.createAssertionError (engineFilesV3_1_4.js:1)
    at AssetManager.peekLiveAssetByAccessorPath (engineFilesV3_1_4.js:1)
    at AssetAccessor.getAudio (engineFilesV3_1_4.js:1)
    at main.js:32
    at Trigger.fire (engineFilesV3_1_4.js:1)
    at Scene._fireLoaded (engineFilesV3_1_4.js:1)
    at Game._flushPostTickTasks (engineFilesV3_1_4.js:1)
    at Game.tick (engineFilesV3_1_4.js:1)
    at Game.game.tick (ServeGameContent.ts:101)
    at GameLoop._onFrameNormal (engineFilesV3_1_4.js:1)

条件(以下を全て満たす)

再現コード

/assets/audio/seは3秒以上の音声ファイル

export = function main(): void {
  const scene1 = new g.Scene({
    game: g.game,
    assetPaths: ["/assets/audio/se"],
  });

  const scene2 = new g.Scene({
    game: g.game,
  });

  const scene3 = new g.Scene({
    game: g.game,
    assetPaths: ["/assets/audio/se"],
  });

  scene1.onLoad.add(() => {
    const audio = scene1.asset.getAudio("/assets/audio/se");
    audio.play();
    scene1.setTimeout(() => {
      g.game.replaceScene(scene2);
    }, 1000);
  });

  scene2.onLoad.add(() => {
    scene2.setTimeout(() => {
      g.game.replaceScene(scene3);
    }, 1000);
  });

  scene3.onLoad.add(() => {
    const audio = scene3.asset.getAudio("/assets/audio/se");
    audio.play();
  });

  g.game.pushScene(scene1);
};

備考

xnv commented 2 years ago

詳細なご報告ありがとうございます。手元で現象を再現できました。見る限りエンジンの不具合のようです。AudioAsset は、シーンの遷移時に再生中だった場合に破棄を遅延したり、さらに遅延中に改めて必要になったら破棄をキャンセルしたりするのですが、その過程で一部データの整合性が崩れてしまっているようです。

修正を進めますが、暫定的にこのトラブルを回避いただく方法としては、次のどちらかが使えるかと思います:

(a) は、 game.json の /assets/audio/se のアセット宣言に 、次のように "global": true, という指定を追加して、当該アセットをグローバルアセットにするものです。

{
  "中略": "...",
  "assets": {
    "/assets/audio/se": {
      "type": "audio",
      "path": "assets/audio/se", // 当該のアセット
      "global": true, // ←これを追加
      "その他の値": "..."
    }
  }
}

グローバルアセット はゲーム開始時から (各シーンで指定せずとも) ずっとロードされたままになるアセットです。そのため AudioAsset の破棄 (のキャンセル) に関連する今回のトラブルを回避できます。代償として、不要なシーンでもずっとそのアセットはロードされっぱなしになりますが、.js 側を変更せずに対処できるので暫定対処としてはこれが一番良さそうです。

別の方法としては、(b) getAudioById() を使う方法があります。確認したところ、 getAudio() がおかしくなる状況でも、 getAudioById() は動作するためです。ただ、assets/ ディレクトリに置いた時のアセット ID は不定とさせていただいている ため、この方法を使うには audio/ ディレクトリにファイルを移動する必要がありますし、また .js 側も変更が必要になります。

asmka commented 2 years ago

丁寧なご回答ありがとうございます。 グローバルアセットについては認識していなかったため大変参考になりました。 お勧めの通り、(a) パターンにて対処しようと思います。