LoginSignup
3
2

More than 3 years have passed since last update.

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

Posted at

概要

  • 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 );
      

なんで?

  1. Bitmapの変更内容が実際に描画されるのは、Bitmap._baseTexture.update()が実行された時。
    • 
      Bitmap.prototype.createBaseTexture = function(source){
        this._baseTexture = new PIXI.BaseTexture(source);
        …
      };
      
  2. 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;
          }
      };
      
  3. 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();
      };
      
  4. つまり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 ++;
    }
    

結果

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2