phina.jsをES Modules対応などモダン化してみた
はじめに
phina.jsはHTML5ゲーム開発用ライブラリです。
常日頃から愛用させてもらっているのですが、以前よりいくつか気になる点がありました。
- CommonJSにしか対応してない(バンドルするときにライブラリをまるごと取り込んでしまう)
- 各クラスをバラで使う(例:pixi.jsでphina.jsのTweenerクラスだけを使ってみる)便利ライブラリとして使いたいときに不都合な点がある
- ES classが使いたい!
そこで以上を解決しようとES Modules対応+αしたesmブランチなるものを作りました。
最新版リリースはこちら: 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だけのプロジェクトでも利用可能)
(ただし一部まだ型付けが十分でないところがあります)
その他
- できる限り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