結果だけでなく過程も見てください

日々の奮闘を綴る日記です。

RPGツクールMVで効果音(SE)が鳴っているかどうかの判定および特定の効果音だけを停止する方法 他

RPGツクールMVいじり二日目です。昨日の記事の続きです。
結論から言うと、効果音(SE)が鳴っているかどうかの判定はできませんでしたが、
やりたいこと(効果音を多重で再生せずループさせる)はできました。

RPGツクールMVの内情はちんぷんかんぷんなので、誤りや慣習などはご教示いただければと思います。

効果音(SE)が鳴っているかどうかの判定

冒頭でも言った通り、うまく動作しなかったのですが、経緯だけご紹介します。

まずrpg_managers.jsのファイルの先頭あたりに以下の一文を追加しておきます。

const path = require('path');

rpg_managers.jsに以下のメソッドを追加します。

AudioManager.isSEPlaying = function(sename) {
  if (sename)
  {
    var seBuffers = this._seBuffers.filter( 
                        function(audio)
                        {
                            var audioname = path.basename(audio.url, path.extname(audio.url));
                            return ((audioname === sename) && audio.isPlaying())
                        });

    return (seBuffers.length != 0)
  }
  else
  {
    return false
  }
};

そのまんまなのですが、SE名が一致し、かつ再生中のエントリを
管理配列であるthis._seBuffersから検索して取得しています。
1つでも見つかれば再生中、そうでなければ停止中と判定しています。

あまりわかっていませんが実機検証の結果、効果音(SE)再生直後などは、実際には再生が開始されていてもこの結果がfalseになることがあるみたいで、再生し始めのときに不当にfalseが数回返る→数回分は効果音が被って再生されてしまう…という結果になってしまいました。これを回避する方法は、サウンドバッファのシーク位置とかを見ればいけそうな気がしますがめんどくさくて見つからなかったということにしてます。それよりも、後述する方法を使えばもっとシンプルにやりたいことが実現できそうでした。

効果音(SE)を二重以上で被らずに再生させる方法

単純に、WebAudioオブジェクトのplay系関数の変数にtrueを渡すだけでうまくいきそうな気がしました。

rpg_managers.jsに以下の内容を追加します。

AudioManager.playSeLoop = function(se) {
    if (se.name) {
        this._seBuffers = this._seBuffers.filter(function(audio) {
            return audio.isPlaying();
        });
        var buffer = this.createBuffer('se', se.name);
        this.updateSeParameters(buffer, se);
        buffer.play(true);
        this._seBuffers.push(buffer);
    }
};

なんてことはない。通常のplaySeと比べplayに渡す引数をfalse→trueと変更しているだけです。
これにより効果音の被りを気にする必要はなくなりました。

または、コアスクリプトの関数を書き換えてもいいのであれば、以下のようにplaySeメソッドの
第二引数でループするかどうかを指定できるようにし、
引数なし(つまり既存でコールされている部分)についてはデフォルト引数で
必ずループさせない(既存と同じ動作)ようにさせても良いです。
個人的にはこちらの方法を推奨します。似たような関数を2つ作成したくはないので。

// 既存のplaySeメソッドに第二引数を追加
AudioManager.playSe = function(se, bLoop=false) {
    if (se.name) {
        this._seBuffers = this._seBuffers.filter(function(audio) {
            return audio.isPlaying();
        });
        var buffer = this.createBuffer('se', se.name);
        this.updateSeParameters(buffer, se);
        buffer.play(bLoop);
        this._seBuffers.push(buffer);
    }
};

もともとやりたいことはSE再生中は、再度同じSEを鳴らさないということで、それを実現する
ためにSEの再生時間分だけフレームをカウントしていました。
つまりループ再生ができれば上述した課題は自動的に解決され、フレームをカウントする必要がなくなります。
# 勢いで書いたエントリなのでもうちょっと検証しますが。

特定の効果音(SE)だけ停止する方法

先日のエントリだと、プレイヤーが停止したらすべての効果音を停止させる動きをしていましたが、
関係ないSEも止めちゃうってそりゃないでしょということで、足跡の効果音だけを停止させることにします。

rpg_managers.jsに以下の関数を追加します。

AudioManager.stopPlayedSE = function(sename)
{
    if (sename)
    {
        for ( var i = 0; i < this._seBuffers.length; ++i )
        {
            var file = path.basename(this._seBuffers[i].url, path.extname(this._seBuffers[i].url))
            if ( file == sename )
            {
                this._seBuffers[i].stop()       // 止めるべきSEを発見した
                this._seBuffers.splice( i, 1 )  // 止めた要素は削除してしまう
                break
            }
        }
    }
}

filterを使うことなども考えましたが、とりあえず線形検索して最初に見つかった要素を
対象にして効果音の停止および管理配列からの削除をさせることにしました。
どうやらこの配列、再生中の効果音(SE)が入るようで、効果音はそこまで多重で再生しない=配列サイズも
そんなに多くはならない=線形探索しても性能への影響はない、と考えています。
# これもちょっと検証が必要な気がしますけど。

昨日の記事は上記の内容を更新しておきます。

プライバシーポリシー お問い合わせ