RPGツクールプラグイン制作過程紹介 第13回
昨年から始めた本シリーズですが、いつの間にかもう2022年ですね。できれば今年度中に完結させたいと思っているので、もうしばらくの間お付き合いいただければと思います。
前回はマップ上に表示したスキルリストウィンドウにスキル一覧を表示させるところまで進めました。今回は、そのスキルがアクターやスキルタイプに応じて変更されるようにしていきたいと思います。
現状の確認
まずは前回のおさらいもかねて、現在どのような動作をするのかを確認してみましょう。
ご覧の通りスキルリストウィンドウ内の項目は何も選択できません。ただ、普通に歩き回ったりメニューを開いたりすることはできます。つまりこのウィンドウは今のところ何の動作もしない、ただの「ハリボテ」ということです。とりあえず、ものすごく「ジャマ」ではありますね……。
また前回は暫定措置としてケイシーの魔法を表示させることにしたわけですが、このままではそれ以外のスキルを表示させることもできません。
というわけで今回はこのウィンドウをきちんと機能させることを目標にしたいと思います。
コードの改良
現状ではご覧のように機能していないスキルリストウィンドウですが、そもそもこのウィンドウが使用されているScene_Skillではどのように使われていたでしょうか。
スキルリストウィンドウの左上には、スキルを使用するアクターの情報が表示されています。右上には、スキルタイプを選択するウィンドウが表示されています。これによってスキルリストウィンドウに、それらを反映したスキル一覧が表示されるようになっています。
本プラグインには今のところそれらを表示させていませんが、代わりに暫定措置としてアクター6番とスキルタイプ1番という固定値をセットしていましたね。
ですがこのように簡略化して設定しただけではうまく機能しない、ということがわかりました。それならいっそ、変に省略せずにコアスクリプトのScene_Skillを丸ごとコピーしてみてはどうでしょうか。
Scene_Skillのコピー
以前コピーした以下の関数2つですが、暫定措置として途中のコードをコメントアウトしていました。
まずはこれらのコメントアウトを解除して元に戻します。反対に、以前追記した箇所をコメントアウトします。つまりScene_Skillに定義されていた通りの状態に戻す、というわけですね。
ですがこの状態で起動しようとするとエラーになってしまうのでしたね。それを防ぐための暫定措置でした。
createItemWindow関数には、以下のコードがあります。
this._itemWindow.setHelpWindow(this._helpWindow);
this._itemWindowのsetHelpWindowという関数をthis._helpWindowという引数で呼び出しているようですが、このthis._helpWindowというのはその名の通りヘルプウィンドウのことです。スキルやアイテムの説明文が表示される、さまざまなシーンで表示されるウィンドウとしてお馴染みだと思います。
このヘルプウィンドウは元々のScene_Skillでは作成されますが、今のところScene_Mapでは作成していません。ですのでこれはエラーの原因になります。
また、itemWindowRect関数には以下のコードがあります。
const wy = this._statusWindow.y + this._statusWindow.height;
このthis._statusWindowはScene_Skillにおけるこのウィンドウです。
アクターの顔グラフィックや名前、HPMPなどが表示されているウィンドウですね。こちらもScene_Skillでは作成されますが、今のところScene_Mapにはありません。
最後に、itemWindowRect関数にある以下のコードもエラー原因の一つです。
const wh = this.mainAreaHeight() - this._statusWindow.height;
wh定数に数値を代入しようとしているのですが、mainAreaHeightという関数はScene_Mapには定義されていませんのでエラーになってしまうのです。
なお余談ですが、このように開発途中のプラグインにおいて必要な関数等がまだ定義されていないためにエラーになってしまうことはよくあることです。その状態でゲームを起動する必要がある場合、いったんプラグインをオフにしましょう。
ということで、現状のエラーを回避するには以下の3つを用意する必要がありそうです。
_helpWindow(に代入する関数)
_statusWindow(に代入する関数)
mainAreaHeight関数
まずはScene_Skillにおいてこれらがどのように定義されているのかを確認します。上記のcreateItemWindow関数がScene_Skillにおいてどこで呼び出されているのかを見てみましょう。
ご覧の通りScene_Skillのcreateという関数にて呼び出されているようですが、この関数には他にもcreateHelpWindowやcreateSkillTypeWindow、createStatusWindowなどなど気になる名前をした関数が多数呼び出されています。これらの定義箇所を見てみましょう。
雰囲気から察するにまさにこれらこそが、今必要としているウィンドウの作成関数に違いありません!
なおcreateHelpWindowはScene_Skillを探してみても見つかりません。でしたら検索してみましょう。
どうやらScene_MenuBaseというクラスに定義されている関数のようです。今参考にしているScene_SkillはScene_ItemBaseという、アイテム類の使用に関するシーンクラスのサブクラスである、ということは以前にもご紹介しました。それではScene_ItemBaseのスーパークラスは何なのでしょうか? 確認してみましょう。
ご覧の通りScene_ItemBaseのスーパークラスはScene_MenuBaseです。Scene_MenuBaseとは、メニュー画面にて使用されるすべてのシーンクラスのスーパークラスなのです。ヘルプウィンドウはさまざまなメニュー関連シーンに登場するので、それらの共通のスーパークラスに定義した方が都合が良いわけですね。
ただし本プラグインではマップ上にウィンドウを表示させようとしているわけですが、Scene_MapはScene_MenuBaseのサブクラスではありません。
Scene_MapはScene_Messageのサブクラスですが、Scene_MessageのスーパークラスはScene_Baseです。つまりScene_MapのクラスツリーにはScene_MenuBaseがどこにも登場しませんので、Scene_MapからはcreateHelpWindow関数を参照することができません。ですのでこの関数もコピーした上でScene_Mapに定義する必要があるでしょう。
というわけで、まずはScene_Skillのcreate関数の内容であるさまざまなウィンドウの作成関数呼び出しをScene_MapのcreateAllWindows関数にコピーしてみることにします。
この関数では以前からcreateItemWindowだけは呼び出していましたが、それ以外の関数もすべてScene_Skillのcreate関数に合わせました。
ただしこれだけではそれらの関数がScene_Mapにまだ定義されていませんのでエラーになります。ということでそれらもコピーして持ってきます。
ご覧のようにウィンドウ作成関数だけではなく、その関数内で呼び出されるxxWindowRect関数も一緒にコピーして貼り付けています。これはもちろん、それらもセットでないとエラーになってしまうからです。
上記の通りcreateHelpWindow関数もScene_MenuBaseからコピーして貼り付けます。
ヘルプウィンドウにも他のウィンドウと同様、このウィンドウの座標や寸法を表す矩形(Rectangle)情報をhelpWindowRect関数にて取得しています。こちらもScene_Mapにはない関数なので、忘れずに貼り付けます。
これで上記1・2のウィンドウ作成関数はそろいました。最後に3のmainAreaHeight関数を用意しましょう。この関数もScene_MenuBaseに定義されています。
注意が必要なのは、mainAreaHeight関数内でさらにhelpAreaHeightという関数が呼び出されています。また、その定義箇所の周囲に似たような名前の関数が多数定義されています。これらの関数は何のためにあるのかというと、ウィンドウの配置場所をUIボタンやヘルプウィンドウの高さなど、他の要素との兼ね合いで計算するために用意されているのです。上記スクリーンショットの関数はいずれも必要になりますので、すべてコピーします。
テスト・デバッグ
これですべての準備が整ったはずです。早速テストしてみましょう…とその前に、FieldAction.jsを再びオンにするのを忘れずに!
起動すると上記エラーが表示されます。デバッグコンソールにて詳細を確認してみましょう。
createSkillTypeWindow関数内のbindという箇所でエラーになっているようです。
このbindとは何なのかというと、プレイヤーがあるコマンドを押した際に実行される関数を紐づけるための処理です。以前にもcreateItemWindow内の似たようなコードをコメントアウトしたことがありましたね。この処理は後で組むことにするので、今はコメントアウトしておきます(createItemWindow内の当該箇所も再びコメントアウトします)。
それでは再度テストしてみましょう。
またエラーが出てしまいました! デバッグコンソールを確認しましょう。
「createAllWindows関数内のcreateActorWindowは関数ではない」というエラー内容です。当該箇所を確認しましょう。
これは、createActorWindowという関数が定義されていないために起こるエラーです。このウィンドウもScene_Skillにて作成されるウィンドウなのですが、今回あえてコピーしませんでした。というのも、このウィンドウは本プラグインでは不要であると考えているからです。ではこのウィンドウは何なのでしょうか?
これは、Scene_Skillにおけるスキルの対象アクター選択ウィンドウです。ツクールでは通常、メニュー画面でスキルを選択するとその対象となるアクターの選択ウィンドウが表示されますよね。これがアクターウィンドウであるわけなのですが、ではなぜ本プラグインでは不要なのでしょうか?
それは、本プラグインにおいてマップ画面上でスキルを選択・使用する時は常にプレイヤーの目の前にいるイベントがその対象となる、という前提であるからです。ですのでこのような対象選択ウィンドウは必要ない、ということなのです。
というわけでcreateActorWindow関数の呼び出しは不要ですので削除してしまいます。
再びテストしてみましょう。
今度こそエラーなく起動できました! ですが以前と同様、ウィンドウ内に何も表示されていません。その原因も以前と同様「アクターが指定されていないから」なのです。
ではScene_Skillではもともと、どのようにしてアクターを取得しているのでしょうか? Scene_Skillの近辺をactorで検索してみましょう。
1662行目は先程削除したcreateActorWindowなので無視して、1667行目に注目してみましょう。このrefreshActorという関数は何やら重要そうな気配がするので、その定義箇所を詳しくみてみます。
以前定義していたsetAcor関数が3つも並んでいます。スキルタイプウィンドウ、ステータスウインドウ、アイテムウィンドウにそれぞれアクターをセットしているようです。ではそのアクターはどこから取得しているのかというと、最初にactorという定数にthis.actor()の戻り値を代入して取得しているようです。このthis.actor()とはScene_Skillに定義されているactorという関数を呼び出しているということなのですが、探してもこの関数は見つかりません。このような場合、上記のcreateHelpWindowのときと同様スーパークラスに定義されていることが多いです。ですのでScene_MenuBase内を探してみます。
rmmz_scenes.jsの1131行目に、Scene_MenuBaseの関数としてactorが定義されています。これがサブクラスであるScene_Skillにも継承されていて、refreshActor関数にて呼び出されていた、ということのようです。
ですのでこちらもrefreshActorともどもコピーしてしまいましょう。その際、その下にあるupdateActorも後々使うことになりそうな気配が漂っているのでついでにコピーしてしまいます。
この状態ではまだrefreshActorを定義しただけで実際に呼び出してはいませんので、その呼び出し箇所もScene_Skillに準じて設定しましょう。Scene_Skillでは、以下の箇所にてrefreshActorを呼び出しています。
startというのはScene_Baseに定義されている関数であり、その名の通りシーン開始時に実行される処理です。つまりScene_Skillでは、シーン開始時にrefreshActorを呼び出し、各種ウィンドウにアクターをセットしている、ということのようですね。この処理をScene_Mapにも流用してしまいましょう。というわけでこの関数もコピーします。
これだけで良さそうに思えますが、実はこのコードには一つ問題があります。それは以下の箇所です。
Scene_ItemBase.prototype.start.call(this);
これは以前にもご紹介したことのある、call関数ですね。Scene_ItemBaseのstart関数を呼び出しているようですが、つまりScene_Skillのスーパークラスの同名関数を呼び出している、ということなのです。スーパークラスと同様の処理を行なった上でrefreshActorを呼び出す、という処理の順番になっています。そうでないとスーパークラスのstartに定義されている重要な処理が実行されなくなってしまうからですね。
ここでポイントなのは、Scene_ItemBaseのstartを呼び出しているのはScene_Skillの直接の親クラスがScene_ItemBaseだから、ということです。Scene_Mapの場合、親クラスが違いますのでそのまま使うことはできません。では以下のようにしてはどうでしょうか。
Scene_Mapの直接の親クラスはScene_Messageです。ですのでこれで問題なく動作するでしょう。
…と言いたいところですが、実はまだ少し問題があります。start関数はもともとScene_Baseに定義されている関数であると言いましたが、このクラスは全てのシーンクラスのスーパークラスです。もちろんScene_Mapの祖先クラスでもあります。ということは、Scene_Mapにももともとstart関数が定義されている、ということなのです。
このようにもともと定義されている関数に処理を追加したい場合、以前にもご紹介した通り元の関数を定数に逃した上で、その定数を呼び出す、という処理にした方が好ましいのです。
つまり以下のようにします。
これは以前にもご紹介した、他プラグインとの競合を避けるためのテクニックですね(以下公式講座の「既存メソッドの再定義」参照)。
これでこの関数は完成です…と言いたいところですが、まだ一つやることがあります。先程Scene_SkillにおけるScene_ItemBaseの箇所をScene_Messageに置き換えたわけですが、実はこの箇所はもはや丸ごと不要なのです。というのも、この処理は元々のScene_Mapのstart関数に含まれているからなのです。つまり_Scene_Map_prototype_start.callの箇所にて実行されているわけなので、同じ処理が2回実行されてしまいます。それでは困るので、Scene_Message.prototype.start.call(this)は削除してしまいます。
これでようやく完成です。早速テストしてみましょう。
またエラーですね……。デバッグコンソールを確認しましょう!
startのrefershActorのsetActorにて、selectLastという関数を未定義のプロパティに対して実行しようとしてエラーが出たようです。
先程actor関数をScene_MenuBaseからコピーしましたが、この関数は以下のようになっています。
つまりthis._actorというプロパティを戻り値とする、という非常にシンプルな処理です。ただしこのthis._actorというプロパティにはまだ初期値を設定していませんので、具体的なアクターが代入されておらず無効な値(undefined)になってしまっているのです。これではエラーが出てもおかしくないですね。
ですのでこのプロパティに実際のアクターを代入したいと思うのですが、その処理には先程「ついでに」コピーしておいたupdateActor関数が鍵を握っています。
これは上記のthis._actorプロパティに具体的なアクターを代入するための処理をしています。具体的なアクターとは$gameParty.menuActor()ですが、これは「メニュー画面で今選択されているアクター」を表します。
というわけでこのupdateActor関数はとても重要な処理をしていることがわかりましたが、ではどこで呼び出されているのでしょうか。検索してみましょう。
Scene_MenuBaseのcreate関数内で呼び出しているようです。このcreateとはその名の通りシーン上のさまざまな要素を作成するための関数であり、Scene_Baseに定義されています。ということは、もちろんそれを継承するScene_Mapにも定義されている、ということです。
では先ほどのように既存関数の再定義によってScene_MapのcreateにupdateActor関数を追加してみましょう。
それではテストしてみましょう!
Scene_Skillと同様のウィンドウがきちんと表示されましたね! また、メニューを開いてアクターを選択するとそのアクターに表示が切り替わるようになっています!
まとめ
今回はスキルウィンドウの内容をきちんと表示するために、Scene_Skillの内容をコピーしてみました。お疲れ様でした。
ですがこのままではまだウィンドウ内の項目を選択することができず、まだ完全に機能しているとはいいがたいです。
次回はその辺りを改良していこうと思います。
それではまたお会いしましょう!
GitHub
この記事が気に入ったらサポートをしてみませんか?