ウィンドウの内容を描いてみた

 前回は WindowWindow_Base、それから WindowLayer を調べました。
 Window が担ってるウィンドウ枠については前回だいたいやったので、今回は Window_Base を詳細に調べていきます。

Window は黒板的な感じ

メソッドを調べる

  Window_Basecontents の描画に使うメソッドを取り出して、カテゴリごとに分類してみました。
 大量にありますね。この辺を自在に使えるようになれば、カスタムレイアウトも自由自在…かなぁ。
 これだけあるんだから、結構色々できるようになるのは間違いないですね。
 contents のプロパティやメソッドで十分じゃないか? みたいなのもたくさんある気がしますが。

表示座標の取得

 表示する場所を決める時に呼び出す系。
 x, y の座標をピクセルで決め打ちしていくこともできますが、例えば lineHeight() を使って行の高さを基準にレイアウトすると画面に統一感が出ます。

色の取得

 これは簡単ですね、対応した色情報が帰ってくるだけで、複雑なことは何にもありません。
 前回詳しくやってるので、今回はメソッドを並べるだけ。

文字データの取得・制御

 文字の表示の際にデータを読み取るために使われるものです。
 なので、大抵は直接使う必要はないと思いますが、自前で細かい表示を制御したい場合は使う必要がありますね。
 引数が text のものは普通の文字列ですが、textState の場合は文字をアニメーションしつつ表示するような場合に使われます。
 また、制御文字の処理をするメソッドなんかもあります。

画像の調整

 画像や文字を表示する際の色や大きさの設定。

画像の表示

 画像を表示するためのもので、読み込み段階が必要なので、単純にメソッドを使っただけでは表示されないことがあります。

文字の表示

 文字を表示するためのもので、かなりのものがステータス表示用ですね。
 もちろん、メッセージウィンドウなどに表示する汎用的なものもあります。

ゲージの表示

 ゲージはその場で描いた画像で、ファイルの読み込みの必要がないので即表示されます。

参考記事とか

 むーてぃさんのRPGツクールMVのプラグインでウィンドウを作って表示させる方法や、kotonoha*さんのマップ上に常に情報ウィンドウを表示するを参考にしつつやっていきます。
 参考記事が見つかると、道しるべができて安心ですね。

Windowを追加してみよう

 Scene_Title が、基本的な設定が終わった後に最初に出てくるシーンなので、ここを実験場にしましょう。
 ざっくりみたところ create() メソッドでウィンドウなどを用意しているようなので、ここに間借りすることにします。
 最初は initialize() に書いてみたんですが、なんかうまくいかなかったので(雑な理解)


Scene_Title.prototype.create = function() {
	Scene_Base.prototype.create.call( this );
	this.createBackground();
	this.createForeground();
	this.createWindowLayer();
	this.createCommandWindow();

	// ↓ ここから追加部分
	const w = new Window();
	this.addChild( w );
	w.move( 20, 20, 600, 400 );
};

 一番基本的な Window オブジェクトを追加してみました。基本的すぎて、実際のところ直接使う機会がないんですけどね(笑)
 コンストラクタに座標とサイズを渡す引数すらないので、addChild() でシーンに追加した後に move() を使って大きさを決めてます。
 move() メソッド持ってんなら座標とサイズ渡せて良さそうな気もしますが、設定のタイミングの問題とかあるんでしょう、多分。
 見事タイトル画面に、ウィンドウが表示できました。

 ちなみに addWindow() ってメソッドもあるんですが、これはシーン直下ではなくて WindowLayer の下に追加するという違いがあるだけです。
 とりあえず表示するなら、どっちでも構いません。
  WindowLayer は前回に書いた通り、ウィンドウを重ねずに切り貼りして表示する制御をします。

 さて今度は同様に、Window_Base を追加してみます。
 以下のコードは追加部分だけ抜粋してます。


	// ↓ ここから追加部分
	const w = new Window_Base( 20, 20, 600, 400 );
	this.addChild( w );
};

 コンストラクタには座標とサイズが渡せるので move() が不要になりました。
 見た目としては Window を追加した時と区別つきません。

文字を表示してみよう

 これはごく簡単で、生成したウィンドウ(ここではwに代入している)に対して w.drawText( 'へろーわーるど!', 0, 0 ); という感じに実行すればいいだけ。
 ただし、ウィンドウ幅自動折り返しとかはやってくれません。
 これ、HTMLのテキストを表示してるんじゃなくて画像として Canvas に描画してるんですよね。
 各種ステータスやゲージを表示する系も、同じノリで座標を指定すれば簡単に表示できます。

 制御文字を使えるEx付きのメソッドを使えば w.drawTextEx( '\\I[10]ドクロ', 0, 0 ); みたいにアイコン画像がすぐに使えます。
 直接アイコンを表示する drawIcon() も特に色々考える必要なく使えます。
 これはシステム系の画像は最初に読み込まれて保存されているからで、後で書きますけどシステム系でない画像は読み込みを待つ必要があります。

画像を表示してみよう

 アイコンのようなシステム関連画像以外は都度読み込まれるので、きちんと読み込みが終わったことを確認してからでないと表示されません。
 ある程度はキャッシュされるので、ほかの場面で表示されていたら「たまたま」表示されることもあります。
 たまたまに頼るわけにはいかないので、ちゃんと読み込みをして確認後に表示する方法を調べます。

 大体の機能は ImageManager クラスが持っているようです。
 ImageManager は静的クラスなので、new して変数に入れるとかしないで、そのまま ImageManager と書いて使います。
 この辺、マネージャ系のクラスは全部そうです。

 さて、drawFace()drawCharacter() は画像が事前に読み込まれてないと表示されません。
 最初は、ちゃんとエラーもなく実行されてるのに表示されなくて、ちょっとハマりました。

 ともかくどっかで、 ImageManager.loadBitmap を使って画像を読み込む必要があります。
 ImageManager.loadBitmap()ピクチャを調べてみたでも調べました。
 そこで返ってきたBitmapBitmap.isReady() をチェックすれば読み込みが終わってるかどうかわかるので、update() で地味にポーリングしてチェックする方法がまずあります。
 ただし、厳密なタイミングが必要とされる場合にはポーリングした方がいい場合もあるかもしれませんが、コールバック関数を渡して読み終わったら実行してもらう方が簡単です。
 嬉しいことに、コールバック関数用のメソッドも用意してあります。Bitmap.addLoadListener() です。
 では、この辺を利用して確実に画像を表示するコードを書いてみます。


	// ↓ ここから追加部分
	const charWindow = new Window_Base( 20, 20, 300, 200 );
	const charBitmap = ImageManager.loadCharacter( 'Actor1' );
	charBitmap.addLoadListener( () => charWindow.drawCharacter( 'Actor1', 0, 24, 48 ) );
	this.addChild( charWindow );
}

 もうちょっと条件を見て描画する必要もあるのかもしれませんが、とにかくこれで表示することができました。
 ちなみに、 loadCharacter( 'Actor1' )loadBitmap( 'img/characters/', 'Actor1' ) と同じです。
 drawCharacter() の中に最初から、読み込まれるの待ってから表示する仕組み入れといてくれよという気もしますが、なんか理由があるんでしょうね。

その他

 他にもImageManager.reserveBitmap()ImageManager.requestBitmap() というメソッドがあって、どっちも画像の先読みで使うみたいなんですが、いまいち違いがわからない。
 違い以前に使い方がピンと来てません。
 reserve の方は、Face 以外では全然使われてないかほぼ使われてません。なんなんだろ…

 と思っていたら、RPGツクールMV コミュニティ版コアスクリプト解説 - Qiita に解説してあると教えてもらいました。
 おおっ、わかる、私にもわかるぞ!!
 あと 【解決済】画像のプリロードについて - ツクマテ の質問・回答ページも教えてもらいました。

 ファイルの先読み用に ImageManager.reserveCharacter() というメソッドが用意されていて、そこから ImageManager.reserveBitmap() を呼んでます。
 名前から想像できるかと思いますが、類似の reserveFace() みたいなメソッドが沢山あって、結局みんなパスが違うだけで reserveBitmap() を呼んでます。
 んで、reserveBitmap()ImageManager.reserveNormalBitmap() を呼んでます。
 その中では、画像を読み込んで ImageManager がキャッシュとして持っててくれて、次にImageManage.loadBitmap() で画像の読み込みを指示された時にキャッシュされてたらそこから渡します。
 ピクチャを先読みしておくと、チラつきや読み損ねを抑えることができるんで、エロRPGで下着差分を読み込むのが遅れてすっぽんぽんが表示されてしまう、みたいな事故が防げるはずです(笑)

 そして、requestXxxx は読み込みを予約しておくもので順に読むから通信の負担が少ないけど、予約が多いとすぐに読みに行ってはくれない。
 コアスクリプトには、イベントコマンド実行前にイベントコマンド内に含まれる画像を検索して requestXxxxを呼ぶ処理が入ってます。
 つまり、ほっといてもいい感じに画像を先読みしてくれるようになってるので、スクリプトを書いて requestXxxx を使うことはあんまりなさそう。

 そこで結論。

ウィンドウの描画機能は Window_Base に沢山あるぞ!