けいゆうのブログ

作曲/ゲーム制作/ティラノビルダー/小説

MENU

ティラノビルダーでつなぎの自然なBGMループを実装する方法

 

スポンサーリンク


はじめに

劇団くじら座サウンド担当のけいゆうです。


2020/09/19現在、ティラノの開発環境はv5へのアップデートが進行中です。

v5ではBGMの範囲指定再生や継ぎ目のない自然なループが可能になっています。


現状ではティラノスクリプトとティラノスタジオがv5に対応していますが、

ティラノビルダーでは今のところ非対応のため、これらの機能が使えません。


今回の記事では、ビルダー内のスクリプト機能を使用することで

BGMのシームレスループを実現する方法を書いていきます。


この手法は先日公開した『ティアマトの星影』で使用したものです。

シャチさんのこちらの記事を参考に、

友人のSくんに根幹を実装してもらいました。

(ほぼ丸投げだったのに一瞬で終わらせてくれたすごい人。頭が上がらない)


syachi.hatenablog.jp


シャチさんとSくん、ありがとうございました。

注意点

デフォルトの再生機能ではなくWeb Audio APIとスクリプトを使用するため、

かゆいところに手が届かない部分もあります。

フェードアウトは失敗しました。

不可能ではなさそうですが……。


また、今回の方法は動作確認済みではありますが

全ての環境でうまくいくかは保証できません。

そちらも併せてご了承ください。

1. tyrano.ksへのマクロ記述

まず下記のコードをtyrano.ks末尾の[return]直前に貼り付けてください。

(本来であればマクロ用の新しいファイルを作るほうがよいのでしょうが、

毎回[call]マクロで呼び出さなければならないので少々面倒です。

またfirst.ksに書くとゲームの起動時にリセットされてしまいます)

[iscript]
tf.context = new AudioContext();
tf.gainNode = tf.context.createGain();
[endscript]

;現在流れているBGMの情報を入れる変数
[eval exp="f.currentbgm"]


;ループ再生マクロ
[macro name='mloop']

[iscript]
function getArrayBuffer(url){
return new Promise(function(resolve){
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function () {
tf.arrayBuffer = request.response;
resolve();
};
request.send();
});
}
function createAudioBuffer(){
return new Promise(function(resolve){
tf.context.decodeAudioData(tf.arrayBuffer, function (buf) {
tf.audioBuffer = buf;
resolve();
});
});
}
function createBuffer(){
return new Promise(function(resolve){
tf.source = tf.context.createBufferSource();
tf.source.buffer = tf.audioBuffer;
// Connect the source to the gain node.
tf.source.connect(tf.gainNode);
// Connect the gain node to the destination.
tf.gainNode.connect(tf.context.destination);
//tf.source.connect(tf.context.destination);
resolve();
});
}
function playOGG(){
// ループ再生の設定
tf.source.loop = true;
// ループ範囲開始位置
tf.source.loopStart = mp.start;
// ループ範囲終了位置
tf.source.loopEnd = mp.end;
// ループ再生開始
tf.source.start(0);
}
// ファイルのパス
var SOUND_URL = mp.storage;
getArrayBuffer(SOUND_URL).then(createAudioBuffer).then(createBuffer).then(playOGG);
[endscript]

[endmacro]


;ループ停止マクロ
[macro name="mstop"]
[iscript]
if(f.currentbgm==null)
{
  f.currentbgm=false;
}
if(f.currentbgm!=false)
{
  tf.source.stop();
}
[endscript]
[eval exp="f.currentbgm=false"]
[endmacro]

ここで作成したmloopマクロの仕様を解説します。


[mloop storage="音声ファイルの場所"
start=ループ開始位置
end=ループ終了位置]


まず1回目は曲の最初から最後まで再生されます。

その後start位置に戻りend位置まで再生、

start位置に戻る……というループを繰り返します。

startとendは秒数です。小数も指定できます。

ループ範囲の探し方はシャチさんのこちらの記事をご参照ください。


syachi.hatenablog.jp


mstopマクロに引数はありません。


[mstop]


このままで使用できます。


f.currentbgmはコメントの通り、再生中のBGM情報を格納する変数です。

現在はfalseとnullしか入っていませんが、

こちらに曲ごとのタグを代入することで管理がしやすくなります。

詳しくは次の記事で解説します。

(次の記事→)

www.utsuboublog.com

2. シナリオでのマクロ使用

これで準備ができました!

続いてはマクロの使用方法です。


ビルダーでシナリオファイルを開いたら、

BGMを再生したい位置にティラノスクリプトコンポーネントを挿入します。


[mloop storage="音声ファイルの場所" start=ループ開始位置 end=ループ終了位置]


この書式に則ってmloopを記述してください。

f:id:b0tchan:20200919185745p:plain
ビルダーの画面


BGMを止めるときは同様にしてコンポーネントを挿入した後、

[mstop]と書きます。

f:id:b0tchan:20200919185907p:plain
[mstop]


mloopで再生した曲は1回1回mstopで停止する必要があります。

(少なくともmloopと同数のmstopが必要です)


またプレイ時にスキップを多用していると

まれにmstopが働かずBGMが重なってしまうことがあります。

対策としてmstopを要所要所でこまめに記述しておくとよいでしょう。

(mloopで曲を再生していないときに実行しても大丈夫です)

終わりに

今回はいったんここで終わりにしたいと思います。

これだけでも一応ループ再生は可能です。


しかし、


・毎回ループ情報を入力するのが面倒くさい

・セーブデータのロード時に曲が流れない


など実際の運用上いろいろと問題があります。

次回の記事ではそれらの解決方法についてお話ししたいと思います。

最後までお読みいただきありがとうございました。


次→

www.utsuboublog.com