JavaScriptでゲーム作ろう

この記事では初めてPixiJSを使うときに知っておくと役に立ちそうな基本的な使い方をさっくり解説しています。

画像のロードやSpriteの使い方、タッチ入力の方法、pixi-sound.jsでの音の再生のやり方など簡単に説明しています。

 

PixiJSは2Dの描画に特化したJavaScriptのライブラリです。

WebGLで画像の描画処理が高速に行えるようになっています。

 

PixiJSを使えばサイトにかっこいい演出を入れたり、かっこいいWEBアプリ作ったり、かっこいいゲーム作ったりできます(語彙力。。)。

 

※PixiJSについては他にも記事があります。タグでまとめているのでそちらもご覧ください。

▷▷ PixiJSタグページ

 

PixiJSの開発環境について

PixiJSの開発環境にはサーバーが必要になります。

必要と言ってもサーバーの役割をしてくれるものがあればOKです。

 

便利なことにテキストエディターのVisual Studio Codeにサーバーの拡張機能があるのでこれを使いましょう。

・Visual Studio Code

 

・サーバーの拡張機能

 

インストールすると画面右下端あたりに下の画像のようなものが表示されます。

これをクリックするとブラウザが開いてプログラムが実行されます。

 

WEBエディターあります

手っ取り早く触ってみたい人はWEB上で使えるエディターが用意されています。

右上のSETTINGSボタンからバージョンの変更もできます。

Pixi Playground

 

PixiJSのダウンロード

入手先はこちら ▷ pixi.js ▷ pixi-sound.js

この記事で使っているpixi.jsはバージョン5.3.7、pixi-soundはバージョン4.0.4です。

※違うバージョンでは仕様が異なっていて記事の説明と違うところがある場合があります。古いバージョンはスクロールすると出てきます。

 

index.html

index.htmlはこんな感じで。プログラムはmain.jsに書いていきます。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <script src="pixi.min.js"></script>
    <script src="pixi-sound.js"></script>
    <script src="main.js"></script>
  </head>
  <body>
  </body>
</html>

 

PixiJSの表示画面を作る

まずpixi.jsで表示画面を作ります。

 

window.onload = () => {
  const app = new PIXI.Application({ 
    width: 600, 
    height: 600,                       
    backgroundColor: 0x222222,
    resolution: 1,
    autoDensity: true
  });
  document.body.appendChild(app.view);
}

画面解像度と背景色、あと解像度系の謎プロパティを指定して画面を作ります。他にもプロパティがあるようですがとりあえずこれだけあればOK。

画面ができると同時にapp.stageという場所が作られていて画面に表示したいものはこのapp.stageに入れていきます。

app.stage.addChild(表示したいもの)とすると画面に表示されます。

 

PixiJSの文字表示の仕方

とりあえずお約束のHello World!。

const style = new PIXI.TextStyle({
  fontFamily: 'sans-serif',
  fontSize: 32,
  fill: 0xffffff,
  fontWeight: 'bold',
  stroke: 0xFF0000,//縁取りの色
  strokeThickness: 4,//縁取りの太さ
});
const p = new PIXI.Text("Hello World!", style);
p.anchor.set(0.5, 0.5);
p.position.set(300, 300);
app.stage.addChild(p);

PIXI.TextStyleで色や大きさなどを指定します。

かなりたくさんプロパティがあるので詳しくはドキュメントを見てください ▷ PIXI.TextStyle

 

PixiJSの図形の描き方

pixi.jsで描いた図形

pixi.jsで図形を描く場合はPIXI.Graphicsを使います。

const g = new PIXI.Graphics();
app.stage.addChild(g);

Graphicsは一つにいくつでも図形を描くことができます。

 

以下では基本的な図形の描き方をいくつか説明します。

これ以外にも図形の描き方があるので詳しく知りたい方はドキュメントをお読みください

PIXI.Graphics

 

線を引く

g.lineStyle(4, 0xffffff, 1);
g.moveTo(10, 10);//始点
g.lineTo(100, 100);//終点

線を引くのはlineStyle()です。

第1引数は線の太さ、第2引数は色です。

第3引数にはalpha(透明度)が指定できます。

 

第4引数はalignmentになっています。デフォルトでは0.5になっています。

直線の場合はこれで良いんですが、このlineStyleは四角や円などの図形にも使うことができるようになっています。

その場合に基準となる線の内側(0)または外側(1)に線を引くように設定できます。

基本的に図形の縁取りに使う場合は0にして使うのが良いと思います(外側にあると幅が大きくなるので)。

 

lineStyleはこの後も図形を描いたりする場合、その後の図形でも自動で適用されます。以降に適用したくない場合は

g.lineStyle();

引数無しで使うと解除されます。

 

四角を描く

g.drawRect(120, 12, 64, 96);

四角形を描くときはdrawRect()使います。

引数は(x座標、y座標、幅、高さ)です。

ただこれだけでは何も表示されません。

 

塗潰す場合は(上の図の赤い四角)

g.beginFill(0xff0000);
g.drawRect(120, 12, 64, 96);
g.endFill();

beginFill()とendFill()で挟みます。

beginFillの引数で塗潰す色を指定します。

 

縁取りしたい場合は(上の図の黄色い枠の四角)

g.lineStyle(4, 0xffff00, 1, 0);
g.drawRect(200, 12, 64, 96);

lineStyle()を使います。

 

塗潰して縁取りすることもできます(上の図の一番右の四角)。

g.lineStyle(4, 0x00ff00, 1, 0);
g.beginFill(0xff00ff);
g.drawRect(280, 12, 64, 96);
g.endFill();

 

円を描く

g.drawCircle(48, 160, 32);

drawCircle()で円を描きます。

塗潰しはbeginFill()とendFill()、縁取りはlineStyle()を使います。

 

多角形を描く

const data = [160, 130, 200, 200, 120, 180];
g.drawPolygon(data);

多角形はdrawPolygon()を使います。

引数に各頂点の座標が入った配列を渡します。座標はx、yの座標を交互に入れます。

塗潰しはbeginFill()とendFill()、縁取りはlineStyle()を使います。

 

描いた図形を消す

描いた図形を消す場合は

g.clear();

を使います。

図形を変形させたり色を変えたい場合は一度消してもう一度書き直します。

 

PixiJSのファイルロード方法

画像などを使う場合はファイルを読み込む必要があります。pixi.jsでは読み込みの方法がいくつかあります。

 

必要な時に読み込む

一枚の画像を読み込んで表示したいとき。

const texture = new PIXI.Texture.from('player.png');
const player = new PIXI.Sprite(texture);
app.stage.addChild(player);

 

まとめてロード1

たくさん読みこむ必要がある場合はまとめてロードできます。

PIXI.Loader.shared
  .add('player.png')
  .add('item.png')
  .load(() => {
    const texture = PIXI.Loader.shared.resources['player.png'].texture;
    const player = new PIXI.Sprite(texture);
    app.stage.addChild(player);
  });

読み込みが終わるとload()が実行されます。

使うときはPIXI.Loader.shared.resourcesにデータが入っているのでファイル名で指定して使いまます。

 

まとめてロード2(名前を付ける)

こちらは読みこむデータに名前を付けるやり方です。

PIXI.Loader.shared
  .add('player', 'player.png')
  .add('item', 'item.png')
  .load(() => {
    const texture = PIXI.Loader.shared.resources['player'].texture;
    const player = new PIXI.Sprite(texture);
    app.stage.addChild(player);
  });

 

まとめてロード3(配列で渡す)

まとめてロード1の変化版。配列で渡すやり方です。

const assets = [
  'player.png',
  'item.png'
];
PIXI.Loader.shared.add(assets).load(() => {
  const texture = PIXI.Loader.shared.resources['player.png'].texture;
  const player = new PIXI.Sprite(texture);
  app.stage.addChild(player);
});

 

まとめてロード4(名前を付けて配列で渡す)

名前を付けるやり方の便利な方法。

const assets = {
  player: 'player.png',
  item: 'item.png'
};
for (let name in assets) {
  PIXI.Loader.shared.add(name, assets[name]);
}
PIXI.Loader.shared.load(() => {
  const texture = PIXI.Loader.shared.resources['player'].texture;
  const player = new PIXI.Sprite(texture);
  app.stage.addChild(player);
});

 

PixiJSのSpriteクラスについて

Sprite

画像ファイルなどを表示するときに使うクラスです。

読みこんだ画像データはPIXI.Loader.shared.resourcesにあります。画像データを使う場合は[○○].textureになります。

 

画像をそのまま表示

ロードした画像をそのまま表示します。

const texture = PIXI.Loader.shared.resources['player.png'].texture;
const player = new PIXI.Sprite(texture);
app.stage.addChild(player);

 

画像を好きな大きさに切り取って表示

画像を好きな場所から好きな大きさだけ切り取って表示できます。

const texture = PIXI.Loader.shared.resources['player.png'].texture;
const frame = new PIXI.Texture(texture, new PIXI.Rectangle(0, 0, 32, 32));
const player = new PIXI.Sprite(frame);
app.stage.addChild(player);

PIXI.Rectangle(x, y, 幅, 高さ)で指定します。

PIXI.Texture(切り取りたいテクスチャー, PIXI.Rectangle(x, y, 幅, 高さ))で新しいテクスチャーを作ります。

 

JSONファイルを使って表示部分を指定する

jsonファイルに必要な情報を用意してそれに従って画像を切り取って表示します。

{
  "frames": {
    "player-0": {
      "frame": {
        "x": 0,
        "y": 0,
        "w": 32,
        "h": 32
      }
    },
    "player-1": {
      "frame": {
        "x": 32,
        "y": 0,
        "w": 32,
        "h": 32
      }
    }
  },
  "meta": {
    "image": "player.png",
    "format": "RGBA8888",
    "size": {
        "w": 256,
        "h": 64
    },
    "scale": 1
  }
}

上のような感じに位置と大きさを指定したjsonファイルを作成します。

 

const assets = [
    'player.json',
    'item.png'
  ];
  PIXI.Loader.shared.add(assets).load(() => {
    onload();//読み込み完了でonload()実行
  }); 
  const onload = ()=>{
    const texture = PIXI.Loader.shared.resources['player.json'].textures['player-1'];
    const player = new PIXI.Sprite(texture);
    app.stage.addChild(player);
  }

この場合はjsonファイル内のデータを見て画像ファイルを自動で読み込みこんでいるようなので画像ファイルを読み込む必要はありません。

 

PIXI.Loader.shared.resources['player.json'].textures['player-1']

のようにtextures[表示したい部分の名前]でjsonで指定した部分の画像が表示されます。

jsonファイルはお絵かきソフトの機能(asepriteにあるらしい?)をつかったり、作成ツール(texturepacker)などを使って作ってください(上の例のように書けば手書きでも一応できます)。

 

さらにSpriteの私なりの独自の使い方をこちらの記事に書いてあります。jasonファイルを使わずに簡単にフレームアニメーションする方法です。

 

Spriteで主に使われるプロパティ

Sprite、Containerなどのプロパティはだいたい共通しています。とりあえず良く使いそうなものだけ。

  • x, y ・・addChildした先との相対座標。position.set(10, 10)という書き方もある。
  • scalse・・拡大率。x方向・y方向がある。マイナスは反転
  • anchor(Spriteのみ)・・・表示の際のスプライトの基準位置。(0, 0)だと画像の左上、(1, 1)で右下
  • pivot・・anchorと同じ。ただしこちらは座標で指定。
  • rotation・・・回転の角度。単位はラジアン
  • width、height・・・幅、高さ
  • visible・・・trueで表示、falseで非表示
  • alpha・・・透明度。1で見える、0で見えなくなる

 

説明が抜けてましたがPIXI.Containerは入れ物みたいなものでこれ自体は見えませんが複数のSpriteをひとまとめにしたいときなどに使います。

SpriteなどをContainerに配置してContainerを移動させるとContainer内での配置を保ったまま移動させることができます。

SpriteはContainerから継承して作られているようです。

 

PixiJSの画面更新処理について

Spriteアニメーション

アニメーションさせたり、ゲーム作ったりするときに必要な自動更新処理を行ってくれるようにします。

app.ticker.add((delta) => {
  //ここに処理を書く
});

または

app.ticker.add(update);
function update(delta){
  //ここに処理を書く
}

60FPS(1秒間に60回)で処理を実行してくれます。

 

PixiJSのタッチイベントについて

タッチイベント

pixi.jsでのタッチイベントの使い方は

abcde.on('pointerup', () => {
  //処理 
});

のようにonメソッドでイベント名を指定してコールバック処理を書けばそのイベントが発生したときに処理を行ってくれます。

 

interactiveをtrueにする

タッチイベントを使うには対象オブジェクトのinteractiveプロパティをtrueにする必要があります。

これを設定し忘れるとタッチイベントを作っても動いてくれません。

「あれぇ?なんで動かんのや?」ってときはだいたいこれを忘れています。

 

pointerでつくればPCもスマホもいける

pixi.jsではかなりたくさんのタッチ/マウスに関するイベントがあります。

参考 ▷▷▷ PIXI.interaction.InteractionManager

mousedownやtouchstartなどマウス・スマホ用にイベントがありますが、基本的にpointerで指定すればPCでもスマホでも動きます。

pointerdown、pointermove、pointerupを基本的に使っていけばOKです。

ただしpointermoveはPCの場合pointerdownしてようがしてなかろうが関係なしにマウスを動かせば発火するので押しているだけを判別して動かすには押している状態を管理するフラグが必要です。

 

スマホではpointeroutが効かない

対象のオブジェクトからポインターが外れたときに発生するpointeroutが残念ながらスマホでは使えないようです。

そして残念ながらtouchoutというものもないようです。

なのでスマホの場合はtouchmoveで動かした位置が対象のオブジェクトの大きさより外側になっているかをチェックして外側に出ていたら処理を行うようにする必要があります。

他にもpointerイベントでスマホでは動かないものがあるようです。

 

タッチの座標を取得

タッチの座標などの情報は下記のような感じに取得できます。

this.on('pointerdown', (event) => {
  const pos = event.data.getLocalPosition(event.currentTarget);
  ball.postion.set(pos.x, pos.y);
});

getLocalPositionは引数に指定した要素上の座標になります。だいたいはタッチしたもの(event.currentTarget)か、もしくはその親(event.currentTarget.parent)の座標を取得することになると思います。

 

パブリングに注意

javascriptのタッチなどのイベントにはバブリングという機能があります。

これは例えばオブジェクトをタッチしたときにそのタッチで起こるイベントがそのタッチしたオブジェクトだけでなくそのオブジェクトの親オブジェクトでもイベントが起こる現象です。

もうちょっと具体的な例としては、例えば画面をタッチしたらキャラがジャンプするようにしていて、その画面の上にポーズボタンを作っていた場合、このポーズボタンを押すとキャラもジャンプしてしまう現象です。ポーズだけしてくれればいいのにキャラがジャンプしてしまう現象が起こります。

これの対処法は

pauseBtn.on("pointerdown", (e) => {
  e.stopPropagation();//eventの伝搬を止める
  //ボタンの処理
});

e.stopPropagation()を書いておくと親への伝搬を止めることができます。

 

Containerのタッチイベント

画面をタッチして動かす場合にPixi.ContainerにイベントをつけたいところですがContainerだけではタッチイベントは機能しないみたいです。

Containerにタッチイベントを仕込みたい場合はSpriteを画面の幅・高さに作ってaddChildしてやるとSpriteからのバブリングで使うことができます(画像は無しでもOK)。

 

マルチタッチについて

pixi.jsのマルチタッチについてはこっちの記事にまとめてあります。

androidでしかうまくいかないようですが気になる方はどうぞ(・ω・)ノ

 

PIXI-SOUNDで音の再生

pixi-soundもロードは上の例と同じようにできます。

読みこんだデータは同様にPIXI.Loader.shared.resourcesにあって、こっちは後ろに.soundをつけて扱います。.soundの後にプロパティかメソッドをつけて扱います([○○].sound.play()みたいな感じ)。

もしくはPIXI.soundという場所にもあるのでPIXI.sound.play(ファイル名)でも扱えます。

  • loop・・trueにするとループします
  • volume・・音量
  • play()・・再生
  • pause()・・ポーズ
  • resume()・・再開
  • stop()・・停止

より詳しく知りたい方は公式ページをどうぞ ▷ PixiSound Basics