見出し画像

項目選択シーンを新設する場合のポイント

ファストトラベルをやりたい

あったんですよ。今回はいわゆるファストトラベル、DQでいうところのルーラをやりたいのでそういうプラグインをまずさがしました。で、あるにはあったんですが、やっぱりこれも細かいところでちょっとやりたいことと違ったり、試しに導入したけどそもそも動かなかったりしたので結局作りました。超大作になってしまうかなと思ったけどまぁだいたい1日でできたのでそんなでもなさそうだしヨシとします。ここから先は自分用の備忘録がてら解説です。

各項目を表現するクラスを定義する

今回はルーラで、行先を選択させたいので、行先のデータをなんらかの形で持って、そのひとつひとつの行先のデータを表現するクラスをつくることにしました。

   //-----------------------------------------------------------------------------
   // CSVN_FastTravelDestination
   //
   // The object class of fast travel destination.
   function CSVN_FastTravelDestination() {
       this.initialize(...arguments);
   }
   CSVN_FastTravelDestination.prototype.initialize = function(
       switchId,
       name,
       desc,
       destMapId,
       destMapX,
       destMapY,
       shipMapId,
       shipMapX,
       shipMapY
   ) {
       this._switchId = switchId;
       this.name = name;
       this.description = desc;
       this._destMapId = destMapId;
       this._destMapX = destMapX;
       this._destMapY = destMapY;
       this._shipMapId = shipMapId;
       this._shipMapX = shipMapX;
       this._shipMapY = shipMapY;
   };
   CSVN_FastTravelDestination.prototype.enabled = function() {
       return $gameSwitches.value(this._switchId);
   };
   
   ......

こういう感じですね。選択肢を表示させるウィンドウクラスをつくるときに Window_Selectable を継承するので、name は選択肢としてウィンドウに並べたときの表示名、description は選択肢の下のヘルプウィンドウに描かれる説明文になります。enabled() は、対応するIDのスイッチのON/OFFを見てウィンドウ側で選択肢として追加するかの判断に使っています。もちろんそこで、表示はするけど選べないようにするとか、やりようはいろいろあるでしょうね。

行先選択シーンの定義

Scene_Item を参考にしながら組みました。

   //-----------------------------------------------------------------------------
   // Scene_FastTravel
   //
   // The scene class of the fast travel screen.
   function Scene_FastTravel() {
       this.initialize(...arguments);
   }
   Scene_FastTravel.prototype = Object.create(Scene_ItemBase.prototype);
   Scene_FastTravel.prototype.constructor = Scene_FastTravel;
   
   Scene_FastTravel.prototype.initialize = function() {
       Scene_ItemBase.prototype.initialize.call(this);
   };
   Scene_FastTravel.prototype.create = function() {
       Scene_ItemBase.prototype.create.call(this);
       this.createHelpWindow();
       this.createDestWindow();
   };
   
   ......

まずシーン開始直後の挙動がこんな感じです。Scene_Item とだいたい同じなんですが、カテゴリの選択(どうぐ/ぶき/ぼうぐ/だいじなもの)はないのでそこは入れていません。

   Scene_FastTravel.prototype.createDestWindow = function() {
       const rect = this.destWindowRect();
       this._itemWindow = new Window_DestList(rect, destinations);
       this._itemWindow.setHelpWindow(this._helpWindow);
       this._itemWindow.setHandler('ok', this.onDestOk.bind(this));
       this._itemWindow.setHandler('cancel', this.onDestCancel.bind(this));
       this.addWindow(this._itemWindow);
       this._itemWindow.activate();
       this._itemWindow.selectLast();
   };
   Scene_FastTravel.prototype.destWindowRect = function() {
       const wx = 0;
       const wy = this.mainAreaTop();
       const ww = Graphics.boxWidth;
       const wh = this.mainAreaBottom() - wy;
       return new Rectangle(wx, wy, ww, wh);
   };
   
   ......

行先選択ウィンドウのなかみがこんな感じです。ウィンドウのサイズと位置が destWindowRect() で決定されています。この wy(ウィンドウの左上のY座標) がもともとあるアイテム選択と違うところで、カテゴリ選択ウィンドウの高さのぶんだけ上になっています。_itemWindow のところは _destWindow にしたかったんですが、_itemWindow としておくと、Window_Selectable を継承している Window_DestList でもそのまま使えて楽なのでそうしています。setHandler(symbol, method) は、決定とキャンセルそれぞれをやったときの動作を決めています。決定時に onDestOk() 、キャンセル時に onDestCancel() を呼ぶようにしています。

   Scene_FastTravel.prototype.onDestOk = function() {
       $gameParty.setLastItem(this.item());
       this.doFastTravel();
   };
   Scene_FastTravel.prototype.onDestCancel = function() {
       if ($gameSwitches.value(swIds.item)) {
           SceneManager.goto(Scene_Item);
       } else {
           SceneManager.goto(Scene_Skill);
       }
   };
   
   ......

決定/キャンセル時の具体的な動作はこんな感じに書きました。$gameParty.setLastItem() は中を読んでないのでわかりませんが、選択位置記憶ですかねぇ? doFastTravel() がファストトラベルの実行の処理です。キャンセル時の動作にもコツがいくつかあります。ifで分岐しているのはアイテムから行先選択を呼んだ場合とスキルから行先選択を呼んだ場合の分岐です。キャンセルしたとき、アイテムから始めたらアイテムに戻ってほしいですし、スキルから始めたらスキルに戻ってほしい、という処理ですね。

   Scene_FastTravel.prototype.doFastTravel = function() {
       if ($gameSwitches.value(swIds.item)) {
           const fastTravelItem = $dataItems[varIds.itemId];
           $gameParty.consumeItem(fastTravelItem);
       } else {
           const actor = $gameActors.actor($gameVariables.value(varIds.user));
           const skill = $dataSkills[varIds.skillId];
           actor._mp -= actor.skillMpCost(skill);
       }

......

これがファストトラベルの処理本体(前半)です。これもアイテムからかどうかでまず分岐しています。アイテムの場合はそのアイテムをここで1つ消費していますが、アイテムの場合アイテム選択から行先選択に移った時点で1つすでに減っているので、キャンセルしたときに使ってないのに1つ減ってしまいます。そのため、この行先選択シーンを呼ぶ前に前もって1つ増やしておく必要があります(わかりにくいですねぇこれ)。同様に、スキルからの場合は行先選択に移る直前にそのスキルの消費MPを前もって増やしています。

スクリーンショット 2021-07-24 19.56.59

こういう感じですね。実際にファストトラベルすると、アイテムやMPの消費がスクリプト内でもういちど呼ばれるので差し引きでちょうどよくなります。続いて後半。

       $gameVariables.setValue(varIds.destMapId, this.item()._destMapId);
       $gameVariables.setValue(varIds.destMapX, this.item()._destMapX);
       $gameVariables.setValue(varIds.destMapY, this.item()._destMapY);
       $gameVariables.setValue(varIds.shipMapId, this.item()._shipMapId);
       $gameVariables.setValue(varIds.shipMapX, this.item()._shipMapX);
       $gameVariables.setValue(varIds.shipMapY, this.item()._shipMapY);

       $gameTemp.reserveCommonEvent(varIds.eventId);
       SceneManager.goto(Scene_Map);
   };

アイテム消費やMP消費の処理が済んだら、事前にデータとして持たせているプレイヤーキャラと大型船の行先のマップID/X/Yを変数にセットして、別途ゲームエディタ側でつくってあるコモンイベントを予約してマップに戻っています。こうすると、行先を選択した直後に設定したコモンイベントが実行されます。

スクリーンショット 2021-07-24 20.05.52

船の移動は船がある時だけにしておかないと、船を持つまえにファストトラベルすると船がいっしょにすっ飛んできてしまうので、そこをふさいでいます。

行先選択ウィンドウのなかみ

基本的には Window_Selectable を継承していて共通する部分もいろいろあります。違う部分としてはこういうあたりでしょうか↓

   Window_DestList.prototype.drawItem = function(index) {
       const item = this.itemAt(index);
       const rect = this.itemLineRect(index);
       if (item && item.enabled()) {
           this.drawItemName(item, rect.x, rect.y, rect.width);
       }
   };
   Window_DestList.prototype.drawItemBackground = function(index) {
       const item = this.itemAt(index);
       const rect = this.itemLineRect(index);
       if (item && item.enabled()) {
           this.drawBackgroundRect(rect);
       }
   };

最初に書いた選択項目のクラスの enabled() がスイッチに対応していて、それのON/OFFで選択肢として出す出さないをここで決めています。たとえば「一度訪問した街や城にはファストトラベルできるようになる」とかであれば、街や城に入るイベントで対応するスイッチをONしておけばいいわけです。この drawItemName() が Window_Selectable から継承してきたものをそのまま呼んでいて、先述のとおりプロパティに name と description を入れておくとそのままいい感じになってくれます。

あともともとどういうデータを入れるの、という部分はこういう感じにしています。

   Window_DestList.prototype.makeItemList = function() {
       this._data = destinations;
   };

今回は私はdestinations という外のスコープで直書きして定義した定数をそのままドーンと中身に入れてしまっていますが、ここもまぁやりようはいろいろあるところでしょう。

いやーすごく長くなりましたねぇ。一応これ公開しますけど、他の方が使うには少し手を入れないとダメだろうなと思います。

追記(2021/8/6)

船の上からルーラしたときにいろいろおかしいのに気づいて修正してたらもっと大きな問題を発見したので修正、v1.0.1です。

ダウンロードはこちら

CSVN_fastTravel.js
※右クリックでDL。

ほかにつくったやつはこちら

cursed-steven/rmmz: RPGツクールMZ用のプラグインスクリプトをおいていきます。

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