見出し画像

実録!RPGツクールMVでビジュアルノベルを作る


RPGツクールMVでビジュアルノベル作りたいなぁ……。


画像22

こういう形の、メッセージ表示中に自由にセーブ&ロードが出来ると理想的だなぁ。

発売してから結構立ったしそういうビジュアルノベル用の準備を一式用意してくれてるプラグインとかあるだろう……アレ、ない!?

というわけで

この記事では、ツクールMVでビジュアルノベルを構築するための制作記録を書き記していきます。

使い慣れたツールで作るためと、忘れないための備忘録がメインです。

同じようなものを作りたいと思ってらしてる方のご参考になれれば幸いです。

お借りするプラグイン

ビジュアルノベルを制作するにあたって以下のプラグインをお借りさせて頂きました。

ルルの教会
立ち絵表示プラグインVer.2(https://nine-yusha.com/plugin-spicture/)
トリアコンタン
ピクチャのボタン化プラグイン(https://triacontane.blogspot.com/2015/11/blog-post_23.html)
動的文字列ピクチャ生成プラグイン(https://triacontane.blogspot.com/2015/12/rpgmv-rpgmv-dtext-1-rpgmv-dtext-dtext.html)
ウィンドウタッチ仕様変更プラグイン(https://raw.githubusercontent.com/triacontane/RPGMakerMV/master/ChangeWindowTouchPolicy.js)
メッセージスキッププラグイン(https://plugin.fungamemake.com/archives/1201)
蔦森くいな
イベント実行中もセーブを可能にするPlugin(https://paradre.com/saveinevent-plugin/)
さば缶
バックログプラグイン(https://plugin.fungamemake.com/archives/10302)

製作者の方々にこの場をお借りして深くお礼申し上げます。

STEP1 立ち絵を表示しよう

まず第一に立ち絵を表示しないとテンションが上がりませんね!

立ち絵を表示するのに今回はルルの教会様の「立ち絵表示プラグインVer.2」をお借り致しました。

STEP1-1.プラグインの設定

プラグインパラメーターの設定から立ち絵を登録しましょう!

画像1

パラメータの立ち絵リストから画像ファイル名を指定し立ち絵IDを設定します

画像2

こんなもんですかね。座標の設定も忘れずに!

画像3

無事表示されました!一気にらしくなりますね!もうこれでいいんじゃないかな!?

STEP2 セーブ&ロード機能をつける

個人的にビジュアルノベルの肝なのがどこでもセーブ&ロード機能。

まずはビジュアルノベルでよく見かける常時表示されるSAVE&LOADボタンを作りましょう。ボタンの作成にはトリアコンタン様の「ピクチャのボタン化プラグイン」をお借りいたしました。

SAVE&LOAD用のUI素材を用意するのが面倒な方は、同じくトリアコンタン様の「動的文字列ピクチャ生成プラグイン」を使いましょう。今回は利用させて頂きます。

STEP2-1. SAVE&LOADのピクチャを表示

画像4

まずはSAVE&LOADのピクチャを表示します。メッセージの前にピクチャを用意してと……

画像5

ああ!立ち絵の裏に隠れてしまってる!どうやらこの立ち絵はピクチャより前に表示されてしまうみたいですね。

理想はメッセージウィンドウのそばにSAVE&LOADのボタンを表示する事。ここはプラグインを改造して立ち絵がピクチャの下に表示されるようにしましょう!

ルルの協会様の立ち絵表示プラグインVer.2(LL_StandingPictureMV.js)をメモ帳などのテキストエディタで開きます。

大体500行目ぐらいに以下のような記述があります

	//-----------------------------------------------------------------------------
	// ExStandingPicture
	//
	// 立ち絵を表示する独自のクラスを追加定義します。

	class ExStandingPicture {

		static create (elm) {
			// 立ち絵1
			elm._spSprite = new Sprite_LL_SPicture();
			elm.addChildAt(elm._spSprite, elm.children.indexOf(foreFronts[1] ? elm._windowLayer : elm._spriteset) + 1);
			// 立ち絵2
			elm._spSprite2 = new Sprite_LL_SPicture();
			elm.addChildAt(elm._spSprite2, elm.children.indexOf(foreFronts[2] ? elm._windowLayer : elm._spriteset) + 1);
			// 立ち絵3
			elm._spSprite3 = new Sprite_LL_SPicture();
			elm.addChildAt(elm._spSprite3, elm.children.indexOf(foreFronts[3] ? elm._windowLayer : elm._spriteset) + 1);
			// 立ち絵4
			elm._spSprite4 = new Sprite_LL_SPicture();
			elm.addChildAt(elm._spSprite4, elm.children.indexOf(foreFronts[4] ? elm._windowLayer : elm._spriteset) + 1);
		}

この辺りが立ち絵のレイヤーを管理している部分ですね。この部分を改造します!ここに以下のコードをコピペして貼り付けましょう。

	//-----------------------------------------------------------------------------
	// ExStandingPicture
	//
	// 立ち絵を表示する独自のクラスを追加定義します。

	class ExStandingPicture {

		static create (elm) {
			// 立ち絵1
			elm._spSprite = new Sprite_LL_SPicture();
			if(foreFronts[1])
			{
				elm.addChildAt(elm._spSprite, elm.children.indexOf(elm._windowLayer) + 1);
			}
			else
			{
				elm._spriteset.addChildAt(elm._spSprite, elm._spriteset.children.indexOf(elm._spriteset._baseSprite) + 1);
			}
			// 立ち絵2
			elm._spSprite2 = new Sprite_LL_SPicture();
			if(foreFronts[2])
			{
				elm.addChildAt(elm._spSprite2, elm.children.indexOf(elm._windowLayer) + 1);
			}
			else
			{
				elm._spriteset.addChildAt(elm._spSprite2, elm._spriteset.children.indexOf(elm._spriteset._baseSprite) + 1);
			}
			// 立ち絵3
			elm._spSprite3 = new Sprite_LL_SPicture();
			if(foreFronts[3])
			{
				elm.addChildAt(elm._spSprite3, elm.children.indexOf(elm._windowLayer) + 1);
			}
			else
			{
				elm._spriteset.addChildAt(elm._spSprite3, elm._spriteset.children.indexOf(elm._spriteset._baseSprite) + 1);
			}
			// 立ち絵4
			elm._spSprite4 = new Sprite_LL_SPicture();
			if(foreFronts[4])
			{
				elm.addChildAt(elm._spSprite4, elm.children.indexOf(elm._windowLayer) + 1);
			}
			else
			{
				elm._spriteset.addChildAt(elm._spSprite4, elm._spriteset.children.indexOf(elm._spriteset._baseSprite) + 1);
			}
		}

これでよし!何をやったかというと立ち絵をピクチャーより下のレイヤーにした感じです。後は実際に試してみましょう!

画像6

無事ピクチャが立ち絵より前面に表示されるようになりました。

STEP2-2. ピクチャをボタン化する

いよいよボタン押下時の挙動を実装します。まずはトリアコンタン様のピクチャのボタン化プラグインを導入し、コモンイベントでそれぞれセーブとロード時のイベントを作りましょう。

画像7

画像8

注意点は、ロードのイベントコマンドが用意されてないのでスクリプトで書く必要がある点です。

続いてはボタン押下時の処理を実装していきましょう。

画像9

プラグインコマンドでピクチャ番号と押下時のコモンイベントIDを指定します。

画像10

あとピクチャのボタン化プラグインの設定で「並列処理として実行」をONにするのを忘れずにしましょう!

画像11

これで無事にSAVE&LOADボタンが押せるようになりました!あれ、でも何かセーブする時にメッセージが流れてるような……

STEP3 メッセージウィンドウを抑制する

ツクールMVの仕様だと、画面上のどこをタッチしてもメッセージが流れてしまいます。

できればSAVE&LOAD押下時ぐらいはメッセージ送りは抑制したい!そこでメッセージウィンドウをクリックした時のみセリフが流れるようにしてみます。

お借りするのはお世話になっております、トリアコンタン様のウィンドウタッチ仕様変更プラグイン(ChangeWindowTouchPolicy.js)。このプラグインを改造してメッセージウィンドウ内をタッチした時のみセリフ送りするようにしていきます

STEP3-1. メッセージウィンドウのタッチ制御

まずはウィンドウタッチ仕様変更プラグイン(ChangeWindowTouchPolicy.js)をメモ帳か何かのテキストエディタで開いて頂き、124行目ぐらいの以下の記述を見つけます。

    Window_Selectable.prototype.isTouchedBorder = function() {
       var x = this.canvasToLocalX(TouchInput.x);
       var y = this.canvasToLocalY(TouchInput.y);
       if (y < this.padding) {
           return true;
       } else if (y >= this.height - this.padding) {
           return true;
       } else if (x < this.padding) {
           return true;
       } else if (x >= this.width - this.padding) {
           return true;
       } else {
           return false;
       }
   };

ここに以下のコードをコピペして上書きしましょう。

    Window_Base.prototype.isTouchedBorder = function() {
       var x = this.canvasToLocalX(TouchInput.x);
       var y = this.canvasToLocalY(TouchInput.y);
       if (y < this.padding) {
           return true;
       } else if (y >= this.height - this.padding) {
           return true;
       } else if (x < this.padding) {
           return true;
       } else if (x >= this.width - this.padding) {
           return true;
       } else {
           return false;
       }
   };

続いて150行目ぐらいの以下の記述を見つけます

    //=============================================================================
   // TouchInput
   //  ポインタ移動時にマウス位置の記録を常に行うように元の処理を上書き
   //=============================================================================
   TouchInput._onMouseMove = function(event) {
       var x = Graphics.pageToCanvasX(event.pageX);
       var y = Graphics.pageToCanvasY(event.pageY);
       this._onMove(x, y);
   };
})();

この部分に以下のコードをコピペしましょう

   //=============================================================================
   // TouchInput
   //  ポインタ移動時にマウス位置の記録を常に行うように元の処理を上書き
   //=============================================================================
   TouchInput._onMouseMove = function(event) {
       var x = Graphics.pageToCanvasX(event.pageX);
       var y = Graphics.pageToCanvasY(event.pageY);
       this._onMove(x, y);
   };

   //=============================================================================
   // Window_Message
   //  メッセージウィンドウは、ウィンドウ内をタッチされた時に反応するように切り替え
   //=============================================================================
   Window_Message.prototype.isTriggered = function() {
       return (Input.isRepeated('ok') || Input.isRepeated('cancel') ||
               (!this.isTouchedBorder() && TouchInput.isRepeated()));
   };

})();

後はプラグインの設定で枠外タッチの挙動を「なし」にすれば……

画像12

画像13

見事!メッセージウィンドウの排他制御が完成しました!これで理想のビジュアルノベルに近づける……ってあれ。なんだかロード時にメッセージがズレてるような……。

STEP3-2. ロード時のバグ修正

実はイベント実行中にセーブ&ロードは不具合が発生するようです。

蔦森くいな様のイベント実行中もセーブを可能にするPluginを導入して修正しておきましょう。本当に助かります。

STEP4 バックログをつける

いよいよ理想のビジュアルノベルに近づいてきました!

後はより完成度を高めてバンバンそれらしくしちゃいましょう。個人的にビジュアルノベルは直前のテキストを読み返したりしたいのでバックログがあるといいですね!

今回はさば缶様のバックログプラグインをお借りいたしました。

https://plugin.fungamemake.com/archives/10302

こちらのバックログを今回のビジュアルノベルシステムに合わせるように改造していきます!改造だらけですがお付き合いいただければ幸いです。

STEP4-1. マウスホイール対応

こちらのバックログはキーボード操作のみ受け付けておりますので、まずはマウスホイールでバックログを操作できるように改造していきましょう!

バックログプラグイン(Saba_BackLog.js)を開いて頂き、225行目辺りの下記箇所を

            Window_BackLog.prototype.update = function () {
               _super.prototype.update.call(this);
               if (Input.isPressed('down')) {
                   this.y -= scrollSpeed;
                   if (this.y < Graphics.height - this._maxHeight) {
                       this.y = Graphics.height - this._maxHeight;
                   }
               }
               else if (Input.isPressed('up')) {
                   this.y += scrollSpeed;
                   if (this.y > 0) {
                       this.y = 0;
                   }
               }
           };

以下のように上書きコピペしましょう!

            Window_BackLog.prototype.update = function () {
               _super.prototype.update.call(this);
               if (Input.isPressed('down') || TouchInput.wheelY > 0) {
                   this.y -= scrollSpeed;
                   if (this.y < Graphics.height - this._maxHeight) {
                       this.y = Graphics.height - this._maxHeight;
                   }
               }
               else if (Input.isPressed('up') || TouchInput.wheelY < 0) {
                   this.y += scrollSpeed;
                   if (this.y > 0) {
                       this.y = 0;
                   }
               }
           };

これでマウスホイール時で操作ができるようになります。

STEP4-2. ボタンで開くためのあれこれ

今度はメッセージをバックログに蓄積する処理と、ピクチャボタンで開けるような処理を同時に入れていきましょう!ついでにバグ修正も行っておきます。

次は248行目付近をの以下の箇所を

        var _Scene_Map_update = Scene_Map.prototype.update;
       Scene_Map.prototype.update = function () {
           if (this._windowBackLog) {
               this._windowBackLog.update();
               if (Input.isTriggered(backLogButton) || Input.isTriggered('cancel')) {
                   this.removeChild(this._windowBackLog);
                   this._windowBackLog = null;
                   SoundManager.playCancel();
               }
               return;
           }
           _Scene_Map_update.call(this);
           if (Input.isTriggered(backLogButton)) {
               this._windowBackLog = new Window_BackLog();
               SoundManager.playOk();
               this.addChild(this._windowBackLog);
           }
       };
       var Scene_Boot_loadSystemImages = Scene_Boot.prototype.loadSystemImages;
       Scene_Boot.prototype.loadSystemImages = function () {
           Scene_Boot_loadSystemImages.call(this);
           if (windowSkin.length > 0) {
               ImageManager.loadSystem(windowSkin);
           }
       };
       BackLog.$gameBackLog = new Game_BackLog();
   })(BackLog = Saba.BackLog || (Saba.BackLog = {}));
})(Saba || (Saba = {}));

次のように上書きコピペして下さい!

        var _Scene_Map_update = Scene_Map.prototype.update;
       Scene_Map.prototype.update = function () {
           if (this._windowBackLog) {
               this._windowBackLog.update();
               if (Input.isTriggered(backLogButton) || Input.isTriggered('cancel') || TouchInput.isCancelled()) {
                   this.removeChild(this._windowBackLog);
                   this._windowBackLog = null;
                   SoundManager.playCancel();
               }
               return;
           }
           _Scene_Map_update.call(this);
           if (Input.isTriggered(backLogButton) || BackLog.startWindowBackLog) {
               BackLog.startWindowBackLog = false;
               this._windowBackLog = new Window_BackLog();
               SoundManager.playOk();
               this.addChild(this._windowBackLog);
           }
       };
       var Scene_Boot_loadSystemImages = Scene_Boot.loadSystemImages;
       Scene_Boot.loadSystemImages = function () {
           Scene_Boot_loadSystemImages.call(this);
           if (windowSkin.length > 0) {
               ImageManager.loadSystem(windowSkin);
           }
       };
       var _Game_Message_add = Game_Message.prototype.add;
       Game_Message.prototype.add = function(text) {
           Saba.BackLog.$gameBackLog.addLog('', text);
           _Game_Message_add.apply(this, arguments);
       };
       BackLog.$gameBackLog = new Game_BackLog();
       BackLog.startWindowBackLog = false;
   })(BackLog = Saba.BackLog || (Saba.BackLog = {}));
})(Saba || (Saba = {}));

これで準備万端です!

後はバックログに使うウィンドウの画像設定を忘れずにし

画像14

続いてコモンイベントでバックログ呼び出しイベントを作ります。これは以下のコードをスクリプトで呼び出して下さい!上のコピペコードで準備を仕込んでおきました!

Saba.BackLog.startWindowBackLog = true;

画像15

後はピクチャボタンを追加してコモンイベントを呼び出すようにすれば……

画像16

画像17

無事にバックログが導入出来ました!いえーい!だいぶそれらしくなってきましたね!

ここまで来たら後はラストスパートをかけていきましょう。

STEP5 スキップ・オートメッセージ機能をつける

後はらしい機能!スキップ機能とオートメッセージ機能もつけていきましょう!お世話になってますトリアコンタン様のメッセージスキッププラグインをお借りします。

プラグインを導入したら設定を開き、スキップ用スイッチとオート用スイッチを設定します。

画像18

後はコモンイベントにスイッチ切り替え用のイベントを用意して……

画像19

画像20

ピクチャボタンも用意してください!(割愛します)

画像21

そしてついに完成の時を迎えました。NKT……

どうでしょうどこからどう見ても立派なビジュアルノベルになりましたよね!?(

おわりに

以上でビジュアルノベルの外形を作る上での道のりが分かったかと思います。多数のプラグインに助けられながら、時折改造を挟むなど結構大変な道のりでしたね。餅は餅屋ということで特別な理由がなければ素直にティラノスクリプト等のノベルゲーム用エンジンを用いて作るのが手っ取り早いかもしれません。

しかし、ツクールMVならではの機能、例えばRPG要素を入れたりゲームアツマールによる通信機能を入れたりする事も出来るので、是非プラスアルファのゲームを目指したいときはツクールMVでビジュアルノベルを作ってみるのも良いのではないでしょうか。

最後に今回の記事で作ったギャルゲーサンプルを公開しておきます!刮目せよ!

https://game.nicovideo.jp/atsumaru/games/gm21210

この記事が気に入ったらサポートをしてみませんか?