概要
- RPGツクールMVのスクリプトで、Sprite.bitmap.contextを用いてCanvas操作を行う際、context側でclearするのではなく、bitmap側でclear等をしないと描画が正常に行われない。
発端
- 昔々ある日のこと、とあるスーパーツクラーがRPGツクールMV上で直線を描くためのプラグインを作っていました。
- スーパーツクラーは瞬く間に下記のようなコードを作成しました。
Sprite_Line_Straight.prototype.update = function() {
Sprite.prototype.update.call( this );
// Sprite.bitmap.contextを操作して線を描画する
let context = bitmap.context;
// NOTE: 前フレームで描画した内容を一旦クリアする
context.clear();
// NOTE: 左上から右下へ直線が毎フレーム伸びていく描画処理
context.beginPath();
context.moveTo( 0, 0 );
context.lineTo( ( 10 * this.i ) , ( 10 * this.i ) );
context.stroke();
context.closePath();
this.i ++;
}
- しかし実際に上記のコードを実行しても、線が全く表示されません。
- ところが
this.i
の値は正常にインクリメントされており、処理自体は動いています。 - にもかかわらず全く線が描画されないので、スーパーツクラーはブチギレ寸前になりました。
- スーパーツクラーは怒りのあまり創造した世界を破壊しようと、プロジェクトをゴミ箱に配置しかけました。
原因
- 単にSprite.bitmap.context側の描画メソッドを実行しても、Bitmapの描画は反映されない。
- Bitmapの描画を反映するためには、clearを含めたBitmap側の描画メソッドを実行する必要がある。
方法
- clear
-
let bitmap = this._lineCanvas.bitmap; bitmap.clear();
-
- clearRect
-
let x = 0; let y = 0; let width = 816; let height = 624; let bitmap = this._lineCanvas.bitmap; bitmap.clearRect( x, y, width, height );
-
なんで?
-
Bitmapの変更内容が実際に描画されるのは、Bitmap._baseTexture.update()が実行された時。
*Bitmap.prototype._createBaseTexture = function(source){ this.__baseTexture = new PIXI.BaseTexture(source); … };
-
Bitmap._baseTexture.update()が実行されるのは、Bitmap._setDirty()が呼び出され、Bitmap._dirtyがtrueになった時。
*Bitmap.prototype._setDirty = function() { this._dirty = true; }; Bitmap.prototype.checkDirty = function() { if (this._dirty) { this._baseTexture.update(); this._dirty = false; } };
-
Bitmap._setDirty()は実行されるのは、Bitmap.clear()を含めた、Bitmapに定義された描画関連のメソッドが呼ばれた時。
*Bitmap.prototype.clear = function() { this.clearRect(0, 0, this.width, this.height); }; Bitmap.prototype.clearRect = function(x, y, width, height) { this._context.clearRect(x, y, width, height); this._setDirty(); };
-
つまりBitmap.context側のメソッドを呼び出しても、Bitmap._setDirty()が実行されないので、描画が行われなかった!
- これ初見で気づくやつおるか~?
- なお、Bitmap.clear系メソッドは、Bitmap.context.clearRectをラップしているので、クリア処理内容に差異はない。
正しい書き方は?
- Sprite.bitmap.context側のメソッドを実行する一連の処理の前に、Sprite.bitmap.clear()を実行しておく。
Sprite_Line_Straight.prototype.update = function() {
// 実はこれを呼んでもBitmap._setDirty()が実行されるわけではない。
// TODO: スプライトの状態によっては呼び出されるかも?要検証。
Sprite.prototype.update.call( this );
// 前フレームで描画した内容を一旦クリアする
let bitmap = this._lineCanvas.bitmap;
bitmap.clear();
// クリアされるのが嫌ならSprite.bitmap._setDirty()を呼び出すとか?
// bitmap._setDirty();
// Sprite.bitmap.contextを操作して線を描画する
// NOTE: 左上から右下へ直線が毎フレーム伸びていく描画処理
let context = bitmap.context;
context.beginPath();
context.moveTo( 0, 0 );
context.lineTo( ( 10 * this.i ) , ( 10 * this.i ) );
context.stroke();
context.closePath();
this.i ++;
}
結果
- 原因を突き止めたスーパーツクラーは一瞬のうちにコードを修正、無事に直線を引くプラグインを作成することができました。
- 落ち着きを取り戻したスーパーツクラーはプロジェクトの削除作業を中断し、世界に平和が訪れましたとさ、めでたしめでたし。