概要
- 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 ++; }
結果
- 原因を突き止めたスーパーツクラーは一瞬のうちにコードを修正、無事に直線を引くプラグインを作成することができました。
- 落ち着きを取り戻したスーパーツクラーはプロジェクトの削除作業を中断し、世界に平和が訪れましたとさ、めでたしめでたし。