4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

phina.jsAdvent Calendar 2016

Day 19

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

Last updated at Posted at 2016-12-20

この記事は、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]

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?