Spriteの元画像をテクスチャアトラスっぽく使ってみる

  • 4
    Like
  • 0
    Comment

この記事は、phina.js Advent Calendar 2016 12/3の記事です。
昨日 → phina.jsでbulletml.jsを使う! by daishi_hmrさん
明日 → phina+websocketの作る例 by tamochu3141さん

はじめに

phina.jsSpriteを使う時は、以下の様な画像を使い、frameIndexを指定して表示していると思います。

tomapiyo.png

1枚につき1キャラクタなら良いのですが、少し凝ったゲームを作るとキャラクタ数と同時に画像が増えて管理が煩雑になってきます。
理想としては、1枚(または複数)の画像に複数のキャラクタを乗せて、一部を切り取って表示させたい所です。
しかし、32 x 3248 x 48みたいに異なるサイズのスプライト画像をまとめた場合、きちんとsetFrameIndexで指定が出来る様に考えて画像を作らなければなりません。

調べた所、世の中には、テクスチャアトラスという物があって、テクスチャの一部を任意に指定して、スプライトを表示させる機能があるというではありませんか。
phina.jsでも似た事が出来ないかなと考えて、こんなのを作ってみました。

サンプル(runstant)

コードの説明

何をしているのかコードを見てみましょう。

最初に表示しているのは通常の使い方です。
読み込んだテクスチャをframeIndexを指定して、表示範囲もUpdate内で制御しています。

ふつう
        //普通の使い方
        var tomapiko1 = phina.display.Sprite(texture, 64, 64);
        tomapiko1.addChildTo(that);
        tomapiko1.setPosition(400, 320);
        tomapiko1.setFrameIndex(15);
        tomapiko1.update = function(e) {
          this.frameIndex++;
          if (this.frameIndex === 0) this.frameIndex = 15;
        }

次に表示しているのはsetFrameTrimmingという関数を使用して、テクスチャの範囲を設定。
設定された範囲内でframeIndexを指定してスプライトを表示しています。

ちょっと変えた
        //画像の使用範囲を指定する使い方
        var tomapiko2 = phina.display.Sprite(texture, 64, 64);
        tomapiko2.addChildTo(that);
        tomapiko2.setPosition(400, 450);

        //16~18コマの位置 x:192 y:128 を始点として、
        //横:192 縦:64をスプライト用画像とする
        tomapiko2.setFrameTrimming(192, 128, 192, 64);
        tomapiko2.setFrameIndex(0)

        tomapiko2.update = function(e) {
          this.frameIndex++;
        }

setFrameTrimmingは通常のphina.jsのSpriteにはありません。
以下の様に用意しています。

setFrameTrimming
phina.display.Sprite.prototype.setFrameTrimming = function(x, y, width, height) {
  this._frameTrimX = x || 0;
  this._frameTrimY = y || 0;
  this._frameTrimWidth = width || this.image.domElement.width - this._frameTrimX;
  this._frameTrimHeight = height || this.image.domElement.height - this._frameTrimY;
  return this;
}

phina.display.Sprite.prototype.setFrameIndex = function(index, width, height) {
  var sx = this._frameTrimX || 0;
  var sy = this._frameTrimY || 0;
  var sw = this._frameTrimWidth  || (this.image.domElement.width-sx);
  var sh = this._frameTrimHeight || (this.image.domElement.height-sy);

  var tw  = width || this.width;      // tw
  var th  = height || this.height;    // th
  var row = ~~(sw / tw);
  var col = ~~(sh / th);
  var maxIndex = row*col;
  index = index%maxIndex;

  var x   = index%row;
  var y   = ~~(index/row);
  this.srcRect.x = sx+x*tw;
  this.srcRect.y = sy+y*th;
  this.srcRect.width  = tw;
  this.srcRect.height = th;

  this._frameIndex = index;

  return this;
}

引数に始点の座標(x,y)、画像の縦横幅(width, height)を指定して使用します。

setFrameTrimming内で_frameTrimX _frameTrimY _frameTrimWidth _frameTrimHeight等の変数で使用して、スプライト表示範囲を指定しています。
それに伴い、setFrameIndexも変更を行っています。

これを使えば、大小異なる大きさの画像を一枚にまとめる事が出来るようになり、画像読み込みの時間も短縮できるようになります。

おしまい

コードはサンプルからコピペしてそのまま動作しますのでご自由にお使いください。
個人的によく使う機能なので、そのうちプルリクするかもしれません…しれません…

最初の構想では、JSONでテクスチャアトラスを作成するサービスを作って、それに対応するクラスを作成するつもりだったのですが、時間が無くて中途半端な機能になってしまいました。
いずれ完成させて、phina.jsから利用できるようにしたいと思ってます。
その時はまた記事書くのでよろしくお願いしますー。