pixi.jsのSpriteでフレームアニメーションできるようにする

※pixi.jsテンプレートを公開しています。▷▷▷ https://github.com/inwan78/pixijs-game-template

pixi.jsのSpriteクラスの基本的な使い方

まずはpixi.jsのSpriteについて。

pixi.jsで画像を表示させるにはSpriteクラスを使います。

const img = new PIXI.Sprite(); 
img.texture = GameManager.instance.game.loader.resources[Resource.Image.Player].texture; 
this.addChild(img);

(※私の例では画像データはGameManager.instance.game.loader.resourcesというところから読みこんでいますがここはお使いの状況に変えてください。)

Spriteは表示したい画像をtextureに設定してやると画像が設定されます。

それをコンテナなど(上の例ではthisになってる部分)にaddChild()すると画面に表示されるようになります。

Spriteの主なプロパティについて

基本的にゲームで使う部分としては

  • x、y・・・addChildしたContainerなどの上の表示位置
  • scale・・・拡大縮小率。1が基準。xが幅、yが高さを変える
  • anchor・・・表示の際のスプライトの基準位置。(0, 0)だと画像の左上、(1, 1)で右下
  • rotation・・・回転。単位はラジアン
  • width、height・・・スプライトの幅、高さ
  • visible・・・trueで表示、falseで非表示
  • alpha・・・透明度。1で見える、0で見えなくなる

あたりが良く使うかと思います。またこれらのプロパティはほかのクラスともだいたい共通しています。

画像の表示はこれでできるんですが実はこれではゲームに使うにはちょっと困るんですよね。これは一枚の画像をそのまま表示するだけなのでちょっと使いづらいんです。

ということでenchant.jsなどで使われているスプライトを分割して表示するやり方を作ります。

画像を切り取って表示する

キャラクタ画像

一枚の画像をそのまま表示するだけでは非常に使い勝手が悪いので必要な部分を切り取って表示できるようにします。

画像を切り取るのはPIXI.Texture(“元画像テクスチャ”, “範囲”)で切り出すことができます。切り取った部分を新しいテクスチャとしてspriteに入れてやればその部分だけを表示できるようになります。

const img = new PIXI.Sprite(); 
const texture = GameManager.instance.game.loader.resources[Resource.Image.Player].texture; 
img.texture = new PIXI.Texture(texture, new PIXI.Rectangle(0, 0, 32, 32));
this.addChild(img);

PIXI.Rectangleは画像を切り取る範囲です。引数は画像内のx座標、y座標、幅、高さの順になっています。これで好きな場所から好きな大きさで切り取ることができます。

フレームアニメーションできるようにする

フレームアニメーションとはパラパラ漫画のような感じのものらしいです(言葉の意味とかあんまり分からん(^^;))。一枚の画像から切り取って順番に切り替えながら表示するやり方なんですがこれはenchant.jsと同じやり方なのでもしわからない方はこちらの解説記事を読んでいただくと何となくわかると思います。これと同じものを作っていきます。

ではenchant.js風なspriteを作っていきましょう。

用意する変数

ではまず今回使う変数はこちらになっております。

 

  • frameNumber 切り取る画像の番号
  • width 切り取る幅
  • height 切り取る高さ

例えば上の記事のように画像のサイズが横160px×縦96pxで使いたいキャラの大きさが32px×32pxだった場合5×3の15枚画像を切り取れます。そしてこれに0番から14番まで番号を付けます。変数frameNumberはその番号を指定するのに使います。

let frameNumber = 0;
const width = 32;
const height = 32;
const player = new PIXI.Sprite(); 
const texture = GameManager.instance.game.loader.resources[Resource.Image.Player].texture; 
const left = frameNumber % 5 * 32;
const top = (frameNumber / 5 | 0) * this.height;
player.texture = new PIXI.Texture(this._image, new PIXI.Rectangle(left, top, width, height));

画像は横が5枚分なのでframeNumberを5で割った余りに切り取り幅を掛けたのが切り取るx座標、割った数字(小数点以下は|0で取り除く)に高さを掛けたのがy座標になります。上の例ではleft, topと書いてますがキャラの座標と混同することがあるので私はこういう書き方にしています(^^;)

これでframeNumberの数字を変えてやれば好きな部分の画像を切り出すことができます。

ただしこれではframeNumberが15以上になった場合におかしくなります。だって画像は14番までしかないですからね。

ということで15以上にならないように

frameNumber = frameNumber % 15;

て感じの処理を入れておくと大きな数字が入った場合も問題なく動くので良いと思います。

Spriteを継承して新しいSpriteを作る

最後にこれからも使いやすいようにspriteを継承して新しいspriteを作ってしまいましょう(‘ω’)ノ

class Sprite extends PIXI.Sprite {
  private _image: any;
  private _frameNumber = 0;
  private frameRows: number;
  private frameColumns: number;
  private frameMax: number;
  constructor(width: number, height: number){
    super();
    this.width = width;
    this.height = height;
  }
  //画像データ
  get image(): any{
    return this._image;
  }
  set image(data: any) {
    this._image = data;
    this.frameColumns = this._image.width / this.width | 0;
    this.frameRows = this._image.height / this.height | 0;
    this.frameMax = this.frameRows * this.frameColumns;
    this.setTexture();
  }
  //フレームナンバー
  get frameNumber(): number{
    return this._frameNumber;
  }
  set frameNumber(frameNumber: number) {
    if(!this._image)return;//まだ画像がセットされてなければ抜ける
    this._frameNumber = frameNumber % this.frameMax;;
    this.setTexture();
  }
  //テクスチャーをセットする
  private setTexture() {
    const left = this.frameNumber % this.frameColumns * this.width;
    const top = (this.frameNumber / this.frameColumns | 0) * this.height;
    this.texture = new PIXI.Texture(this._image, new PIXI.Rectangle(left, top, this.width, this.height));
  }
}

使うときはこんな感じ。

const player = new Sprite(32, 32);
player.image = resources[Resource.Image.Player].texture;
player.frame = 10;

簡単に内容を説明するとこんな感じになってます。

image

get、setというものを使っています。あんま詳しくわかんないんですけど=で代入したときに関数みたいに処理してくれる便利なやつらしいです(^^;)なのでsetでテクスチャーを入れたときに画像のサイズと切り取るサイズから縦横の切り取れる数と最大フレーム数を出してます。で、setTextureで切り取った画像をセットしてます。

frameNumber

これもsetを使って数字が入れられたときに最大値のチェックをしてます。

setTexture()

ここで画像を切り取って切り取った画像をスプライトのテクスチャーにセットしています。

出来たけど欠点がある

というわけでenchant.js風のSpriteフレームアニメーションができるようになりました。

ただ、このやり方実はあんまり良くないっぽいんですよ。というのも実はこのやり方だと画像を移動させたりするときにノイズが入るんですよね。ノイズというのはちらつきというかなんか余計なものが入るんですよ。

で、その問題を解決してくれるやり方(たぶんね。ほとんど使ったことない(;^ω^))があるんですがそれがすごい面倒なんですよ(-_-;)参考にさせていただいたsmith氏の本も当然こっちのやり方を使ってるんですがいかんせんめんどい(2回目)。

なにがめんどうかというのを説明するのも面倒なのでこちらの記事をどうぞ ▷ https://ryo620.org/post/pixijs-game-01/

画像を切ってまたくっつけてjsonファイル作って。。ってそんなのイヤすぎる!!(;´Д`)

というわけで私はenchant.jsスタイルでやってます。

一応このノイズの入る欠点は小数点以下を切り捨てることでも無くすことができます。それについてはこっちに書いてます。

ただ小数点以下を消しても拡大縮小・回転するとやっぱりノイズが入ります(-_-;)

ひょっとしたら画像の作り方を工夫してサイズいっぱいに作らず端を1ドット空けるとかするとでなくなるかもしれないです(試してはいない)。ちらつく原因は隣の画像の端の部分が入ってるっぽいです。

あと私は低解像度でゲーム作ってるから目立つだけで解像度をあげれば分からないのかもしれないです(これも試していない)。

ちなみに今回のスプライトの方法で作ったゲームがこちらです。このやり方でも十分良いゲームが作れます(自画自賛)。

同カテゴリー記事

記事の感想・コメント

※コメントはまだありません※

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

最近作ったゲーム