gracetory’s blog

東池袋にある合同会社グレストリのエンジニアブログです

monaca+phaser3で音を鳴らす

f:id:grnishi:20200725233324p:plain

はじめに

「残暑が厳しい」なんて言いますが、言葉の意味として立秋を過ぎた頃からを残暑と言います。2020年ですと8月7日。専ら暑さ真っ盛り。最近だとお盆過ぎたら涼しくなってくるかなーという感じですが、昔はお盆前には涼しくなってきたんですかね。grnishiです。

本題

monaca(cordovaでも良いですが)とphaser3でiOS、Android用のゲームアプリを作っています。

https://ja.monaca.io/

https://phaser.io/

いくつか障壁はあるのですがその中でも音楽の再生周りについて簡単にまとめます。

環境

Cordovaバージョン: 9.0.0

iOSプラットフォーム: 5.1.1

Androidプラットフォーム: 8.1.0

Gradleバージョン: 4.10.3

Xcodeバージョン: 11.3

Monaca Core Utility バージョン 2.1.0

Phaser 3.24.1

普通にPhaser3で音を再生

function preload () {
  this.load.audio("bgm", [
      "assets/audio/bgm.ogg",
      "assets/audio/bgm.mp3"
  ]);
}

function create () {
  var bgm = this.sound.add("bgm");
  bgm.play();
}

普通に再生するならこれで問題ありません。

しかし、ChromeやSafariでは自動再生できません。何かしらタップさせれば良いのですが、起動したらそのままBGM再生というゲームにありがちな表現が出来ません。

iframeで無音を使った回避策も試してみましたがそれでもダメでした。

Mediaプラグイン

Monaca公式ドキュメントにも記載されているMediaプラグインを使った方法。

docs.monaca.io

var bgm= null;
document.addEventListener("deviceready", function() {
  bgm= new Media ("assets/audio/bgm.mp3");
  bgm.play({numberOfLoops: 0});
}, false);

これで自動再生は可能になります。しかし、2音同時再生ができません。ゲームを作るのであれば、BGMとSEを同時に鳴らす必要があります。

Cordova Native Audio Plugin

github.com

document.addEventListener("deviceready", function() {
  window.plugins.NativeAudio.preloadComplex("bgm", "assets/audio/bgm.mp3", 1, 1, 0);
  window.plugins.NativeAudio.play("bgm");
}, false);

これで2音同時再生ができます。しかし、ミュートのスイッチをONにしても音が鳴ります。というわけでres/cordova-plugin-nativeaudio/src/ios/NativeAudio.mの48行目にある

[session setCategory:AVAudioSessionCategoryPlayback error:nil];

をコメントアウトするとOKです。

問題発生

余談ですが、Cordova Native Audio Pluginを入れてAndroid実機で確認すると起動時にクラッシュしました(Android 10)

OSのバージョンが関係あるかわかりませんが。

クラッシュログを確認すると

java.lang.RuntimeException: Unable to resume activity {jp.co.gracetory.hogehoge/jp.co.gracetory.hogehoge.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.util.ArrayList.isEmpty()' on a null object reference
~~中略~~
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.util.ArrayList.isEmpty()' on a null object reference
at com.rjfun.cordova.plugin.nativeaudio.NativeAudio.onResume(NativeAudio.java:330)
~~後略~~

と出力されていました。

該当のソースコードを確認しました。

327    @Override
328    public void onResume(boolean multitasking) {
329        super.onResume(multitasking);
330        while (!resumeList.isEmpty()) {
331            NativeAudioAsset asset = resumeList.remove(0);
332            asset.resume();
333        }
334    }

なぜこうなるのかしっかりと調査していませんが、とりあえず修正。

327    @Override
328    public void onResume(boolean multitasking) {
329        super.onResume(multitasking);
330        if (resumeList != null) {
331            while (!resumeList.isEmpty()) {
332                NativeAudioAsset asset = resumeList.remove(0);
333                asset.resume();
334            }
335        }
336    }

とりあえず起動できました。

どれが良いのか?

作りたいアプリによってどれを使うべきかは異なります。

下記簡単に機能をまとめます。

機能 Phaser3 Mediaプラグイン NativeAudioPlugin
再生
停止
一時停止
ボリューム変更
ミュート
ループ再生
シーク
ディレイ
デチューン
再生速度変更
録音
Monacaプレビュー

まとめ

本記事に限らず、オープンソースで活発に開発されているものは実現方法が一つではありません。

しっかりと調査をして、どの技術を採用するべきかを決定するのが重要です。