RPGツクールMVでプレイヤーが移動しているときだけ足音を鳴らす方法
ワケあってRPGツクールMVを触ることになりました。
RPGツクールに触った機会と言えば・・・
- 中学生時代にプレイしたスーファミのRPGツクール2
- はじめてパソコンに触ったときにプレイしたRPGツクール2000
あぁ年がバレるぅ~~~。友達とワイワイ作ったりプレイしたり、楽しかったなぁ……とノスタルジーに浸るのは置いといて。
初日ということで、とりあえずプレイヤーが移動したときに足音を鳴らす機能を実装してみました。後々必要になりそうだったので。
ネット軽く調べてみたところ、歩数やキーの押下状況で音の再生/停止をコントロールする方法を見つけましたが、
以下の観点から別の方法でコントロールすることにしました。
- 再生時間の長いSEでも自然に聞こえるようにしたい (歩数コントロールだと、場合によって音がかぶりそう(たぶんね))
- マウスクリック、スマホのタップでも正常にSEを鳴らしたい (←だと方向キーは押してない状態になるので、判定条件に方向キーの押下状況を使いたくない)
なお当方RPGツクールの慣習などをほとんど知りませんので、誤り等ありましたら教えてくださいね~。
前書き
考え方
- プレイヤーの移動を検知したとき、SE再生中フラグがすでにONなら何もしない。SE再生中フラグがOFFなら、SEをループで鳴らし、SE再生中フラグをONにする。
- プレイヤーが移動をやめたらSEを停止し、SE再生中フラグをOFFにする。
実装
自作メソッドをrpg_managers.jsに追加します。
効果音ごとに再生を停止するメソッドと、効果音をループ再生させるメソッドです。
rpg_managers.jsの先頭に以下を追加。
const path = require('path');
また以下を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 } } } }
次に、コアスクリプトの関数を書き換えてしまっていいのかわかりませんが、
AudioManager.playSeメソッドを改造します。
以下のように第二引数でループするかどうかを指定できるようにし、
引数なし(つまり既存でコールされている部分)についてはデフォルト引数で
必ずループさせない(既存と同じ動作)ようにさせます。
// 既存の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); } };
次にプレイヤーが移動しているかどうかを判定するコモンイベントを1つ作成します。
const PREV_MOVING=101, CUR_MOVING=102, STOP_COUNTER=104 { if ( this.character(-1).isMoving() ){ $gameVariables.setValue( CUR_MOVING, 1 ) // プレイヤーが移動中かどうかのフラグは「移動中」 $gameVariables.setValue( STOP_COUNTER, 0 ) // isMoving()連続false(停止)回数は0にリセット } else { // isMoving()が一定回数連続でfalse(停止)にならない限り「停止中」とは判定せず、前回の結果をそのまま使う。 $gameVariables.setValue( CUR_MOVING, ($gameVariables.value(STOP_COUNTER) <= 2) ? $gameVariables.value(PREV_MOVING) : 0 ) $gameVariables.setValue( STOP_COUNTER, $gameVariables.value(STOP_COUNTER) + 1 ) // isMoving()連続false(停止)回数を+1 } $gameVariables.setValue( PREV_MOVING, $gameVariables.value(CUR_MOVING) ) // 次回向け、前回の移動中フラグを保存する }
プレイヤーが移動しているかどうかは「this.character(-1).isMoving()」で判定できるのですが、
これをそのまま使うと思った通りに動作しません。
方向キーを押しっぱなしにしていても、グリッドとグリッドの間で一瞬停止期間があるようで、
そのまま実装すると、プレイヤーはずっと移動しているのに、上述した停止期間にひっかかって足跡のSEが所々で
停止してしまうのです。そのため、ここでは連続してisMoving()がfalse(プレイヤーの移動停止)を返してきたときに、
はじめて本当にプレイヤーの動作が停止したと判定することにします。
ここでは連続して3回isMoving()がfalseを返したら停止と判断しています。
次に、足音を鳴らしたいマップにイベントを作成し、トリガーを「並列処理」にします。
イベントの実行内容の1つ目は、↑で作成したコモンイベントを呼び出します。
実行内容の2つ目で、スクリプトを実行します。
なお以下の項目については、ご自分の環境に合わせて変更してください。
- playSeLoopメソッドおよびstopPlayedSEメソッドに渡す変数。SE名(ファイルの拡張子を除いたもの)、ボリューム、ピッチ、パン。
const CUR_MOVING=102, SE_PLAYED=103, $bPlayerMoving = $gameVariables.value(CUR_MOVING) { if ( $bPlayerMoving ) { if ( $gameVariables.value(SE_PLAYED) != 1 ){ AudioManager.playSe({"name":"stepsound","volume":100,"pitch":100,"pan":0}, true) $gameVariables.setValue( SE_PLAYED, 1 ) } } else { AudioManager.stopPlayedSE("stepsound"); $gameVariables.setValue( SE_PLAYED, 0 ) } this.wait(1) }
効果音は停止までループして再生させています。
this.wait(1)はノーウェイトだと重そうなのと、1FPSに一度処理する、とタイミングが決まっていた方が
何かと都合が良いと思ったためです。RPGツクールの常識、よくわかんねぇ。
実装は以上です。
今後は以下を考えてます。
- 足元のマップチップの種別やリージョンID?とやらで判定する
ところで
RPGツクールってRubyでガリガリ書けるようになったと聞きましたが、また元に戻ったんですかね?
マルチデバイス対応でHTML5 + JavaScriptで動作しているというのは納得なのですが。
できれば全部スクリプトで組ませて欲しいですねぇ。
せめて1つのスクリプトの命令で今の100倍くらいは行数を書けるようにして欲しいっす。
2019/07/24追記
と思ったらこんなプラグインを作成しておられる方がいらっしゃるではありませんか!
ありがとうございます。使わせていただいてます。
翠さんのエディタ内で12行以上のスクリプトを書けるプラグイン +エラー時に該当箇所を標準出力する機能を追加しました - ツクマテ