2月に配信された RPGツクールMV アップデート ver.1.5.2 ですが、ver1.5.1 からの更新点のまとめ。
ver1.5.1 への更新メモ からの続きです。RPGツクールMV JGSS 技術メモ もよろしければどうぞ。
※「ループの中断」コードに関してバグがあるようなので、ループの使用には注意してください。
rpg_core.js
今回の変更点はひとつだけ、だと思います。まず発見できるのは以下の修正箇所で、
Graphics.initialize = function(width, height, type) {
// (中略)
this._videoUnlocked = !Utils.isMobileDevice(); // ver1.5.1
this._videoUnlocked = false; // ver1.5.2
この _videoUnlocked フラグですが、以下の関数で参照されています。
Graphics._onTouchEnd = function(event) {
if (!this._videoUnlocked) {
this._video.play();
this._videoUnlocked = true;
}
if (this._isVideoVisible() && this._video.paused) {
this._video.play();
}
};
またこれと関連して、以下のコードが追加されています。
Graphics._setupEventHandlers = function() {
window.addEventListener('resize', this._onWindowResize.bind(this));
document.addEventListener('keydown', this._onKeyDown.bind(this));
document.addEventListener('keydown', this._onTouchEnd.bind(this)); // ver1.5.2で追加
document.addEventListener('mousedown', this._onTouchEnd.bind(this)); // ver1.5.2で追加
document.addEventListener('touchend', this._onTouchEnd.bind(this));
};
そして同様に _setupEventHandlers 関数はかなり書き換わっています。
// ver1.5.1
WebAudio._setupEventHandlers = function() {
document.addEventListener("touchend", function() {
var context = WebAudio._context;
if (context && context.state === "suspended" && typeof context.resume === "function") {
context.resume().then(function() {
WebAudio._onTouchStart();
})
} else {
WebAudio._onTouchStart();
}
});
document.addEventListener('touchstart', this._onTouchStart.bind(this));
document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
};
// ver1.5.2
WebAudio._setupEventHandlers = function() {
var resumeHandler = function() {
var context = WebAudio._context;
if (context && context.state === "suspended" && typeof context.resume === "function") {
context.resume().then(function() {
WebAudio._onTouchStart();
})
} else {
WebAudio._onTouchStart();
}
};
document.addEventListener("keydown", resumeHandler);
document.addEventListener("mousedown", resumeHandler);
document.addEventListener("touchend", resumeHandler);
document.addEventListener('touchstart', this._onTouchStart.bind(this));
document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
};
これらの変更ですが、個人的には「モバイルデバイスだけでなく、通常のPCでもタッチ画面を装備した機種が増えてきて、その対応」だと理解しました。
つまりはタッチ画面とマウス・キーボードを両方装備した機種で、混在した操作ができるようにコードが改良・拡張されたのだとおもわれます。
rpg_objects.js
今回はループに関する部分が修正されていますが、理解するためには、ループ処理に関する基本的な理解が必要だとおもわれます。簡単におさらいしておきましょう。
ループ処理に関する基本
イベントの「フロー制御」から「ループ」を選択すると、エディタ上には「ループ」と「以上繰り返し」の命令が自動的に追加されて、その間に字下げされた(インデントされた)命令の入力欄があらわれます。
これら「ループ」と「以上繰り返し」の命令に対応するのが以下のコードです。
// Loop
Game_Interpreter.prototype.command112 = function() {
return true;
};
// Repeat Above
Game_Interpreter.prototype.command413 = function() {
do {
this._index--;
} while (this.currentCommand().indent !== this._indent);
return true;
};
この処理で出てくる indent はエディタ上の「字下げ」に関連しています。例えば、かなり強引な例ですが、以下のように2重のループを定義して、その中にメッセージ文をいれたサンプルを用意しました。
この data/map001.json に保存された、このイベントのコードは以下のようになります。
"list":[
{"code":112,"indent":0,"parameters":[]},
{"code":112,"indent":1,"parameters":[]},
{"code":101,"indent":2,"parameters":["",0,0,2]},
{"code":401,"indent":2,"parameters":["123"]},
{"code":0,"indent":2,"parameters":[]},
{"code":413,"indent":1,"parameters":[]},
{"code":101,"indent":1,"parameters":["",0,0,2]},
{"code":401,"indent":1,"parameters":["456"]},
{"code":0,"indent":1,"parameters":[]},
{"code":413,"indent":0,"parameters":[]},
{"code":0,"indent":0,"parameters":[]}
]
上記のコードの字下げは、私がわかりやすいように手動で設定したものです。内部データに indent という値があり、字下げ(インデント)の数、つまり何重のループの内側であるか、を示しています。
ここで code == 112 が「ループ」で、code == 413 が 「以上繰り返し」命令に対応することをふまえて、上記の JSON データと JavaScript コードを見比べてみてください。
command112は実質何もしないコードである(単なる目印である)ことと、command413 関数のコードがループ処理の実態であることがわかります。そしてその処理内容は「同じindexをもつ112コードが見つかるまで戻る」となっています。シンプルながらきちんと動作するうまい仕組みですね。
なお code が 101 はテキスト表示、続く 401 は表示するテキストデータを意味しています。 code が 0 は何もしないコードで、エディタ上での空の行と一致します。
今回の修正について
以上、ループ実装の仕組みが見えてきたところで、今回の修正点を確認しましょう。
まず今回の修正対象ですが、イベントの「フロー制御」にある「ループの中断」です。
// ver1.5.1
Game_Interpreter.prototype.command113 = function() {
while (this._index < this._list.length - 1) {
this._index++;
var command = this.currentCommand();
if (command.code === 413 && command.indent < this._indent) {
break;
}
}
return true;
};
まず既存コードを見てみると、処理は単純で「自身より字下げされていない413コードが見つかるまで先に進める(処理をスキップする)」です。ループの次の行から処理が再開されますから、これで動作に問題がないようにみえます。
それに対して ver1.5.2 では、depth という変数を追加して、ループの深さをチェックしているように改善しているようにみえます。多重ループの場合に何か問題があったのでしょうか?
// ver1.5.2
Game_Interpreter.prototype.command113 = function () {
var depth = 0;
while (this._index < this._list.length - 1) {
this._index++;
var command = this.currentCommand();
if (command.code === 112)
depth++;
if (command.code === 413 && command.indent < this._indent) {
if (depth > 0)
depth--;
else
break;
}
}
return true;
};
エンバグではないか?
でも、この修正されたコードには違和感を感じます。例えば、以下のようなイベントを実行すると
"123" とテキストメッセージを1度だけ表示して終了します。"456"は表示されません。赤枠の「ループの中断」があるため、これは正しい動作です。
さて、以下の赤枠のように、ループをもうひとつ追加してみます。「ループの中断」後に追加した処理なので、動作に変化はないはず、ですが…
これを実行すると、私の ver1.5.2 環境では "123" 表示がループします。1度だけではなく、つまりさきほどと動作が変わっています。
たぶんですが、ver1.5.2のコード修正でループ処理に問題が生じています。新しく追加された depth の処理で、code == 112 の時に無条件に depth++ している部分が変で、そのあとの code == 413 のときの処理とマッチしません。
例えば以下のように、code が 112 の時にも indent の値をチェックするようにすれば、問題は解決するとおもわれます。
// ver1.5.2 改善案
Game_Interpreter.prototype.command113 = function () {
var depth = 0;
while (this._index < this._list.length - 1) {
this._index++;
var command = this.currentCommand();
if (command.code === 112 && command.indent < this._indent) // ここを修正?
depth++;
if (command.code === 413 && command.indent < this._indent) {
if (depth > 0)
depth--;
else
break;
}
}
return true;
};
というか、今回の修正は本当に必要だったのでしょうか? ver1.5.1 の時のコードで問題なく動作する気がするのですが…
rpg_managers.js
これまでは BattleManager に checkAbort関数 と checkAbort2関数がありましたが、
BattleManager.checkAbort = function() {
if ($gameParty.isEmpty() || this.isAborting()) {
this.processAbort();
return true;
}
return false;
};
BattleManager.checkAbort2 = function() {
if ($gameParty.isEmpty() || this.isAborting()) {
SoundManager.playEscape();
this._escaped = true;
this.processAbort();
}
return false;
};
ver1.5.2 からは checkAbort関数に統一されましたが、その中身はcheckAbort2関数のロジックそのままです。
今回の変更はこれだけの模様。
rpg_sprites.js
マップごとの戦闘背景が指定されていない場合、そのマップにおける船での戦闘画面などでもデフォルト動作になる?ように修正されたようです。
Spriteset_Battle.prototype.overworldBattleback1Name = function() {
if ($gameMap.battleback1Name() === '') return ''; // ver1.5.2で追加
if ($gamePlayer.isInVehicle()) {
return this.shipBattleback1Name();
} else {
return this.normalBattleback1Name();
}
};
Spriteset_Battle.prototype.overworldBattleback2Name = function() {
if ($gameMap.battleback2Name() === '') return ''; // ver1.5.2で追加
if ($gamePlayer.isInVehicle()) {
return this.shipBattleback2Name();
} else {
return this.normalBattleback2Name();
}
};
不勉強で申し訳ないのですが、そもそも船のときなどの戦闘背景を変更する方法を知りません。例えば船だと "Ship" 固定だとばかり… プラグインなどで変更している方の場合、マップごとにちゃんと戦闘背景を設定しないと今後はうまく動作しないかもしれませんね。ご注意を。
変更なし
以下のファイルは今回は特に変更がない模様。
rpg_windows.js
rpg_scenes.js