phina.jsでスプライトをpixi.jsを利用して描画する

  • 3
    いいね
  • 0
    コメント

この記事は、phina.js Advent Calendar 2016 の記事です。
昨日→phina.jsとWebGLでいろいろ by daishi_hmrさん
明日→phina.jsのShapeを複数まとめて画像として使う by simiraaaaさん

pentamaniaさんの「phina.jsとpixi.jsを連携させる」を参考にしております。

はじめに

phina.jsのスプライトは十分速いのですが、弾を沢山だしたり、エフェクトばりばりーなSTGとか作ってると、若干処理速度に不安が出てきます。(特にiPhoneとか)
何か方法は…と考えてたところ、pentamaniaさんの記事を発見し、パク参考にさせて頂くついでに使用感をphina.js側に寄せる方向で実装を行ってみました。

主な説明は、pentamaniaさんの記事を参照して頂くとして(手抜き)、こちらの記事では、「方法1:canvas2DのdrawImageを利用」を採用しております。

参考記事と同様に、pixiオブジェクトを描画するためのレイヤークラス、phina.display.PixiLayerを用意します。
さらにphina.js側との使用感を統一する為に、PixiLayer用にスプライト操作を行う、phina.pixi.Spriteを用意しています。

phina.display.PixiLayer
phina.define('phina.display.PixiLayer', {
  superClass: 'phina.display.Layer',

  stage: null,
  renderer: null,

  /** 子供を 自分のCanvasRenderer で描画するか */
  renderChildBySelf: true,

  init: function(options) {
    this.superInit();
    options = (options || {}).$safe({
      width: 640,
      height: 640
    });

    this.stage = new PIXI.Container();
    this.renderer = PIXI.autoDetectRenderer(options.width, options.height, {transparent: true});

    this.on('enterframe', function() {
      this.renderer.render(this.stage);
    });
  },

  draw: function(canvas) {
    var domElement = this.renderer.view;
    canvas.context.drawImage(domElement, 0, 0, domElement.width, domElement.height);
  },

  addChild: function(child){
    if (child.pixiObject) {
      this.stage.addChild(child.pixiObject);
    }
    return phina.display.Layer.prototype.addChild.apply(this, arguments);
  },

  removeChild: function(child){
    if (child.pixiObject) {
      this.stage.removeChild(child.pixiObject);
    }
    return phina.display.Layer.prototype.removeChild.apply(this, arguments);
  }
});
phina.pixi.Sprite

phina.pixi = phina.pixi || {};

phina.define('phina.pixi.Sprite', {
  superClass: 'phina.display.Sprite',

  pixiObject: null,

  init: function(image, width, height) {
    this.superInit(image, width, height);

    this.pixiObject = new PIXI.Sprite.fromImage(this.image.src);
    this.pixiObject.anchor.set(0.5, 0.5);

    this.pixiObject.texture.baseTexture.width = this.image.domElement.width;
    this.pixiObject.texture.baseTexture.height = this.image.domElement.height;

    this.on('enterframe', function(e) {
      // Elementと必要な情報を同期
      this.pixiObject.position.set(this.x, this.y);
      this.pixiObject.rotation = this.rotation.toRadian();
      this.pixiObject.scale.set(this.scaleX, this.scaleY);
      this.pixiObject.anchor.set(this.originX, this.originY);
      this.pixiObject.alpha = this.alpha;
    });
  },

  setFrameIndex: function(index, width, height) {
    phina.display.Sprite.prototype.setFrameIndex.apply(this, arguments);
    this.pixiObject.texture.frame = new PIXI.Rectangle(this.srcRect.x, this.srcRect.y, this.srcRect.width, this.srcRect.height);
    return this;
  },

  setImage: function(newImage, width, height) {
    this._image = newImage;
    this.pixiObject = new PIXI.Sprite.fromImage(newImage.src);
    this.pixiObject.texture.baseTexture.width = this.image.domElement.width;
    this.pixiObject.texture.baseTexture.height = this.image.domElement.height;
    this.pixiObject.texture.frame = new PIXI.Rectangle(this.srcRect.x, this.srcRect.y, this.srcRect.width, this.srcRect.height);
    return this;
  },

  setPosition: function(x, y) {
    this.pixiObject.position.set(x, y);
    return phina.display.Sprite.prototype.setPosition.apply(this, arguments);
  },

  setOrigin: function(x, y) {
    this.pixiObject.anchor.set(x, y);
    return phina.display.Sprite.prototype.setOrigin.apply(this, arguments);
  },

  setScale: function(x, y) {
    y = y || x;
    this.pixiObject.scale.set(x, y);
    return phina.display.Sprite.prototype.setOrigin.apply(this, arguments);
  },
});

コードはちょっと長いですが、やっている事は至極単純です。
phina.pixi.Spriteはメンバとして、pixiのオブジェクトを保持しており、phina.display.Layerにはphina.pixi.Spriteを追加された際に、pixiのオブジェクトを自身のレンダラに追加する処理を加えています。
また、phina.pixi.SpriteにSpriteに対する操作をphina.display.Spriteと同様に行えるようにして、従来のSpriteと同様に違和感無く使用出来るようにしてあります(とりあえずサンプルに必要な機能だけですが)

パフォーマンスは?

無改造Spriteサンプル
pixi.jsのSpriteで表示するサンプル
参考記事と同様の結果となりますが、ほぼ4~5倍程度のSprite量でも60FPSを保てる様になりました。

おしまい

ほぼ人様の記事のただ乗り見たいな感じで非常に恐縮ですが、大量に高速でスプライトを表示したい場合、こんな方法もあると言う事で参考にして頂ければと思います。

サンプルのソースはこちら[github]

この投稿は phina.js Advent Calendar 201619日目の記事です。