Making your first Phaser gameをやってみた

https://www.catch.jp:443/wiki/index.php?phaser%2Ftutorial_01

Programing > Phaser > Tutorial 01

高速でサクサク開発できる、HTML5/Javascript向け2Dゲームエンジン「Phaser」(フェイザー)。

これは、そのチュートリアル「Making your first Phaser game:はじめてのPhaserゲームを作ろう」をやってみたメモ。

というか、ほとんど日本語訳なんだけど、かなり超訳っぽくなっている。

ちなみに、こんな感じのゲームを作る。

example_001a.png

ゲームの完成版はココ(矢印キーで操作)

http://www.catch.jp/program/phaser/001/making_your_first_Phaser_game.html

チュートリアルのオリジナル版は、ココ(Phaserの開発元であるPhotom Stormのブログ)に載っていて、Phaserにも収録されている。

http://www.photonstorm.com/phaser/tutorial-making-your-first-phaser-game


利用条件

Japanese Tutorial

Copyright 2014 Yutaka Kachi released under MIT license.

間違いなどあるかも知れません。

もしも見つけたら、Twitter @y_catchへ連絡いただけると助かります。

Original

Copyright 2006 - 2014 Photon Storm Ltd. All rights reserved.

Copyright (c) 2014 Richard Davey, Photon Storm Ltd.

Released under MIT License. see http://opensource.org/licenses/mit-license.php

このチュートリアルを書いたのは、 Alvin Ourrad と Richard Davey。

MITライセンスなPhaser配布セットに含まれている。


目次

Phaserってナニ?

Phaserは、HTML5対応でクロスプラットフォームのWebゲーム開発フレームワーク。デスクトップとモバイルに対応、だそうな。

じつは、2DグラフィックライブラリPixi.jsをコアにして、サウンドとか衝突判定とか物理演算を追加した2Dゲーム開発フレームワーク。

PCとモバイルのマルチプラットフォーム対応で、オープンソース(MITライセンス)。

必要なもの

  1. チュートリアルのソースと素材をダウンロードする
  2. とってもとっても基本的なJavascriptの知識が必要
  3. あと、Phaserで開発する環境を整えておくこと

Part.1:骨組み

チュートリアルのサンプルファイルのうち、part1.htmlをエディタで開くと、こんなふうになっている。これがPhaserの基本的な骨組み。

<!doctype html> 
<html lang="en"> 
<head> 
	<meta charset="UTF-8" />
	<title>Phaser - Making your first game, part 1</title>
	<script type="text/javascript" src="js/phaser.min.js"></script>
   <style type="text/css">
       body {
           margin: 0;
       }
   </style>
</head>
<body>

<script type="text/javascript">

var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update });

function preload() {
}

function create() {
}

function update() {
}

</script>

</body>
</html>

ただし、ただの骨組みなので、このpart1.htmlをブラウザで開いても残念ながら、なにも表示されない。

var game = new Phaser.Game ・・・

では、Javascriptの1行目(以下のところ)を見ていこう。

var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update });

この行では、Phaserのインスタンスを生成して、game変数に割り当てることで、Phaserに生命をあたえる。べつに"game"という変数名でなくてもいいけど、こんなふうにするのが一般的なやり方になっている。ほかのサンプルでも、こんなふうにしてある。

幅と高さ

最初の2つの設定値は、幅(width = 800)と高さ(height = 600)で、Phaserが生成するCanvasタグのサイズを指定している。ここでは、800×600ピクセルになっている。これは、自分の好きなサイズを指定できるけど、実行するデバイスの解像度の中に収まっていなければならない。

CANVASかWebGLの選択

3番目の設定値は、「Phaser.CANVAS」か「Phaser.WEBGL」か「Phaser.AUTO」のどれか。知っているとおり、CANVASもWebGLもJavascript/HTML5でグラフィックを描く方法で、自分の使いたいものを指定すればいい。オススメは、「Phaser.AUTO」。これは、まずWebGLで描いてみて、ブラウザがサポートしていなければCANVASで描いてくれる。

DOMへの挿入

4番目の設定値は、空っぽの文字列になっているけど、Phaserが生成したCANVASのDOM-IDを指定できる。今回は、空っぽのままなので、body要素にCANVASが挿入される。

関数(ステータス)の割り当て

最後の設定値で、Phaserで必須となる関数をステータスとしてオブジェクトに割り当てている。使い方は、ここに説明してある

Note: このオブジェクトにステータスを直接記述する方法はかならずしも最善じゃないけど、ここでは、すばやくプロトタイプを作るために、この方法を採用している。

で、ここで割り当てた関数( preload()、create()、update())の中身を、このあと記述していく。

訳注:これはゲームの進行ごとにステータスを割り当てる。ステートマシンとか状態遷移と呼ばれる技法になっている

Part.2:ゲーム素材のロード

では、ゲームで使う素材を指定してロードする。

preload関数の中で、game.loadメソッドを呼ぶと、ゲーム内に素材が読み込まれる。

こんな感じで、今まで空っぽだったpreload関数の中に記述してやる。

function preload() {
   game.load.image('sky', 'assets/sky.png');
   game.load.image('ground', 'assets/platform.png');
   game.load.image('star', 'assets/star.png');
   game.load.spritesheet('dude', 'assets/dude.png', 32, 48);
}

Phaserでこんなふうに記述しておくと、実行時に自動的に素材を読み込んでくれる。

実際のコードは、チュートリアルのpart2.htmlを参照。でも、まだ何も表示されない。

アセット

ちなみに、このような素材をAssets(アセット)と呼んでいる。

ここでは、3つの画像と1つのスプライトシートを読み込んでいる。

素材は、チュートリアルのassetsフォルダにあるので、ファイルパスとファイル名で記述してやる。

アセットキー

もうひとつ注目して欲しいのが、game.loadメソッドの最初の引数。'sky' 'ground' 'star' 'dude'という文字列が指定してあるけど、これがアセットキー。ゲームに素材を呼び出すには、このアセットキーをコードから指定する。アセットキーは、Javascriptの文字列なら何でも自由に使える。

Part.3:スプライトの表示

スプライトのことは知っているかな?

スプライトとは、2Dゲームでキャラクターや背景のグラフィックを表示するための機能。ゲーム内のキャラクターは、小さな画像(スプライト)として表示される。これをすばやく切り替えることで、アニメーションさせる。

では、そのスプライトを表示させてみよう。まずは、星を1個表示させる。

create関数に、次のコードを記述する。

function create() {
     game.add.sprite(0, 0, 'star');
}

で、そのhtmlファイルをいったん保存して、ブラウザで表示させると、次のように、左上に星が表示される。

example_002.png

実際のコードは、チュートリアルのpart3.htmlを参照。うーむ、やっと出た!

表示順について

今のところ、背景は黒色になっている。

スプライトが表示される順番は、コードを記述した順番になる。

だから、星の背景を表示したい場合は、星の前に背景のスプライトのコードを記述する。

game.add.sprite メソッド

ゲームの裏側で、game.add.spriteメソッドは、新しいPhaser.Spriteオブジェクトを生成して、スプライトを“game world”に追加する。この“game world”に、ゲームのオブジェクトが存在している。

ゲームの座標

この“game world”は、じつはサイズが固定されていないし、無限に広がっている。座標の原点は「0,0」になっていて、一見すると左上のコーナーに割り当てられている。だけど、組み込みのCameraコマンドで自由に調整できる。

Part.4:“game world”を構築する

では、“game world”を構築してみよう。

先のCreate関数のコードの変わりに、次のように記述する。

var platforms;

function create() {

   //  物理演算エンジンとして、Arcade Physicsシステムをオンにする
   game.physics.startSystem(Phaser.Physics.ARCADE);

   //  シンプルな背景
   game.add.sprite(0, 0, 'sky');

   //  プラットフォームグループの生成。このグループは、地面(ground)と2つの張り出し(ledge)からできている
   platforms = game.add.group();

   //  プラットフォームグループのオブジェクトは、すべて物理演算をオンにする
   platforms.enableBody = true;

   // ここで、platformsグループに地面(ground)を追加する
   var ground = platforms.create(0, game.world.height - 64, 'ground');

   //  地面のサイズをゲームの幅にフィットさせる (スプライトのオリジナルサイズは、400x32)
   ground.scale.setTo(2, 2);

   //  地面を固定する
   ground.body.immovable = true;

   //  同様に、platformsグループに張り出し(ledge)を追加する
   var ledge = platforms.create(400, 400, 'ground');

   ledge.body.immovable = true;

   ledge = platforms.create(-150, 250, 'ground');

   ledge.body.immovable = true;
   
}

実際のコードは、チュートリアルのpart4.htmlを参照。動かないけど、物理演算エンジンが設定されている。

最初のパーツは、星のスプライトと同じだけど、アセットキーが"sky"になっていて、これで背景を指定する。

この背景には、800×600サイズのPNGファイルを読み込んでいる。

example_003.png

グループ機能について

Phaserのグループ機能は、ほんとうに強力だ。グループ機能は、その名前が示すとおり、よく似たオブジェクトをひとつにまとめて、単一の部品のようにコントロールする。さらに、グループ間で衝突判定もサポートしている。このサンプルゲームでは、先ほどのコードで作ったplatformグループと、もうひとつのグループとの間で衝突を判定する。

次のコードは、先ほどplatformグループを定義したときのコードだ。

platforms = game.add.group();

ここでは、platformというローカル変数に、グループオブジェクトを代入している。このグループオブジェクトに、ゲーム画面に配置する要素を追加していく。最初に、地面(ground)を追加した。この地面オブジェクトは、ゲーム画面の最下部にあって、"ground"イメージが表示される。地面オブジェクトの幅は、ゲーム画面に合うよう拡大縮小している。それから、"immovable"プロパティを"true"にする。こうしておくと、プレーヤーのキャラクタが地面に衝突しても、地面オブジェクトは固定されている(さらに詳しい説明は、このあと)。

Part.5:プレイヤーを用意する

新しいローカル変数"player"を用意して、以下のコードをcreate関数に追加する。

   // player変数を用意して、 'dude'スプライトを設定する
   player = game.add.sprite(32, game.world.height - 150, 'dude');

   //  物理演算をオンにする
   game.physics.arcade.enable(player);

   //  Playerの物理プロパティ. このちっちゃいヤツは、すこしばかりバウンドする
   player.body.bounce.y = 0.2;
   player.body.gravity.y = 300;
   player.body.collideWorldBounds = true;

   //  左右に歩くためのアニメーション
   player.animations.add('left', [0, 1, 2, 3], 10, true);
   player.animations.add('right', [5, 6, 7, 8], 10, true);

実際のコードは、part5.htmlを参照。表示すると、こんな感じ。

左下に見えるプレイヤー:dudeが、物理演算にしたがって、地面の下まで勝手に落ちる。

example_004.png

スプライトシート

ここでは、playerスプライトを用意して、横32ピクセル、高さはゲーム画面の最下部から150ピクセルに配置している。この"dude"(デューデュ)アセットは、最初のところでロードしたヤツだ。preload関数のところをチラッとのぞいてみると、これがイメージではなく、スプライトシート(spritesheet)として読み込まれているのが分かるだろう。

game.load.spritesheet('dude', 'assets/dude.png', 32, 48);

スプライトシートは、単一のイメージではなく、動きを表現するアニメーションのコマが、ひとつの画像ファイルにまとめられている。実際のスプライトシートを画像ファイルとして開くと、こんな感じになっている。

example_dude.png

アニメーションの定義

コードでは、"left"と"right"という2つのアニメーションを定義している。

player.animations.add('left', [0, 1, 2, 3], 10, true);

"left"アニメーションは、スプライトシートの左から0, 1, 2, 3番目までのコマを使い、1秒当たり10コマの速度で表示する。"true"パラメータは、アニメーションをくり返す指示になる。これが、走りの基本周期で、これを反対側でも同じように定義している。

あと、物理的なパラメータも、すこし設定してある。

Note: Phaserは、スプライトの反転機能をサポートして、アニメーションのコマを節約できるけれど、ここでは基本に忠実にやっている。

物理演算

Phaserは、いくつかの異なる物理演算エンジンをサポートしている。「Arcade Physics」と「Ninja Physics」「P2.JS Full-Body Physics」の3種類だ。このチュートリアルでは、モバイルに適した、シンプルで軽量な「Arcade Physics」エンジンを取り上げている。物理エンジンを使うには、コード上でそれを走らせ、さらに、すべてのスプライトとグループで物理エンジンを適用しなければならない。

一度、スプライトに、ArcadePhysics.Body.インスタンスのbodyプロパティを割り当てる。すると、gravityなどbodyオブジェクトが持つ多くの利用できるようになる。書くのは、こんなふうに簡単だ。

player.body.gravity.y = 300;

これは、テキトーな例だけど、それなりに動作する。設定値が大きすぎるので、プレイヤーのキャラは、重すぎて、すとんと地面の下まで落ちて画面の下に張り付いてしまう(part5.htmlで確認できる)。

Part.6:衝突判定とupdate関数

さて、どうしてこんなふうになってしまうかと言うと、まだ、グループとプレーヤーの間に衝突判定を設定してないからだ。すでに、地面と張り出しは固定しておいた。まだやってないのは、プレイヤーが何かと衝突したとき、ちゃんと停止させることだ。

地面スプライトは、もともと動く物体で(ダイナミックボディとも言う)、プレイヤーがぶつかると、衝突の反動は地面に伝わる。この場合、2つの物体は、互いに反対方向に動いて、dudeははねかえり地面は落ち始めるはずだ。だけど、地面が固定してあるので、プレイヤーだけがジャンプすることになる。

衝突判定

さて、ようやく衝突判定について説明できる。

update関数に、次のようにプレイヤーの衝突判定を設定しよう。

function update() {

    //  プレイヤーとplatformグループに衝突判定を追加
   game.physics.arcade.collide(player, platforms); 
}

実際のコードは、part6.htmlを参照。表示するとこんな感じ。

example_005.png

update関数

update関数は、フレームごとに呼び出されるゲームのくり返し処理部分だ。Physics.collide関数は、このゲームプログラムに魔法をかけてくれる。対象となる2つのオブジェクトをテストして、それぞれのオブジェクトにどのように振舞うかを伝える。

この例では、プレイヤースプライトとplatformグループが対象オブジェクトになっている。グループメンバーの衝突判定をする場合は、地面と張り出しについて、それぞれテストしてくれる。

やっと地面に立った。

Part.7:キーボード操作

衝突判定は、これでOK。だけど、まだプレイヤーが動かない。ここで、たぶんドキュメントのイベントリスナーのところを調べなくちゃと思ったんじゃないかな。でも大丈夫。Phaserには、キーボードマネージャーが組み込んであり、こんなちょっとした関数で利用できる。

cursors = game.input.keyboard.createCursorKeys();

この関数には、上(up)、下(down)、左(left)、右(right)という4つのプロパティを持ったcursorsオブジェクトが仕込んであって、Phaser.Key:http://docs.phaser.io/Phaser.Key.htmlオブジェクトのインスタンスになっている。

あとは、update関数の中に、こんな感じのコードを書いてやればいい。

   //  プレイヤーの移動速度をリセット
   player.body.velocity.x = 0;

   if (cursors.left.isDown)
   {
       //  左へ移動
       player.body.velocity.x = -150;

       player.animations.play('left');
   }
   else if (cursors.right.isDown)
   {
       //  右へ移動
       player.body.velocity.x = 150;

       player.animations.play('right');
   }
   else
   {
       //  そのまま
       player.animations.stop();

       player.frame = 4;
   }
   
   //  上矢印キーがおされて、かつプレイヤーが地面についていたらジャンプ
   if (cursors.up.isDown && player.body.touching.down)
   {
       player.body.velocity.y = -350;
   }

実際のコードは、part7.htmlを参照。表示すると、こんな感じ。

example_006.png

ついに、キーボードで操作できた!

プレイヤーを動かすコード

ここでは、コードを多めに追加したので、ちょっと解説してみよう。

まず最初に、水平方向の速度(移動量)をリセットしている。それから、どの矢印キーが押されているかチェックする。それで、もし左矢印キーが押されているなら、水平方向の移動量をマイナスにして、左向きのアニメーションを開始する。もしも、右矢印キーが押されているなら、水平方向の移動量をプラスにして、左向きのアニメーションを開始する。

ここでは、フレームごとに、速度をクリアしてから移動方向を設定する"ストップ-スタート"スタイルを採用している。そのおかげで、プレイヤーのスプライトは、矢印キーを押しているときには移動し、離すと確実に停止する。Phaserは、慣性とか加速とか、もっと複雑な動きを与えることができるけど、このサンプルでは、こんな感じにしておこう。

最後のコードでは、何もキーが押されていないとき、プレイヤーも4番目のフレームを表示させている。4番目のフレームでは、dudeはただ突っ立っているだけだ。

ジャンプ

コードの最後のパートは、ジャンプ機能を追加している。上矢印キーがジャンプボタンになっているので、コードでは、このキーが押されているかをチェックしている。しかし、それだけではなく、プレイヤーが地面に付いているか、空中にいるかもチェックしている。そして、プレイヤーが地面についていて、かつ、上矢印キーが押されている場合、上方向に350px/秒 コマの速度を与える。

ジャンプしたプレイヤーは、自動的に落ちていき、platoformグループと衝突すると停止する。

キーを押して、プレイヤーをいろいろコントロールして、game worldを探検してみよう。part7.htmlのソースコードの値を調整して、どんな動きになるか、試してみると良いだろう。

Part.8:星のかがやき

では、ゲームに目的に付け加えよう。ゲーム画面に星をまきちらして、プレイヤーはそれを集めて回る。

そこで、"stars"グループを足して、設置する。このゲームのcreate関数に、次のコードを追加する。

   stars = game.add.group();

   stars.enableBody = true;

   //  12個の星を等間隔に配置する
   for (var i = 0; i < 12; i++)
   {
       // 'stars'グループの中に、星を生成する
       var star = stars.create(i * 70, 0, 'star');

       //  星に重力を設定する
       star.body.gravity.y = 6;

       //  星には、ランダムなはねかえり具合を設定する
       star.body.bounce.y = 0.7 + Math.random() * 0.2;
   }

実際のコードは、チュートリアルのpart8.htmlを参照。これで、星が追加された。

example_007.png

星の属性

星を作る手順は、platformグループを作ったときとほぼ同じだ。さらに、Javascriptの"for"ループで12個の星を作っている。"i * 70"と記述して、横70ピクセルずつ等間隔に配置する。

それから、プレイヤーに重力を与えたのと同じように、星にも与えるので、ゲームがスタートすると上から落ちてくるようになる。さらに、はねかえり(bounce)を設定して、platformとぶつかったとき、ちょっとジャンプする。このはねかえり(bounce)には、0(バウンドしない)から1(ずっとバウンドする)まで設定できる。このゲームでは、0.7から0.9までの適当な値を設定している。

星の衝突判定

だけど、衝突判定がないと、星はplatoformを通り抜けてしまう。だから、update関数の中に、次のように星とplatformの衝突判定を記述する。

game.physics.arcade.collide(stars, platforms);

もちろん、次のように、プレイヤーと星がぶつかった時の処理も必要だ。

game.physics.arcade.overlap(player, stars, collectStar, null, this);

このコードでは、プレイヤーと星のどれかが重なったら、次のcollectStar関数を呼び出す。

function collectStar (player, star) {
   
   // 画面から星を消す
   star.kill();

}

画面から星を消すには、killメソッドを呼び出すだけ。

これで、このゲームを実行すると、プレイヤーが走ったり、ジャンプでplatformに飛び乗ったりして、星を集めることができる。もしもアイデアがあれば、そのコードを追加してみるのも、悪くないかもね。

Part.9: 最後の仕上げ

さあ最後に、得点を表示するよう改良してみよう。そのためには、Phaser.textオブジェクトを使う。そのために、次の二つの変数を用意する。ひとつは、現在の得点を保持するもの。もうひとつは、テキストオブジェクト自体だ。

var score = 0;
var scoreText;

create関数のところで、このscoreTextオブジェクトを生成する。

scoreText = game.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });

16x16は、テキストを表示する位置。'score: 0'は、表示する文字列の初期設定。そのあと、フォントサイズと文字色をオブジェクトとして記述する。フォントの種類は指定していなくて、ブラウザのデフォルトを使う。Windowsなら、Arialになるはず。

次に、collectStar関数を改良して、プレイヤーが星を集めたとき、得点がカウントアップするようにする。

function collectStar (player, star) {
   
   // 星を画面から消す
   star.kill();

   // 得点をアップして、再度表示
   score += 10;
   scoreText.text = 'Score: ' + score;

}

星を集めるたびに、10ポイントを追加して、scoreTextを再表示する。

実際のコードは、チュートリアルのpart9.htmlを参照。これで、完成!

example_001a.png

完成したコードは、チュートリアルのpart9.htmlを参照。

ここでも、試すことができる。

http://www.catch.jp/program/phaser/001/making_your_first_Phaser_game.html

まとめ

されこれで、Phaserを使った簡単なゲームの作り方を理解した。物理演算を備えたスプライトの生成、プレイヤーの動かし方、ほかの物体と相互作用する方法などだ。

もちろん、もっといろいろな機能を付け加えることもできるだろう。たとえば、ゲームのクリア画面はまだないし、敵キャラもいない。敵キャラを作るには、"spikes"グループを作って、プレイヤーと衝突判定すればいい。

攻撃的なゲームの代わりに、一定時間内にどれだけ星を集めるか競うゲームにしてもいいね。assetsフォルダに、ほんの少しグラフィック素材を入れてあるので、アイデアをふくらませて見て欲しい。

あと250個以上あるコードサンプルも、役に立つはず。

疑問点があるなら、Phaserフォーラムhttp://www.html5gamedevs.com/で聞いてみてね。


さらに改良してみた

このチュートリアルのゲームを、独自に改良して一段とゲームぽくしてみた。

改良点は、こんな感じ。

  • ルール(星がどんどん増えていく。制限時間制)
  • タイマーを使い、時間制限ゲームにした
  • Phaserに同梱のBasic Project Templateを使う
  • グローバル変数を使わず、ゲームのステータスが遷移していく
  • メインメニューとゲームオーバー画面の追加
  • 効果音

関連資料


  トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
添付ファイル: fileexample_007.png 1500件 [詳細] fileexample_001a.png 1492件 [詳細] fileexample_006.png 1521件 [詳細] fileexample_005.png 1477件 [詳細] fileexample_004.png 1469件 [詳細] fileexample_dude.png 1395件 [詳細] fileexample_003.png 1379件 [詳細] fileexample_002.png 1591件 [詳細]
Last-modified: 2014-12-23 (火) 22:25:12 (1889d)