Phaser 3 + Webpack + Typescript でゲームを作る

# 概要
- Phaser 3を使ってHTML5ゲームを開発するための環境構築
- Typescriptで処理を書き，Webpackでトランスパイル
- ESLint + Prettier によるコード整形の導入

# 各種ツールの導入
フレームワークやパッケージなど，環境構築に必要なものを導入していきます．

## Node.jsの導入
[Node.js](https://nodejs.org/ja/)というサーバサイドのJavaScirpt実行環境をインストールします．

Node.jsのホームページからインストーラーをダウンロードするか，MacならHomebrewでもインストールできます．

```shell
brew install node
```

Node.jsのバージョンを簡単に管理したい方はNodebrewからインストールするのもおすすめ．(`bash_profile`等は適宜変えてください)

```shell
brew install nodebrew
nodebrew setup
echo "export PATH=\$HOME/.nodebrew/current/bin:\$PATH" >> ~/.bash_profile
nodebrew install-binary v14.15.3
nodebrew use v14.15.3
node -v
```

Node.jsのパッケージ管理ツールはnpmとyarnが主に使われてますが，ここではyarnを使います．npm派の方は適宜読み替えてください．

```shell
npm install -g yarn
yarn -v
```

## プロジェクトの初期化
プロジェクトのルートディレクトリでプロジェクトを初期化します．

```shell
yarn init
```
色々聞かれますが，特に理由がなければEnterを押していれば大丈夫だと思います．初期化が終わると，ルートディレクトリに`package.json`が生成されます．

## Phaserの導入
[Phaser](https://phaser.io/phaser3)を導入します．PhaserはHTML5用ブラウザゲームのフレームワークです．

```shell
yarn add phaser
```

## Typescriptの導入
[TypeScript](https://www.typescriptlang.org/)という言語を導入します．ざっくり言うと，静的型付けのJavaScriptです．

```shell
yarn add -D typescript
```
TypeScriptをインストールしたら，ルートディレクトリに`tsconfig.json`というファイルを作成してください．

```json:tsconfig.json
{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": false,
    "strict": false
  }
}
```

## Webpackの導入
[Webpack](https://webpack.js.org/)というモジュールバンドラを導入します．JavaScriptやCSS，TypeScriptなどのファイルを1つのJavaScriptファイルにまとめてくれます．

```shell
yarn add -D webpack webpack-cli@3.3.12 webpack-dev-server ts-loader expose-loader
```
webpack-cliのv4をインストールするとエラーが発生するので，バージョン指定をしています．

上記のインストールができたら，ルートディレクトリに`webpack.config.js`というファイルを作成してください．

```js:webpack.config.js
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
const pathToPhaser = path.join(__dirname, '/node_modules/phaser/');
const phaser = path.join(pathToPhaser, 'dist/phaser.js');

module.exports = {
  entry: './index.ts',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      { test: /\.ts$/, loader: 'ts-loader', exclude: '/node_modules/' },
      {
        test: /phaser\.js$/,
        loader: 'expose-loader',
        options: {
          exposes: { globalName: 'Phaser', override: true },
        },
      },
    ],
  },
  devServer: {
    contentBase: path.resolve(__dirname, './'),
    publicPath: '/dist/',
    host: '127.0.0.1',
    port: 8080,
    open: true,
  },

  resolve: {
    extensions: ['.ts', '.js'],
    alias: {
      phaser: phaser,
    },
  },
};
```

## ESLintとPrettierの導入
[ESLint](https://eslint.org/)と[Prettier](https://prettier.io/)を導入します．どちらもコード整形してくれるツールで，ESLintは構文チェックとコード整形を，PrettierはESLintで整形できないコードを整形してくれます．

```shell
yarn add -D prettier 
yarn add -D eslint eslint-config-prettier eslint-plugin-prettier
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser 
```

ESLintをインストールしたら`.eslintrc.json`を，Prettierをインストールしたら`.prettierrc`をルートディレクトリに作成してください．

```json:.exlintrc.json
{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "./tsconfig.json"
  },
  "plugins": ["@typescript-eslint", "prettier"],
  "extends": [
    "plugin:@typescript-eslint/recommended",
    "prettier/@typescript-eslint",
    "plugin:prettier/recommended"
  ],
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "rules": {
    "prettier/prettier": "error",
    "no-return-assign": ["off"]
  }
}
```

```json:.prettierrc
{
 "trailingComma": "all",
 "tabWidth": 2,
 "semi": true,
 "singleQuote": true
}
```

上記2つのファイルはコード整形のルールなので，コードが書きやすいように適宜ルールを書き換えてください．

# 動作確認
[Phaser 3のチュートリアル](https://phaser.io/tutorials/making-your-first-phaser-3-game/part1)を使って動作確認します．

今回は，チュートリアルのPart2 - Loading Assetsまで(公式HPで配布されているチュートリアルのサンプルコードの`part3.html`まで)の内容を動作させてみます．

公式HPにあるチュートリアルのPart1 - IntroductionのRequirementsの節にあるサンプルコードのzipファイルをダウンロードしてください．

ダウンロードしたらzipファイルを解凍し，中にある`assets`ディレクトリを，プロジェクトのルートディレクトリの下に置いてください．また，ルートディレクトリに`scenes`ディレクトリを作成しておいてください．

## HTMLの用意
プロジェクトのルートディレクトリに，`index.html`を作成してください．

```html:index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>TutorialMaking your first Phaser 3 Game - Part 1</title>
    <style type="text/css">
      body {
        margin: 0;
      }
    </style>
    <script src="dist/bundle.js" ></script>
  </head>
  <body>
    <div id="game"></div>
  </body>
</html>
```

中身はサンプルコードのhtmlファイルとほほ同じです．ただし，`script`タグのsrcは`dist/bundle.js`に指定しています．

## エントリーポイントの用意
Phaserではゲームの1画面を1つのSceneというオブジェクトで管理しています．なので，先程作成した`scenes`ディレクトリにScene毎のTypeScriptファイルを置き，Webpackのエントリーポイントである`index.ts`ファイルでSceneを読み込んであげます．

ルートディレクトリに`index.ts`を作成してください．

```ts:index.ts
import 'phaser';
import Main from './scenes/main';

const config: Phaser.Types.Core.GameConfig = {
  width: 800,
  height: 600,
  type: Phaser.AUTO,
  parent: 'game',
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    parent: 'game',
  },
  scene: Main,
};

export class Game extends Phaser.Game {
  constructor(config: Phaser.Types.Core.GameConfig) {
    super(config);
  }
}

window.addEventListener('load', () => {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const game = new Game(config);
});
```

`index.ts`でPhaserを読み込み，`GameConfig`オブジェクトを作成して，`Game`インスタンスを作成します．使うSceneをConfigで宣言することで，実際に読み込んで描画することができます．

## Sceneの用意
今回は，画面は1つしかないので，Sceneは1つになります．

`scenes`ディレクトリに`main.ts`を作成し，描画するオブジェクトや処理を書いていきます．

```ts:main.ts
export default class Main extends Phaser.Scene {
  constructor() {
    super({
      key: 'Main',
      physics: {
        arcade: {
          gravity: { y: 300 },
          debug: false,
        },
      },
    });
  }

  preload(): void {
    this.load.image('sky', 'assets/sky.png');
    this.load.image('ground', 'assets/platform.png');
    this.load.image('star', 'assets/star.png');
    this.load.image('bomb', 'assets/bomb.png');
    this.load.spritesheet('dude', 'assets/dude.png', {
      frameWidth: 32,
      frameHeight: 48
    });
  }

  create(): void {
    this.add.image(400, 300, 'sky');
  }

  update(): void {
  }
}
```

## サーバの起動
以下のコマンドで開発用サーバを起動し，表示されたURLへ飛ぶと動作しているかと思います(デフォルトのURLは http://127.0.0.1:8080/ になっているかと思います)．

```shell
npx webpack-dev-server
```

チュートリアルのpart2までなので，空の背景を読み込んで描画させているだけですが，とりあえず動作はしているのではないでしょうか．

# 最後に
1画面1ファイルだと，ちょっとゲームの規模が大きくなると1ファイルのステップ数がスゴイことになりそうなので，部品毎に分けるなど，うまいことスクリプトを細分化して綺麗な設計になるように考えたい