RPGツクールMVでCanvas描画処理が反映されない原因と対処方法

## 概要
  * RPGツクールMVのスクリプトで、Sprite.bitmap.contextを用いてCanvas操作を行う際、context側でclearするのではなく、bitmap側でclear等をしないと描画が正常に行われない。

## 発端
  * 昔々ある日のこと、とあるスーパーツクラーがRPGツクールMV上で直線を描くためのプラグインを作っていました。
  * スーパーツクラーは瞬く間に下記のようなコードを作成しました。
  <pre>
    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 ++;
    }
  </pre>
  * しかし実際に上記のコードを実行しても、線が全く表示されません。
  * ところが<code>this.i</code>の値は正常にインクリメントされており、処理自体は動いています。
  * にもかかわらず全く線が描画されないので、スーパーツクラーはブチギレ寸前になりました。
  * スーパーツクラーは怒りのあまり創造した世界を破壊しようと、プロジェクトをゴミ箱に配置しかけました。

## 原因
  * 単にSprite.bitmap.context側の描画メソッドを実行しても、Bitmapの描画は反映されない。
  * Bitmapの描画を反映するためには、clearを含めたBitmap側の描画メソッドを実行する必要がある。

## 方法
  * clear
      * <pre>
          let bitmap = this._lineCanvas.bitmap;
          bitmap.clear();
        </pre>
  * clearRect
      * <pre>
          let x = 0;
          let y = 0;
          let width = 816;
          let height = 624;
          let bitmap = this._lineCanvas.bitmap;
          bitmap.clearRect( x, y, width, height );
        </pre>

## なんで？
  1. Bitmapの変更内容が実際に描画されるのは、Bitmap._baseTexture.update()が実行された時。
    * <pre>
        Bitmap.prototype._createBaseTexture = function(source){
          this.__baseTexture = new PIXI.BaseTexture(source);
          …
        };
      </pre>
  1. Bitmap._baseTexture.update()が実行されるのは、Bitmap._setDirty()が呼び出され、Bitmap._dirtyがtrueになった時。
    * <pre>
        Bitmap.prototype._setDirty = function() {
          this._dirty = true;
        };

        Bitmap.prototype.checkDirty = function() {
            if (this._dirty) {
                this._baseTexture.update();
                this._dirty = false;
            }
        };
      </pre>
  1. Bitmap._setDirty()は実行されるのは、Bitmap.clear()を含めた、Bitmapに定義された描画関連のメソッドが呼ばれた時。
    * <pre>
        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();
        };
      </pre>
  1. つまりBitmap.context側のメソッドを呼び出しても、Bitmap._setDirty()が実行されないので、描画が行われなかった！
      * これ初見で気づくやつおるか～？
      * なお、Bitmap.clear系メソッドは、Bitmap.context.clearRectをラップしているので、クリア処理内容に差異はない。

## 正しい書き方は？
  * Sprite.bitmap.context側のメソッドを実行する一連の処理の前に、Sprite.bitmap.clear()を実行しておく。
  <pre>
    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 ++;
    }
  </pre>

## 結果
  * 原因を突き止めたスーパーツクラーは一瞬のうちにコードを修正、無事に直線を引くプラグインを作成することができました。
    * https://twitter.com/velfare_nagata/status/1150787990119018496
  * 落ち着きを取り戻したスーパーツクラーはプロジェクトの削除作業を中断し、世界に平和が訪れましたとさ、めでたしめでたし。