🐤

phina.jsをES Modules対応などモダン化してみた

2020/10/10に公開

はじめに

phina.jsはHTML5ゲーム開発用ライブラリです。
常日頃から愛用させてもらっているのですが、以前よりいくつか気になる点がありました。

そこで以上を解決しようとES Modules対応+αしたesmブランチなるものを作りました。
https://github.com/pentamania/phina.js/tree/esm-support_alpha

最新版リリースはこちら: https://github.com/pentamania/phina.js/releases/tag/esm-alpha.4

使い方

※以下、最新版を使っている前提です。

基本

クラス読み込みは今風にimport文を使います。

import { GameApp, DisplayScene, Sprite } from "phina.js esm版ビルドへのパス"

// MainScene 定義
window.MainScene = class extends DisplayScene {
  constructor(options) {
    super(options);

    // スプライト表示
    new Sprite("logo")
      .setPosition(this.gridX.center(), this.gridY.center())
      .addChildTo(this)
    ;
  }
}

// App生成、実行
const app = new GameApp({
  startLabel: "main",
  assets: {
    image: {
      logo: 'https://cdn.jsdelivr.net/npm/phina.js@0.2.3/logo.png',
    },
  }
});
app.run()

各クラスのAPIはごく一部を除き、従来版と変わりありません。ただ後述するようにES classに書き換えているため、いくつか注意が必要です。

ライブラリの読み込み方法は環境に応じて変わりますが、ここでは代表例を2つ紹介します。

ブラウザで直接読み込むケース

ブラウザではmjsビルドを読み込みます。以下はCDNからの読み込み例

<script type="module">
  import { GameApp } from "https://cdn.jsdelivr.net/npm/@pentamania/phina-es@0.0.4/build/phina.esm.mjs"
  /* 省略 */ 
</script>

実際に動くサンプルはこちら:https://runstant.com/pentamania/projects/466a4480

バンドラーを通して読み込むケース

webpackやviteなどのモジュールバンドラーを通して利用する場合、
まずはnpm(yarn他)でパッケージを追加します。

npm install phina.js@npm:@pentamania/phina-es
# もしくは
yarn add phina.js@npm:@pentamania/phina-es

後は以下のように読み込むだけです。

import { GameApp } from "phina.js"
/* 省略 */

従来版からの変更点、機能拡張について

ES class化

元々のphinaはクラス定義に独自のメソッド(phina.createClass)が使われていますが、それをES classに書き換えました。
こちらの方が(エディタのコーディング支援を受けられるので)DX的に良いのと、後述の型定義出力に便利ということがあります。

ただcreateClassとは異なり、インスタンス化の際に必ずnew演算子をつけないといけないなどの違いがあります。

ちなみに結構自力で書き換えてます(めちゃくちゃ大変…)

prototype拡張するかは選択式に

従来版ではArrayなどのビルトインオブジェクトのprototype拡張する処理を行いますが、他のライブラリと組み合わせるときは都合が悪いので、以下展開用の関数を実行するまで拡張は行わないようにしてます。

import { extendBuiltInObject } from 'path/to/phina.esm.js'

// 従来版のように一斉拡張
extendBuiltInObject();

// 一部だけ拡張することも可能
extendBuiltInObject("Number", ["clamp", "upto"]);

またArray.findなど現在ではサポートが一般的になったメソッドのポリフィルは省いてます。

ちなみにphina.js本体が拡張された状態を前提にコードが書かれており、そこをどうするかが結構悩みどころでした。(callメソッドを駆使するなどして何とかしています)

typescript型定義の提供(&ドキュメント強化)

リリースパッケージではついでに型定義ファイルも提供しています。
これにより、VS Codeなどの対応エディタではインテリセンス、型エラー判定などが利用できます。(JavaScriptだけのプロジェクトでも利用可能)

phina-d.ts-intellisense

(ただし一部まだ型付けが十分でないところがあります)

その他

  • できる限りAPIをいじらないようにしていますが、型定義のため若干のバグの修正、Rest Parametersへの置換などを行っています。
  • box2d, canvasrecorderなど他のライブラリと連携することが前提の機能は基本サポートしてません(ThreeLayer(three.js連携)だけ部分的にサポート)
  • Accelerometer(モバイル端末の加速度検知機能)クラスは現在では動作させるハードルが非常に高く、まず動かないのでenterframeでの仕込みをやめてます
  • Element.toJSONはES classではサポートが難しいため、動作しません

既存シーン定義について

phinaにはMainScene, TitleSceneなどいくつか定義済みのシーンクラスがあり、従来ではphina.defineで上書きしてGameAppにそれを読み込ませるという処理が一般的ですが、class化に伴い、代わりにwindowオブジェクトに直接生やすということをします。

phina.define("MainScene", { superClass: "DisplayScene", /* ...*/})
// ↓
window.MainScene = class extends DisplayScene {/*...*/}

(若干不格好(そしてtypescriptでは怒られる)なのでどうにかしたい…)

今後

今も型が不十分なのを直したい等ありますが、大体やりたいことは実現できたので、現在はそれほど開発に注力していません。
ですが、要望・意見やバグ報告などは大歓迎ですのでよろしくお願いいたします。(こちらの記事へのコメントやtwitterのDMなどをご利用ください)

Discussion