🚀

phaserでゲーム開発 第一回

に公開

まえがき

プログラマーの皆さん、こんにちは。

皆さんは休日にプログラミングの勉強してますか?
私はなんとなく知らない言語の文法読んだり、適当に思いついたものを書いてみたりです。
けど特に作りたいものがない時ってモチベーションあがりませんよね。
というわけで、ゲームでも作る?ってなるのですが、フレームワークも色々あって迷いますよね。
今回は公開まで簡単にできるphaserを選択してみました。

フレームワークの選定理由

おそらく参考資料が最も多いのはUnityだと思います。
しかし、これは初めて練習でやるには機能が過剰だし、開発環境がものすごく重くて、高スペックなPCがないと動かすのが面倒です。
私は一時期これでC#の練習をしていました。

次に、pythonにpyxelというものがあって、これは軽量で初学者向きなのですが、PCにpythonインストールしないといけないし、レンタルサーバでのWEB公開はちょっと面倒です。一応できなくはないけどね。
私は一時期これでpythonの練習をしていました。

で、今回のphaserなんですが、これは超軽量です。
ただのjavascriptだけで動くので難しい環境構築が不要で、ちょっと勉強したいだけとか、仲間内に見せる程度ミニゲーム作りたいだけの方にはピッタリです。
私は一時期これでtypescriptの練習をしていました。

・・・が、今回はjavascriptにしています。なぜならtypescriptはnode.jsとかいるから。
ただし、こちらは私が知る限り日本語の入門書がないのでそこは今後に期待です。

なお、今回使用している画像に関してはこちらのサイトから利用させてもらっています。
画像の転売以外は使用可能なライセンスになっております。
※商用利用する場合は利用規約を自身でしっかり読み込んでください。

https://pipoya.net/sozai/terms-of-use/#google_vignette

やってみよう

では、実際にやってみましょう。
今回は画面が表示されて背景が映るまで。

ファイル配置はこれを想定。

.
├── READ.ME
└── src
    ├── assets
    │   └── img
    │       ├── bg7.jpg
    │           
    ├── index.html
    └── js
        ├── main.js
        └── Scene
            ├── GameScene.js

まず、起点になるhtmlを用意します。
ここでphaserライブラリの読み込みと、自分が書いたjsを読み込みます。
画像はjsの中から呼ぶのでこれは丸写しで触ることはほぼありません。
タイトルタグの中を好きな名前にしてもらうくらい。
CDNなので環境構築不要です。

indes.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Phaserサンプル</title>
  <!-- Phaser本体をCDNから読み込み -->
  <script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.js"></script>
  <script type="module" src="./js/main.js"></script>
</head>
<body>

ゲームの基本設定を書いてゲームエンジンをスタートさせるコードを書きます。

main.js
import { GameScene } from "./Scene/GameScene.js"


/**
 * Phaser のゲーム設定オブジェクト
 * @type {import('phaser').Types.Core.GameConfig}
 */
const config = {
    /** 
     * 描画タイプ
     * - Phaser.AUTO: WebGL が利用可能なら WebGL、それ以外は Canvas を自動選択 
     */
    type: Phaser.AUTO,

    /** ゲーム画面の幅(ピクセル) */
    width: 800,

    /** ゲーム画面の高さ(ピクセル) */
    height: 600,

    /** 使用するシーンの配列 */
    scene: [GameScene],

    /** 物理エンジンの設定 */
    physics: {
        /** デフォルトで使用する物理エンジンの種類 */
        default: 'arcade',

        /** Arcade 物理エンジンの設定 */
        arcade: {
            /** 重力設定(y軸方向に300の力を加える) */
            gravity: { y: 300 },

            /** デバッグ描画の有効/無効 */
            debug: false,
        }
    }
};

/**
 * Phaser.Game のインスタンス
 * 
 * @type {Phaser.Game}
 * @description
 * ゲーム全体を管理するオブジェクト。
 * 
 * - 引数に渡された config オブジェクトに基づき初期化される。
 * - シーンの管理(開始・停止・切替)を行う。
 * - 物理エンジンやレンダラー、入力管理など、ゲームの中枢となる。
 */

const game = new Phaser.Game(config);


gravityはマリオ風のジャンプアクションやなどの場合に使います。
2DのJRPGみたいなジャンプの概念がないものは0を設定します。
これがあると物が落ちるという物理演算を勝手にやってくれます。
フレームワークを使わずにこれも自分でやる場合加速度計算をしないといけません。

// ジャンプ開始
if (jumpKey.isDown && onFloor) {
    velocityY = -500; // 初速を与える
}

// 毎フレーム
velocityY += gravity * delta; // 加速度を速度に加える
player.y += velocityY * delta; // 速度を位置に加える

// 床判定
if (player.y > groundY) {
    player.y = groundY;
    velocityY = 0;
}

ゲームはシーンというものが1つのまとまりになります。
タイトルシーン、ゲームシーン、エンドロールシーンとか。
とりあえず1シーンで構成します。

GameScene.js
/**
 * ゲームのメインシーンを表すクラス
 * 
 * @extends Phaser.Scene
 */
export class GameScene extends Phaser.Scene {
    /**
     * シーンのコンストラクタ
     * - シーンキーを "GameScene" として登録する
     */
    constructor() {
        super("GameScene");
    }

    /**
     * アセットの事前読み込みを行う
     * - 画像や音声などをロードする
     * 
     * @override
     */
    preload() {
        // アセットのベースパスを設定
        this.load.setBaseURL('/assets/');
        // 背景画像を読み込み
        this.load.image('sky', 'img/bg7.jpg');
    }

    /**
     * シーン初期化時に呼ばれる
     * - ゲームオブジェクトを配置する
     * 
     * @override
     */
    create() {
        // 背景画像を追加し、ゲーム画面全体に拡大
        const bg = this.add.image(0, 0, 'sky').setOrigin(0, 0);
        bg.setDisplaySize(this.sys.game.config.width, this.sys.game.config.height);
    }

    /**
     * 毎フレーム呼ばれる更新処理
     * - プレイヤー操作や物理演算などを記述する
     * 
     * @param {number} [time] 経過時間(ミリ秒)
     * @param {number} [delta] 前フレームからの経過時間(ミリ秒)
     * @override
     */
    update(time, delta) {
        // ゲームループ内の処理を書く
    }
}

Phaser.Sceneを継承することでゲームループなどを勝手に制御してくれます。
constructorではシーンのキーを設定しているだけです。

preloadとcreateはPhaser.Sceneの用意したメソッドなので必ずこの名前で実装します。
このpreloadで、画像の読み込みを行います。
preload はシーン開始前に必ず一度だけ呼ばれ、アセットをロードし終わってから create に進みます。
イメージはwindow.onload に近いと考えるとわかりやすいです。

次に、createはシーンの読み込み時に1回だけ発動します。
その後はupdateが毎フレーム呼ばれます。
いわゆるメインループです。
フレームワークを使わない場合はwhile文とかで実装したりするのですが、自分でやるとフリーズに注意したりとか面倒なのでphaserのチカラを借ります。
この仕組みはpyxelなども似ているので一度覚えると使い回しの効く知識だと思います。

[ preload ] → リソース読み込み
      ↓
[ create ] → オブジェクト配置・初期化
      ↓
[ update ] → 毎フレーム実行(無限ループ)

では立ち上げてみましょう。
やりかたはなんでもいいですが、ローカルwebサーバを立ち上げてブラウザで開きます。
pythonがインストールされてるなら以下のように。

python3 -m http.server 8000 --directory /src

これで、背景が表示されるようになります。
立ち上げまではたったこれだけです。
簡単ですね。
ただのjavascriptなので無料の静的HTMLホストサイトでも公開することができちゃいます。
今回はここまで。
お疲れ様でした。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion