概要
- Phaser3(TypeScript)を動かしてみる
前回の記事では環境構築まで行ったので、実際にPhaserを動作させてみます。
ディレクトリの用意
package.json
のあるディレクトリに
- scenes
- assets
というディレクトリを作成してください。
場面ごとに必要なスクリプトファイルは scenes
ディレクトリに保存します。
画像や音声などのファイルは assets
ディレクトリに保存します。
HTMLの用意
次にHTMLファイルを用意していきます。
package.json
のあるディレクトリにindex.html
というファイルを作成し、以下のように記載してください。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.css" integrity="sha256-7VVaJ5GDwFQiLIc+eNksQLUSSY5JNZtqv9o2BI8UGYg=" crossorigin="anonymous" />
<title>テスト</title>
<script src="dist/bundle.js"></script>
</head>
<body>
<div id="game"></div>
</body>
</html>
ポイント
- dist下にあるbundle.jsを読み込ませる
- Phaserの画面書き出し先の要素を用意する(
<div id="game"></div>
) - リセットCSSを読ませる(好み)
WebpackによりバンドルされたJavaScriptは./dist/bundle.jsに作られます。
この中にPhaserや自分で書いたスクリプトの内容が入ってます。
ブラウザごとに既定のスタイルが用意されていて煩わしい動きをするので、
リセットCSSを読み込ませて既定のスタイルをつぶしています。
エントリポイント(index.ts)の追加
次にWebpackのエントリポイントとして指定していたindex.ts
を用意します。
package.json
のあるディレクトリにindex.ts
というファイルを作成し、以下のように記載してください。
import 'phaser';
//あとでコメントアウトを解除する
import Scenes from './scenes/scenes';
//コンフィグ
const config: Phaser.Types.Core.GameConfig = {
//画面サイズ
width: 360,
height: 640,
type: Phaser.AUTO,
//ゲーム画面を描画するcanvasを書き出す先
parent: 'game',
//ゲーム画面を伸縮して表示させるための設定
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: 'game',
},
//あとでコメントアウトを解除する
//必要なシーンを読み込む
scene: Scenes,
};
export class Game extends Phaser.Game {
constructor(config: Phaser.Types.Core.GameConfig) {
super(config);
}
}
//HTMLがロードされた後にインスタンスを生成する
window.addEventListener('load', () => {
const game = new Game(config);
});
Phaserを実際に読み込み、configを注入してGameインスタンスを作ってます。
画像ファイルを用意する
assets
ディレクトリに picture.png
という画像ファイルを保存します。
いらすとやの画像でもなんでもいいです。
シーンを作ってみる
Phaserはシーン(Scene)という単位で、オブジェクト(Game Object)の管理をしてます。
「ローディング」や「タイトル」、「ポーズ」など、独立する画面ごとにシーンを切り分けていくようにするとイイ感じなんじゃないでしょうか。
とりあえず
- ローディング画面
- タイトル画面
- ゲーム画面
を簡易に作ってみます。
ローディング画面を作成する
scenes
ディレクトリにloading-scene.ts
というファイルを作成し、以下のように記載してください。
export default class LoadingScene extends Phaser.Scene {
constructor() {
super({
key: 'Loading',
});
}
/**アセットを読み込むライフサイクルで呼ばれるメソッド*/
preload(): void {
//ロード中の文面を設定する
const loadingText = (progress: number): string =>
`Now Loading ... ${Math.round(progress * 100)}%`;
//テキストオブジェクトを作る
const currentLoadingText = this.add.text(10, 10, loadingText(0));
//ファイルのロードをしていく
this.load.image('acorn', '../assets/picture.png');
//疑似的に大量のアセットをロードするかのような動きをさせてる
for (let index = 0; index < 100; index++) {
this.load.image('acorn' + index, '../assets/picture.png');
}
//ロードに進捗があるたびに発生するイベント
this.load.on('progress', (progress: number) => {
//テキストの内容を書き換える
currentLoadingText.text = loadingText(progress);
});
//ロードが完了すると発生するイベント
this.load.on('complete', () => {
//タイトルシーンへ遷移
this.scene.start('Title');
});
}
}
シーンはPhaser.Scene
を継承して実装していきます。
必要な画像データをロードする処理は、preload()
の中に実装していくようにしてください。
ここでロードしたデータは、別のシーンでも使えます。
コンストラクタ内にシーン固有のコンフィグを設定していってください。
タイトル画面を作成する
scenes
ディレクトリにtitle-scene.ts
というファイルを作成し、以下のように記載してください。
export default class TitleScene extends Phaser.Scene {
constructor() {
super({
key: 'Title',
});
}
/**ロードが終わったあとのライフサイクルで呼ばれるメソッド */
create(): void {
const text = this.add.text(10, 10, 'おしたらStart');
//setInteractiveを呼ぶと動的なオブジェクトになる
//入力系のイベントなどが有効化される
text.setInteractive();
text.on('pointerdown', () => {
this.scene.start('Main');
});
}
}
必要な画像データがロードされたあと、シーンの開始時点で必要な処理は、create()
の中に実装していくようにしてください。
ゲーム画面を作成する
scenes
ディレクトリにmain-scene.ts
というファイルを作成し、以下のように記載してください。
export default class MainScene extends Phaser.Scene {
private acorn: Phaser.Physics.Arcade.Image;
constructor() {
super({
key: 'Main',
physics: { arcade: { debug: true } },
});
}
create(): void {
this.physics.world.gravity.y = 100;
this.acorn = this.physics.add
.sprite(30, 30, 'acorn') //loading画面で設定したkeyで画像を読める
.setScale(0.1)
.setOrigin(0, 0)
.setInteractive()
.setVelocityX(100)
.setCollideWorldBounds(true, 1, 1);
this.acorn.on('pointerdown', () => {
this.acorn.setVelocityX(-this.acorn.body.velocity.x);
});
}
update(): void {
console.log(this.acorn.x, this.acorn.y);
}
}
シーンが開始されて毎フレーム必要な処理は、update()
の中に実装していくようにしてください。
acornに対してsetしている内容は大体読めばわかる通りです。
数値をいじるといろいろ変化がおきるので試してください。
このシーン固有のコンフィグとして、物理演算を有効化しています。
各シーンをフレームワークのConfigに設定する
index.ts
の中で設定しているconfigに設定を適用します。
scenes
ディレクトリにscenes.ts
というファイルを作成し、以下のように記載してください。
import Main from './main-scene';
import Loading from './loading-scene';
import Title from './title-scene';
export default [Loading, Main, Title];
export { Main, Loading, Title };
シーン増えるとわけわかんなくなってくるので、ここでシーンを全部束ねておきます。
最後に index.ts
のコメントアウトを解除します。
import 'phaser';
//ここのコメントアウトを解除する
import Scenes from './scenes/scenes';
//コンフィグ
const config: Phaser.Types.Core.GameConfig = {
//画面サイズ
width: 360,
height: 640,
type: Phaser.AUTO,
//ゲーム画面を描画するcanvasを書き出す先
parent: 'game',
//ゲーム画面を伸縮して表示させるための設定
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: 'game',
},
//ここのコメントアウトを解除する
//必要なシーンを読み込む
scene: Scenes,
};
export class Game extends Phaser.Game {
constructor(config: Phaser.Types.Core.GameConfig) {
super(config);
}
}
//HTMLがロードされた後にインスタンスを生成する
window.addEventListener('load', () => {
const game = new Game(config);
});
これで作成したシーンの内容がPhaserに反映されるようになります。
開発用サーバを起動する
yarn dev
を実行してください。
Webpackがindex.tsを起点に必要なモジュールをバンドルして、 dist/bundle.js
を生成しつつ、開発用のWebサーバを立ち上げてくれます。
TypeScriptファイルの更新を検知し、なんらか更新されたら自動的に再読み込みが行われます。
超簡素ですが、とりあえず動作する状態になりました。
最後に
次回はTiledと連携してレベルを作ったりするところをやってみます。