Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

minimo
趣味でphina.jsとenchant.jsを使ってゲーム作ってます。最近はphina.jsがメイン。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away