コアスクリプト解読で分からない部分を話し合いましょう

プラグインを作る人にとって、避けて通れないのが、MVのコアスクリプトの解析です。
このスレッドでは、主に、公式のコアスクリプトを読んでいて、分かりにくいところなどを
質問、相談し合うスレッドにしたいと思います。
(※注意:現在では、アツマールのコアスクリプトなども出ていますが、筆者はそちらには明るくないことを
あらかじめお断りしておきます。分かる方のご回答をお待ちください)
なお、相談するコアスクリプトのバージョンは、特に必要な場合(例:古いプラグインの改造に必要なケースなど)以外は、
最新版('17/05/05 では、 1.4.0)を前提として話しましょう。

■おまけ1:コアスクリプト解読、はじめの一歩
MV初心者講座のプラグイン編に加筆修正しました。(赤字が加筆部分)

rpg_core.js
画像や入力など、システムの土台となる部分を記述したファイルです。車で言えばエンジンの部分にあたるため、この部分は普通プラグインで書き換えません。書き換えるのは上級者になるでしょう。 解析は最後が無難

rpg_managers.js
ImageManager や、BattleManager など、マネージャ、つまり様々な要素を管理するためのモジュールを集めたファイルです。後述のファイルで定義されたクラスから頻繁に呼び出されます。
このため、下記のファイルから先に解読して、~Managerが出てきた時点で読むことをオススメ

rpg_objects.js
アクターや敵キャラ、アイテムなど、ゲーム内のデータとその処理を記したクラスを集めたファイルです。ここで定義されるクラス名は、全て「Game_」 で始まります。 このファイルか、次のファイルを最初に読むのをオススメ

rpg_scenes.js
シーンを扱うクラスを集めたファイルです。マップシーン(Scene_Map)やメニューシーン(Scene_Menu)など、ゲームのシチュエーションごとにシーンは細かく分けられています。それぞれのクラスは、ベースとなるシーン(Scene_Base)をオーバーライドし、バトルシーン(Scene_Battle)では rpg_manager.js の BattleManagerを多く参照するなど、緻密で理解しやすい構造になっています。 是非、rpg_managers.jsのSceneManager と一緒にお読みください

rpg_sprites.js
ゲーム画面の表示を司るファイルです。ここまでのファイルの記述では、ゲームの処理は記述していても、画面に関する記述はしていません。このファイルと、次の rpg_windows.js に表示関連の処理をまとめることで、「内部処理」と「見た目の処理」を分離しているのです。名前が「Sprite_」で始まるクラスは、イベントやキャラなど、各要素の画像をスプライトとして表示し、「Spriteset_」で始まるクラスは、複数のスプライトをまとめて画面を構成しています。
Game_xxxx と Sprite_xxxx が1対1対応していることに気づけば、半分は理解できたも同然です

rpg_windows.js
画面内に表示される数多くのウィンドウを一括して定義したファイルです。全てのクラス名が「Window_」で始まります。 全てのウインドウに共通の処理は、Window_Base で、選択肢のあるウィンドウの共通処理は Window_Selectable でやっているため、このふたつを中心に理解すると早いです

■おまけ2:MVスクリプト習得の裏技:VX Aceから勉強する
実は、MVのコアスクリプトは、ツクールVXAceのRGSS3、つまりRubyで書かれたスクリプトを、
そのまま、JavaScriptにした上で(一部、RubyとJavaScriptの言語仕様の違いから、構造を書きかえている部分あり)、
新機能を追加したものです。
そして、RGSS3には、丁寧な日本語コメントがついている上、ヘルプに「スクリプト入門」があります。
このため、MVのコアスクリプトの解析のために、わざわざVX Aceの該当部分を見る人が、結構いると聞きます。
これもひとつの勉強方法かもしれないと思い、紹介しました。
ご存知の通り、VX Aceは、しょっちゅうセールやってますので、これ目当てに買うのも悪くないかも……?
 

トリアコンタン

モデレーター
スタッフ
モデレーター
詳細な解説をありがとうございます!
では、私の方からメソッドに関する解説を少し追記します。

以下のメソッドは様々なクラスで同じ名称になっていて、それぞれ役割が決まっています。
役割を予め理解しておけば読み解くときの助けになると思います。
  • initialize
「初期化」処理です。オブジェクトを作成したときに同時に呼ばれて、オブジェクトの初期状態を構築します。
  • update
「状態を更新する処理」を指し、一部の例外を除いて「毎フレーム(1/60秒)」必ず呼び出されます。
Sprite~というクラスなら画像の状態を、Game~というクラスならゲームの状態を更新します。
イベントで言うところの「並列処理」に近いイメージです。よってupdateの処理の総量はそのままゲームの重さに繋がります。
  • refresh
「再構築」処理です。今までの内容を一旦クリアして構築し直します。
WindowクラスやSpriteクラスのrefreshは多くの場合負荷が高く、毎フレーム実行するには非効率です。
なので再構築する必要がある場合のみrefreshが呼ばれます。

2017-05-05 (1).png

以上、簡単ですが参考になりましたら。
 
若干上級者向けですが、僕からネタをひとつ。
タッチ移動で使われる、経路探索のカスタマイズについてです。
経路探索は、Game_Character.prototype.findDirectionTo() という関数でやっているのですが、
長い事、ここをいじって、8方向の経路探索プラグインなどを作る人が現れませんでした。

これは、ここで使われている「A*アルゴリズム」を難解に感じた人が多かったのかもしれません。
現に、この単語で検索しても、原理は書かれていても、記述が難しすぎるか、
初心者向けであまり詳しく突っ込んでないサイトしか見つかりません。

そこで「この関数は何をやっているのか」を、なるべく分かりやすく、
日本語だけで、僕の言葉で、説明します。以下、皆様の理解の助けになれば。

■A*アルゴリズム概要:
・「オープンリスト」に開始座標を追加。
・「オープンリスト」が空でない間、以下を繰り返す{
 ・現在座標=「オープンリスト」内の最も「コスト」の低い座標
 ・※もしここで、現在座標が目的地座標になったら、めでたく探索成功で終了。
 ・現在座標を「クローズドリスト」に移す。
 ・現在座標に隣接する各座標を以下のやり方で調べる{
  ・「オープンリスト」にも「クローズドリスト」にも含まれてない&通行可能なら{
   ・「オープンリスト」に座標を移して「コスト」を計算。
  }
 }

・目的地座標にたどり着けなかったので、探索失敗で終了。

■ところで「コスト」って何? どう計算しているの?:
今回の場合「開始座標から目的地座標までの歩数」と同じ意味です。
つまり1歩=コスト1です。
もし、例えば「森は敵が出やすいからコスト2にしよう」「山道はさらに敵が出やすいからコスト5にしよう」
なんて改造も、原理を理解すれば簡単に可能です。

A*アルゴリズムでは、「コスト」を参照して計算いるのが分かると思います。
しかし、計算中は当然、目的地までの正しい歩数なんて分かりませんよね。
そこで、A*アルゴリズムでは「大体このくらいかな」という推測関数を作り、
それを「ヒューリスティック関数」と呼んでいます。
コアスクリプトでは「現在座標から目的地までの(X座標の距離+Y座標の距離)」を
ヒューリスティック関数にしています。

よって、「コスト」の計算は、以下の式で行います。
「コスト」=「開始座標から現在座標までの歩数」+「ヒューリスティック関数で計算した推定歩数」

■じゃあ、Game_Character.prototype.findDirectionTo()は何をしているの?:
・まず、上記のA*アルゴリズムで経路を発見。
 ・ただし、いくつかマイルールを設けている:
  ・あまり長くなると計算時間がかかりすぎるため、12歩を超えたら探索を打ち切る。
   この歩数は Game_Character.prototype.searchLimit() をいじれば変更可能。
  ・縦と横のうち、距離が短い方を先に優先して移動する。
・発見した経路の「最初の一歩の方向」を返す。もし、探索失敗なら(=経路が存在しないか遠すぎなら)0を返す。

■タッチ移動を8方向に拡張したプラグイン
上記を理解すれば、いろんな拡張は、それほど難しくないことが分かるでしょう。

以下は、手前味噌で恐縮ですが、僕がタッチ移動を8方向に拡張したプラグインです。
https://twitter.com/ktakaki00/status/735898718247866368
「他に誰か作ってるんじゃ?」と思いつつ、誰も作ってなかったので、作りました。

皆さんも、何か気になる点があったら、改造にチャレンジしてはいかが?
 
最後に編集:

ツミオ

ユーザー
プラグイン製作には興味がないけれど、自作システムを作ってみたいという人(要は僕みたいな奴です)にも嬉しいスレッドですね。
ありがとうございます。

僕が自作システムを製作するにあたって頻繁に参照した記事がありますので、ここで紹介しておきます。
http://qiita.com/krmbn0576/items/7d13d2c2f63d7dcf9300
プラグインをこれから製作してみようと思っている方はもちろん、僕のように自作システムの導入を考えている人も読んで損はない記事です。

Game_xxxx と Sprite_xxxx が1対1対応していることに気づけば、半分は理解できたも同然です
ちょうど昨日、イベントの色調を変える機能がほしくて(僕が見た限りはデフォの機能になかったのですが、もしあったらすみません)色々とコアスクリプトを眺めていました。
上記引用文のことをもう少し早く知っていたら楽に機能追加できたのかもなあと思いましたので、紹介がてら引用いたします。
ちなみにですが、イベントの色調変化というのは下図のようなもののことです。
もしデフォ機能でできるようでしたら、ぜひ教えてください(ツクール一ヶ月目の初心者なので、見落としている可能性がかなりあります)。
しきちょーへんこー.jpg

ただ
「Spriteset_」で始まるクラスは、複数のスプライトをまとめて画面を構成
というのが僕にはちょっと難しくて理解できておらず、「なんかようわからんけど動いた」というレベルでの動作しかできていません。
SpriteとSpritesetの違いをもう少し詳しく解説していただけたら嬉しいなーと思います(ゴニョゴニョ)。


非常に参考になる投稿、ありがとうございます。
これからもちょいちょい覗いて勉強させていただきます。
 
最後に編集:
>ツミオさん、こん**は。
ご紹介のサイト、拝見したが、確かに、スプライトの説明が少ないですね。
せっかくなので、こちらでご紹介いたしますね。

Spriteset_xxxx とは、ずばり、「その画面で表示されるスプライトを『全て』入れた入れ物」です。
マップの画面では、Spriteset_Map、バトルの画面では、Spriteset_Battleが、それにあたります。
共通点が多いため、共通点を、Spriteset_Base にまとめてあり、これが上記の2つのスーパークラスになります。

■ひとつの定義でScene_xxxxクラスからスプライト全体を呼び出せるようになる

実際、rpg_scenes.js の Scene_Map クラスをご覧下さい。
Scene_Map.prototype.createSpriteset = function() {
 this._spriteset = new Spriteset_Map();
 this.addChild(this._spriteset);
};

ここの this._spriteset で、Spriteset_Map のインスタンスを作成することにより、
これだけで、全てのスプライトに、Scene_Map クラスからアクセス可能になるわけです。

Sprite_Map を Sprite_Battle に変えれば、バトル画面での定義になります。
Scene_Battle.prototype.createSpriteset = function() {
 this._spriteset = new Spriteset_Battle();
 this.addChild(this._spriteset);
};


■Spriteset_xxxx にスプライトを登録していく様子

では、試しにSpeiteset_Mapのほんの一部を見てみましょう。
Spriteset_Map.prototype.createLowerLayer = function() {
Spriteset_Base.prototype.createLowerLayer.call(this);
this.createParallax();
this.createTilemap();
this.createCharacters();
this.createShadow();
this.createDestination();
this.createWeather();
};


Parallax(パノラマ)、タイルマップ、キャラ(イベント&プレイヤー&乗り物など)、飛行船の陰、
行き先、天候スプライトの順番に作られているのがお分かりと思います。

試しに、createParallax を見てみましょう。
Spriteset_Map.prototype.createParallax = function() {

this._parallax = new TilingSprite();
this._parallax.move(0, 0, Graphics.width, Graphics.height);
this._baseSprite.addChild(this._parallax);
};

この中で特に、 this._baseSprite.addChild(this._parallax); に注目です。
addChild によって、Spriteset_Map のインスタンスに追加しているんですね。

他のスプライトも、同様に addChild で追加されて行きますが、大切なのは、その順番。
「後から追加された方が、上に表示される」というルールがあります。

また、スプライトは木構造に追加されていきます。
実際、this._baseSprite というのは、下層スプライトをまとめるために作られたもので、
Spriteset_Base.prototype.createBaseSprite() にて、
this.addChild(this._baseSprite);
とされており、その上に上層レイヤ(フラッシュする層とか)が追加されて行きます。

こうすることで、後から、this._baseSpriteにスプライトを追加しても、
上層レイヤの上に行くことがない、ということです。

■プラグインなどから、スプライトを直接操作する方法
TinyGetInfoWnd.js など、プラグインコマンドでスプライトを追加するプラグインがありますね。
https://twitter.com/ktakaki00/status/839428915525373954
あれはどうやっているのでしょう。

ずばり、こうやっています。
var spriteSet = SceneManager._scene._spriteset;
ここで、SceneManager._scene とは、現在のシーンのインスタンスが入ります。
これがバトルかマップなら、上記の式で、Spriteset_xxxx クラスのインスタンスが入ります。

よってここに、新しくスプライトを追加すれば、表示される、というわけです。
TinyGetInoWndより引用します。
Spriteset_Base.prototype.addGetInfoWindow = function(id, type, text, value) {
 var window = this.createInfoWindow(id, type, text, value);
 this.getInfoWnds.push(window);
 SceneManager._scene.addChild(window);
};

これ、プラグインの一部なので単体では正常動作しませんが、大切なのは最後の行。
最後にしっかり、SceneManager._scene.addChild(window);
と書かれているのに注目です。

■以上
若干長くなりましたが、ご満足いただけたら幸いです。
まだ「このあたりがわからない、うろ覚え」といったところがあったら、遠慮なくおっしゃってくださいな。
 
ツミオさん:続きです。
>ちょうど昨日、イベントの色調を変える機能がほしくて(僕が見た限りはデフォの機能になかったのですが、もしあったらすみません)色々とコアスクリプトを眺めていました。
>ちなみにですが、イベントの色調変化というのは下図のようなもののことです。
>もしデフォ機能でできるようでしたら、ぜひ教えてください(ツクール一ヶ月目の初心者なので、見落としている可能性がかなりあります)。

お答えします。それはツクールMVでは、プラグインか、イベントコマンド「スクリプト」による操作が必要な項目です。
実は僕も、この機能は必要としていて、AndAppのゲーム用に、
イベントの色合い変更、時間をかけて不透明度を変更、上下反転、クロスフェードなどを使ったプラグインを作っています。
これは、ツミオさん以外にも需要がありそうなので、
素材/プラグイン交換ツクールMV向け にスレッドを立てて、紹介しようかなと思っています。
見掛けたら、DLしていただけると嬉しいです。

追記:アップいたしました!
https://forum.tkool.jp/index.php?threads/スクリプト:イベントの上下反転やクロスフェードなどを実現.92/

ツミオさんはプラグインより自作システムを目指されているようですが、
MVで自作システムを作るなら、スクリプトをかじっていて損はありません。
よって、プラグインの中身を読むことも勉強になると思いますので、
「これどうやってるの?」って気になる点が出てきたら、
中を読むことも勉強法の一つとして提案しておきます。
 
最後に編集:

ツミオ

ユーザー
神無月サスケさん。
わかりやすい解説をありがとうございます。
目からうろこなことが次々と出てきて、ゲーム制作が楽しくなってきています。

ほぼ神無月サスケさんの書き込みの繰り返しになりますが、僕なりに理解したことを書いてみます。


各シーンのSpritesetにSpriteを追加していっているのですね。

今まで例えば

コード:
Scene_Map.prototype.hoge = function(character){
    this._spriteset._tilemap.addChild(new Sprite_Character(character));
}
のようなコードを書いたとき「_spritesetってなんぞ」と思っていたのですが(他サイトさんのをコピペして使っていました)、ようやく内部でどのような処理がなされているのか理解できました。

ついでに言うと、ここでまさに「Game_xxxx と Sprite_xxxx が1対1対応している」ということなのですね(上記のコードの場合だとGame_CharacterBaseやそれを継承したもの)。

「_tilemap」は要はマップチップ系を置いてあるやつを指定しているのかなーと思って使っていますが、もし全然違っていたら指摘していただけると助かります。


ちなみにですが、上記のコードを
コード:
Scene_Map.prototype.hoge = function(character){
    SceneManager._scene._spriteset._tilemap.addChild(new Sprite_Character(character));
}
としても正常に動作することも確認しました。
これが「SceneManager._sceneとは、現在のシーンのインスタンス」ということなのですね。
ただSceneManagerの挙動をほとんど理解していないため(JavaScript……というよりプログラミングそのものの知識も足りない)、なぜここでSceneManagerを呼べるのかがわかりませんが……。
JavaScriptの入門書を読みつつ、徐々にコアスクリプトを調べていきたいと思います。


【こっからスクリプト解読と関係のない話】

初めてのゲーム制作、初めてのJavaScript、初めてのツクール……と初めてづくしということもあって、ほぼ行き当たりばったりですが、こういった場で徐々に知識を増やしていけたらなと思っています。

今のところ
  • rpg_windows.js
  • rpg_scenes.js
の二つはそこそこ読んだので、
  • rpg_sprites.js
  • rpg_objects.js
あたりにも挑戦していきたいですね。

もちろん「プラグインの中身を読む」ことも並行して挑戦してみます。
素敵なプラグインを作っている方がとても多く、僕もちょこちょこ導入させてもらっています。
投稿していただいたプラグインの中身もさっそく見てみますね。


最後になりましたが、詳しい解説、本当にありがとうございました!
とても勉強になりました。
 
最後に編集:
用語辞典や魔物辞典などで一覧リストが表示されますが、
[Pagedown]キー(Q)や[PageUp]キー(W)を押すと、リストの次ページ・前ページにすぐにアクセス可能です。

これはそれらのプラグイン内には記述されていないみたいですが、
コアスクリプトのどの部分で挙動を操作しているのでしょうか?

プラグイン内にはOK or キャンセル操作は下記の様に記述されている
例)this._****Window.setHandler('cancel', this.popScene.bind(this));

用語辞典・魔物辞典の画面において、Qキー・Wキーが効く事が分かりづらすぎるので
カーソルの左右キーに変更したいのですが、これはプラグイン内、コアスクリプトどちらをどの様に操作するのが最適でしょうか?

また、装備画面もQキー・Wキーでのアクターの切り替えを、カーソル左右キーに切り替えれば便利と言えば便利ですが、
こちらは装備品の選択で左右キーを使うため不具合の元となりそうで。

上記ご教授頂ければ幸いです。
 

ツミオ

ユーザー
用語辞典や魔物辞典などで一覧リストが表示されますが、
[Pagedown]キー(Q)や[PageUp]キー(W)を押すと、リストの次ページ・前ページにすぐにアクセス可能です。

これはそれらのプラグイン内には記述されていないみたいですが、
コアスクリプトのどの部分で挙動を操作しているのでしょうか?

プラグイン内にはOK or キャンセル操作は下記の様に記述されている
例)this._****Window.setHandler('cancel', this.popScene.bind(this));

用語辞典・魔物辞典の画面において、Qキー・Wキーが効く事が分かりづらすぎるので
カーソルの左右キーに変更したいのですが、これはプラグイン内、コアスクリプトどちらをどの様に操作するのが最適でしょうか?

また、装備画面もQキー・Wキーでのアクターの切り替えを、カーソル左右キーに切り替えれば便利と言えば便利ですが、
こちらは装備品の選択で左右キーを使うため不具合の元となりそうで。

上記ご教授頂ければ幸いです。
あおきことりさん。
僕も勉強がてら、少しコードを追いかけてみました。
以下はデフォルトのプラグインEnemyBook.jsについての話になります。

結論から言うと、最終的に「Window_Selectable.prototype.cursorPagedown」を呼んでWキーが押された際の処理を決めているようです(rpg_window.js)。
このcursorPagedownがどこから呼ばれているかと言えば、「Window_Selectable.prototype.processCursorMove」です。
この部分をいじれば、目的の動作が達成できるかなと思います。

ただし、Window_Selectableを継承して作られている機能は多岐に渡ります。
ここを直接いじってしまうと、おっしゃる通り思わぬ不具合の原因となってしまうかもしれません。
そこでWindow_Selectableを継承したクラスを作り、「processCursorMove」と「cursorPagedown」の挙動を変え、それをもとに目的の機能をもったWindowを作るのがよいと思います。


短くなりましたが、以上参考になれば幸いです。

追記:
function Window_EnemyBookIndex() {
this.initialize.apply(this, arguments);
}
Window_EnemyBookIndex.prototype = Object.create(Window_Selectable.prototype);

EnemyBook.jsなら、太字で書いた場所を継承後のクラス(クラスと言わないという話も聞いたのですが、それ以外に何と呼べばいいのか僕は知らないので便宜上クラスと呼びます)に変えてください。

【ここからすごく個人的な話】
プラグインの改造を始めたばかりのころ、あおきことりさんのブログの「プラグイン作成講座」等をとても参考にさせてもらいました(違う人だったらすみません)。
大変わかりやすく、右も左も分からない僕にとってはバイブルのようなものでした。
ありがとうございます。
 
最後に編集:
>ツミオさん
ご回答ありがとうございます!!
仰る通り、Window_SelectableをWindow_Selectable02として別クラスを作って、processCursorMoveを書き換える事でうまく動作しました。

if (Input.isRepeated('right')) {
this.cursorPagedown();
//this.cursorRight(Input.isTriggered('right'));
}
if (Input.isRepeated('left')) {
this.cursorPageup();
//this.cursorLeft(Input.isTriggered('left'));
}

なるほど、Window_Selectableにウィンドウ内のキー操作も含まれていたんですね。
助かりました。

プラグイン作成講座読んでいただいて恐縮ですm(__)m
Javascript苦手な私が何とか学びつつ書いた懐かしい記事です。いまだに慣れませんが、沢山の方のお役に立てれば何よりです。
 

ツミオ

ユーザー
いつもお世話になっております。

今回の質問は僕自身なにを書けばいいのかあまり理解していないところがありますので、もし「この情報が足りなくて判断できない」という点がありましたら、ご指摘いただければなと思います。

少し長くなるので、まず質問の概要を簡単にまとめます。

【知りたいこと】
Game_EventクラスからScene_Mapクラスのメソッドを実行する方法を知りたい。


【やりたいこと】
イベントのトリガーがONになったとき、イベントに実行する内容が何もなければ独自のイベントを実行する。
今回はScene_Mapのthis._spritesetに定義されているメソッドを実行したい


ここから少し詳しめに書きます。

【やったこと】

Game_Event 内でトリガーがONになったかどうかをチェックし、ONになったら$gameMap.events()[x].commandプロパティ(command プロパティは独自に追加したもので、on,off以外の中身はありません)の内容を'on'に書き換えます。
次にScene_Mapのupdateで、マップ上に存在する全てのイベントの$gameMap.events()[x].commandをチェックし続けます。
もしonになったものがあれば、特定のメソッドAを実行します。
見た目の動画はこんな感じです。
イベントの判定タイミングを少しいじっていますが(主人公とイベントが重なっていないとイベントは実行されません)、黒い■が主人公の位置です。
イベントが実行されてかつ、イベントの中身が空のときに青い物体を表示するようにしています。
これでも確かに僕のやりたい動作は達成できているのですが……。

今回の場合ですと、イベントの中身が空のときに即特定のメソッドAを実行した方が負荷も少ないし、コードもわかりやすいのかなと思いました。
そう思った理由も記しておきます。

【そう思った理由】

今の方法ですと
1.イベントが実行されてかつイベントの中身が空の場合に
2.$gameMap.events()[x].commandプロパティをONにする
3.Scene_Map.prototype.updateで$gameMap.events()[x].commandの中身をチェックして(常に監視しているので、これも重いと思います。特にイベントが増えると……)
4.チェックされた内容がonなら特定のメソッドAを実行
といくつかの段階を踏んでいます。

これを
1.イベントが実行されてかつイベントの中身が空の場合に
2.特定のメソッドAを実行
とできるとScene_Map.prototype.updateで値を監視する必要がなくなって軽くなるかなと思いました。

また、Game_Eventクラス内で$gameMapの値を書き換えるのはそもそも何か変ではないか? という感じもしました(実際、コアスクリプト内をさらっと見た感じ、参照はしても書き換えてはいないようでした)。


【具体的に何をどうしているのか】

まずGame_Eventクラスのstarの内容を以下のように書き換えました(else以下が追加部分です)。

コード:
Game_Event.prototype.start = function() {
    var list = this.list();
    if (list && list.length > 1) {
        this._starting = true;
        if (this.isTriggerIn([0,1,2])) {
            this.lock();
        }
    }else{//この行から追加したコード
        this.onCommand();
    }
};
このときonCommandが実行されるタイミングでScene_Map.prototype.hogeメソッドを実行したいのですが、そのやり方がわからずに困っています。
Game_xxx系のクラスは外部からアクセスする用の変数が用意されているようなのですが、どうもScene_xxx系はそういったものがないようなのです……(僕が見落としているだけかもしれません)。

こ のhogeメソッドは、Scene_Mapの_spriteset(Spriteset_Map)にアクセスし、その中の特定のメソッドを実行しています (動画で言うと青い物体を描く部分です。「キャラクターよりは下に描きたいが、他のタイルよりも上に描きたい」のでSpriteset_Mapをいじる形 になりました。これも変かもしれません)。

【結局なにをどうしたいのか】
最 初に書いたように「Game_EventクラスからScene_Mapクラスのメソッドを実行する方法を知りたい」のですが、「そもそもそういう処理の仕 方がおかしい」ということでしたら(そんな気もしています……)、「イベントのトリガーがONになったとき (Game_Event.prototype.startのstartが実行されたとき?)、独自の処理をはさむ方法」でより適切なものを知りたいと思っ ています。

以上長くなりましたが、もしご存知の方がいましたら、教えていただけませんでしょうか。

よろしくお願いいたします。
 

トリアコンタン

モデレーター
スタッフ
モデレーター
こんにちは。
結論から言ってしまうとSceneManager._sceneでどこからでも現在のシーンオブジェクトを参照できます。

ただ、お察しの通りGameクラスからSceneクラスに直接アクセスすることは、既存のコアスクリプトの参照、被参照の関係を崩してしまいます。

それにより可読性、保守性が若干低下しますが、それを許容するかどうかは個々人の価値観次第だと(個人的には)思います。
 
最後に編集:

ツミオ

ユーザー
こんにちは。
結論から言ってしまうとSceneManager._Sceneでどこからでも現在のシーンオブジェクトを参照できます。

ただ、お察しの通りGameクラスからSceneクラスに直接アクセスすることは、既存のコアスクリプトの参照、被参照の関係を崩してしまいます。

それにより可読性、保守性が若干低下しますが、それを許容するかどうかは個々人の価値観次第だと(個人的には)思います。
お返事ありがとうございます。
教えていただいた通りにすることで、無事に目的の動作を達成させることができました。

邪道な方法なようですが、もう少しプログラミングのスキルが上達するまではGameクラスからSceneクラスに直接アクセスするやり方で行ってみようと思います。

解答ありがとうございました。
 

尾角つの

ユーザー
僕もよろしいでしょうか?

(ver.1.4.1ですが) rpg_core.js の WindowLayer の初期化の部分(6440行)の最後に、メモリリークバグの対応?で
this.on('removed', this.onRemoveAsAChild);
を呼び出しているみたいなのですが、これを使用しない場合、どのようなメモリリークが起きるのでしょうか?

<経緯>
Windowクラスとよく似た内容のクラス、(Perchクラスって言ってますが)画像を独自にまとめたクラスを作ってます。
まとめた画像が width height からはみ出さないようにマスク処理をかけようと思っていて……
初期化の部分で、
Perch.prototype.initialize = function() {
PIXI.Container.call(this);
マスクのセットアップ処理(WindowLayerのinitializeに近い処理)
Perchの変数の初期化処理 (Windowのinitializeに近い処理)
}
という感じで(わかりにくくてすいません)、マスクの処理内で this.on('removed', this.onRemoveAsAChild) を呼び出してから、変数の処理で this.addChildAt(sprite, index) を行おうと思っているのですが、問題ないのでしょうか?
(ちなみに、WindowLayer参考にレンダーの仕方も勉強中です…)

わかる方がいらっしゃれば、ご教授いただけると助かります。
よろしくお願いします(_ _)

※追記:記述間違えていたので、修正しましたすみません。this.onのところ
 

トリアコンタン

モデレーター
スタッフ
モデレーター
こんばんは!

こちらの記述が追加されたのは、本体ver1.3.2からです。
去年の夏に本体1.3.0でpixi.jsが4系にアップデートされた際に、メニューの開閉を繰り返すとメモリリークが発生するという現象が報告され、その対応を行ったものです。

以下が本体1.3.1(対応コードなし)でメニュー開閉を繰り返したときのメモリ使用状況です。
青と緑の線が時間と共に増大しているのが分かります。
2017-05-23 (1).png

そして以下が本体1.3.2(対応コードあり)でメニュー開閉を繰り返したときのメモリ使用状況です。
各数値とも極端な増大は見られません。
2017-05-23.png

詳細の原因は把握していませんが、シーン遷移時にWindowLayerが破棄された際に
子要素に対する参照がどこかに残ってしまい、それがメニュー開閉(つまりMapとMenu間のシーン遷移繰り返し)を繰り返すことでゴミがたまっていったことが直接の原因と思われます。
対策として、remove時のイベントで明示的に子要素を破棄しています。

ただし、この現象は現在の最新版であるpixi.js 4.0.3では見られなくなっています。
こちらは、本体1.4.0かつメモリリーク対応コードをコメントアウトして計測した結果です。
おおよそ本体1.3.2のグラフに近いかたちで安定しています。

2017-05-23 (2).png

WindowLayerを参考にしてマスク処理を行うSprite群を作成されるとのことですが、
結論としては、保険として当該コードを含めることに問題はないかと思いますが、おそらく不要かと思います。

もしどうしても気になる場合、メニュー開閉を延々と自働で繰り返す以下のテストプラグインと
F8を押下して表示されるデベロッパツールのTimelineタグでメモリ状況を計測してみるといいかもしれません。
https://raw.githubusercontent.com/triacontane/RPGMakerMV/master/ForceMenu.js
 

尾角つの

ユーザー
トリアコンタン様、ありがとうございます!
詳細の画像まで用意していただけて、本当に嬉しいです。

しばらくツクールに触れていなかった時期に、ウィンドウを開閉するとメモリリーク起きるっていうのは小耳に挟んではいましたが、その部分の修正だったんですね。
現状(v4.0.3)では解決ということで、まずは入れないで作成してみて様子を見ようと思います。(メモリリーク起きたら入れてみます。)

こんなすぐに回答いただけるとは思っていなかったので驚きました(*´∀`*)
僕ももっとプログラムいじれるように勉強しないとですね…
再度、ありがとうございました!
 

ツミオ

ユーザー
たびたび失礼します。

まず質問をごく簡単にまとめます。

・複数のファイルにまたがって記述されたクラス(やメソッド)を利用する方法が知りたい

ここから詳しく書きます。

よく使う機能をまとめたプラグインファイルをBaseSystem.jsとして保存し、自作ゲームのプラグインとして導入しました。
プラグインは追加した順番で上書きされると聞いていたので、このBaseSystem.jsを一番上に置き、他のBaseSystem.jsの機能を使っているファイルを下に置きました。

さあ動かすぞと思ってゲームを実行してみると、何やらエラーが出ました。
そのエラー自体はなんだかよくわからないエラーだったので放置したのですが、「あ、そういえば即時関数で外部から参照できないようにしているとか聞いたな」と思い出しました。

そこでベースファイルの
(function ()
を除けて、最後の
)();
も除けました。

ゲームを実行してみると無事エラーは出なくなり、正常に動くようになりました。
ですが他の方のプラグインを見てみると、この即時関数をつけているものがほとんどです。
即時関数を除けることによって何か不都合なことがあるんだろうなと予想はつくのですが、他にエラーを出さなくする方法がわからず困っています。


繰り返しになりますが、お尋ねしたい内容を最後に箇条書きでまとめておきます。

  1. ベースファイルのクラスやメソッドを別のファイルでも使いたい
  2. 今は即時関数を外して使っている
  3. 即時関数を外すと何か問題が出る?
  4. もし問題が出るようなら、どのようにすればよいのか知りたい

自分でもよくわかっていない部分が多々あるので質問が不明瞭かもしれませんが、以上よろしくお願いいたします。
 

フトコロ

ユーザー
>ツミオさん

私は即時関数を使ってプラグインを作成していませんが、問題は起きていません。

ちなみに即時関数を使わないことで起きる問題としては、他のプラグイン(またはコアスクリプト)で使用している関数(変数)と同じ名前の関数(変数)を作成してしまった時に動作がおかしくなる、ということが考えられます。

使用する名前には気をつけて、他の人が使わないようなものにすれば大丈夫だと思います。
そもそも、コアスクリプトは即時関数を使っていませんから。
 

ツミオ

ユーザー
フトコロさん。
お返事ありがとうございます。

他のプラグイン(またはコアスクリプト)で使用している関数(変数)と同じ名前の関数(変数)を作成してしまった時に動作がおかしくなる
使用する名前には気をつけて、他の人が使わないようなものにすれば大丈夫だと思います。
そもそも、コアスクリプトは即時関数を使っていませんから。
そういうことだったんですね。
これで安心して使えます。
なるべく名前のかぶらない関数を作るようにはしているので、即時関数を外したまま使おうと思います。

とてもわかりやすかったです。
回答ありがとうございました!
 
トップ