Phaser 3 (+ Vite) で作成した HTML5 製のゲームに Steamworks SDK を導入する手順（NW.js + Greenworks）を 2024/07 にやってみたメモ

参考にした記事:

https://qiita.com/laineus/items/f8a9f908587d9d9e8087

# 1. Phaser 3 で HTML5 のゲームを作成する
https://phaser.io/tutorials/create-game-app

↑ Phaser 3 でゲーム作成を検討する場合、[2024/03 に出た](https://www.npmjs.com/package/@phaserjs/create-game) `create-phaser-game` を使うとフレームワークとバンドラーを組み合わせたテンプレートを作ってくれる。(ただ、比較的新しいツールのためか、昔からある公式の [Getting Started Tutorial](https://phaser.io/tutorials/getting-started-phaser3) や [Making your First Game](https://phaser.io/tutorials/making-your-first-phaser-3-game/part1) や Phaser 導入記事などで取り扱いが薄い。)

https://qiita.com/n2-freevas/items/f8f158360c55727cb06f

↑ の記事にあるような、Vite で開発するプロジェクトテンプレートを作ってくれる。[v1.2.3](https://www.npmjs.com/package/@phaserjs/create-game/v/1.2.3) で作成すると、以下のような構成のプロジェクトが作成された。 (Demo Game など、バンドラーだけで Phaser 以外のフレームワークを使わない構成もある)


```
my-phaser-game/
  - public/
  - src/
  - index.html
  - package.json
  - ...
```

その後 CLI は以下のように開発方法も指示してくれる。

```
...
o----------------------------------------------------------o
|               Project created successfully               |
|                Thank you for using Phaser                |
|        Tell us about your game! - games@phaser.io        |
o----------------------------------------------------------o

Next steps:
1. cd my-phaser-game
2. npm install
3. npm run dev
```

CLI の指示どおり `npm install` `npm run dev` することで、Vite を使ったブラウザで常時確認しながらの開発ができる。`npm run build` では公開用のファイルをビルドすることができて、`dist` ディレクトリに成果物ができる。

```
my-phaser-game/
  - ...
  - dist/
    - assets/
    - favicon.ico
    - index.html
```

この `dist` をインターネットに公開すればそのままブラウザで動かせるゲームを公開できる。今回はこれを Steam 連携するために、NW.js で動かす。

# 2. Phaser 3 で作ったゲームを NW.js で動かす

## 2.1. NW.js のダウンロード

NW.js のダウンロードの際、Steam 連携用ライブラリの Greenworks とバージョンを揃えたい関係で、バージョンを確認する必要がある。[NW.js のブログの情報](https://nwjs.io/blog/)と [Greenworks の対応情報](https://github.com/greenheartgames/greenworks/releases)、[Steamworks.js](https://www.npmjs.com/package/steamworks.js) 内の [redistributable_bin のバージョン](https://github.com/ceifa/steamworks.js/commits/main/sdk/redistributable_bin) をまとめると、検証した 2024/07 時点では

| [NW.js](https://nwjs.io/blog/) (Chromium + Node) | [Greenworks](https://github.com/greenheartgames/greenworks/releases) | [Steamworks.js](https://www.npmjs.com/package/steamworks.js ) | [Steamworks SDK](https://partner.steamgames.com/doc/sdk?l=japanese) |
| ---- | ---- | ---- | ---- | 
| 2025/04/04 [0.98.1](https://nwjs.io/blog/v0.98.0/) Chromium 135 + Node 23.10.0 | | | |
| 2025/03/28 [0.98.0](https://nwjs.io/blog/v0.98.0/) Chromium 135 + Node 23.3.0 |  2025/04/01 [0.20.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.20.0) Steamworks SDK 1.62 | | 2025/03/14 [1.62](https://partner.steamgames.com/downloads/list) |
| 2025/03/11 [0.97.0](https://nwjs.io/blog/v0.97.0/) Chromium 134 + Node 23.3.0 | | | |
| 2025/02/18 [0.96.0](https://nwjs.io/blog/v0.96.0/) Chromium 133 + Node 23.3.0 | | | |
| 2025/01/24 [0.95.0](https://nwjs.io/blog/v0.95.0/) Chromium 132 + Node 23.3.0 | | | |
| 2025/01/05 [0.94.1](https://nwjs.io/blog/v0.94.1/) Chromium 131 + Node 23.3.0 | | | |
| 2024/12/03 [0.94.0](https://nwjs.io/blog/v0.94.0/) Chromium 131 + Node 22.7.0 | 2024/12/09 [0.19.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.19.0) Steamworks SDK 1.61 |  | 2024/11/08 [1.61](https://partner.steamgames.com/downloads/list) |
| 2024/10/22 [0.93.0](https://nwjs.io/blog/v0.93.0/) Chromium 130 + Node 22.2.0 | | | |
| 2024/09/24 [0.92.0](https://nwjs.io/blog/v0.92.0/) Chromium 129 + Node 22.2.0 | 2024/10/17 [0.18.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.18.0) Steamworks SDK 1.60 | | | 
| 2024/08/21 [0.91.0](https://nwjs.io/blog/v0.91.0/) Chromium 128 + Node 22.2.0 | | | |
| 2024/07/30 [0.90.0](https://nwjs.io/blog/v0.90.0/) Chromium 127 + Node 22.2.0 | | | | 
| 2024/06/25 [0.89.0](https://nwjs.io/blog/v0.89.0/) Chromium 126 + Node 22.2.0 | 2024/07/27 [0.17.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.17.0) Steamworks SDK 1.60 | 2024/08/06 [0.4.0](https://www.npmjs.com/package/steamworks.js/v/0.4.0) Steamworks SDK 1.60  |  2024/06/26 [1.60](https://partner.steamgames.com/downloads/list) |
| 2024/05/21 [0.88.0](https://nwjs.io/blog/v0.88.0/) Chromium 125 + Node 22.0.0 | | | |
| 2024/05/21 [0.87.0](https://nwjs.io/blog/v0.87.0/) Chromium 124 + Node 21.1.0 | | | |
| 2024/05/21 [0.86.0](https://nwjs.io/blog/v0.86.0/) Chromium 123 + Node 21.1.0 | 2024/04/07 [0.16.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.16.0) Steamworks SDK 1.59 |  | 2024/02/09 [1.59](https://partner.steamgames.com/downloads/list) |
| 2024/05/21 [0.85.0](https://nwjs.io/blog/v0.85.0/) Chromium 122 + Node 21.1.0 |  | | |
| 2024/05/21 [0.84.0](https://nwjs.io/blog/v0.84.0/) Chromium 121 + Node 21.1.0 |  | | |
| 2024/05/21 [0.83.0](https://nwjs.io/blog/v0.83.0/) Chromium 120 + Node 21.1.0 |  | | |
| 2024/05/21 [0.82.0](https://nwjs.io/blog/v0.82.0/) Chromium 119 + Node 20.0.7 | 2023/11/05 [0.15.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.15.0) Steamworks SDK 1.58 | 2024/06/04 [0.3.2](https://www.npmjs.com/package/steamworks.js/v/0.3.2) Steamworks SDK 1.58a | 2023/10/25 [1.58a](https://partner.steamgames.com/downloads/list)   | 

なので、今回は `nwjs-v0.89.0-win-x64.zip` と `greenworks-v0.17.0-nw-v0.89.0-win-64.zip` と `steamworks_sdk_160.zip` をダウンロードした。Steam 連携などを考えなければどの NW.js で問題なさそうだが、Steam 連携しないならそもそも Web 上でそのまま公開する方が早そう。

Greenworks は開発がしばらく止まっていたので Steamworks.js などのフォロワーが生まれていたが、2024/07 現在

- Greenworks
    -  2018/11/18 [0.14.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.14.0) -> 2023/11/05 [0.15.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.15.0) -> 2024/04/07 [0.16.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.16.0) -> 2024/07/27 [0.17.0](https://github.com/greenheartgames/greenworks/releases/tag/v0.17.0)
-  Steamworks.js 
    -  2023/07/24 [0.3.0](https://www.npmjs.com/package/steamworks.js/v/0.3.0) -> 2023/08/23 [0.3.1](https://www.npmjs.com/package/steamworks.js/v/0.3.1) -> 2024/06/04 [0.3.2](https://www.npmjs.com/package/steamworks.js/v/0.3.2)

と、2018 年から開発が止まっていた Greenworks にも 2023 年後半からちょこちょこ更新が入っていた。[元記事](https://qiita.com/laineus/items/f8a9f908587d9d9e8087)が充実していたので NW.js + Greenworks を試すことにした。

## 2.2. dist/ を NW.js で起動

[元記事](https://qiita.com/laineus/items/f8a9f908587d9d9e8087) を参考に、`nwjs-v0.89.0-win-x64.zip` を展開してできるファイル群に `dist/` 配下のファイルをコピーする。コード上、`index.html` が ルートになる想定なので、`dist/` ディレクトリごとコピーするとうまくいかないので注意。

```
my-phaser-game/                 nwjs-sdk-v0.89.0-win-x64/
  - ...                           - ...
                                  - nw.exe
                                  - package.json ★追加する
  - dist/
    - assets/       -- cp -->     - assets/
    - favicon.ico   -- cp -->     - favicon.ico
    - index.html    -- cp -->     - index.html
```

↑のように、`nw.exe` と同じディレクトリにファイル群をコピーする。`package.json` は [NW.js 用の書式](https://docs.nwjs.io/en/latest/References/Manifest%20Format/)で

```json:package.json ★追加する
{
    "name": "my-phaser-game",
    "main": "index.html"
}
```

のように追加する。Phaser  の `dist/` はブラウザコンテキストで動かせる状態なので、この時点で `nw.exe` を起動して試してみても問題なく動く。

# 3. Phaser 3 で作ったゲームを NW.js で動かしつつ、Greenworks で Steam 連携を実装する

## 3.1. ディレクトリ構成

[元記事](https://qiita.com/laineus/items/f8a9f908587d9d9e8087) のままファイルを配置すれば OK で、それぞれのディレクトリの下から NW.js 下にコピーする場合、以下のようになる。Greenworks 用のコピーは[公式](https://github.com/greenheartgames/greenworks/blob/master/docs/quick-start-nwjs.md)も参考にする。win64 の例を書くと、

```
| my-phaser-game/ | greenworks-v0.17.0-*/ | steamworks_sdk_160/               | nwjs-sdk-v0.89.0-win-x64/       |
|                 |                       |                                   | - nw.exe                        |
|                 |                       |                                   | - package.json ★追加する       |
| - dist/         |                       |                                   |                                 |
|   - assets/     -->                                                           - assets/                       |
|   - favicon.ico -->                                                           - favicon.ico                   |
|   - index.html  -->                                                           - index.html                    |
|                 |                       |                                   | - steam_appid.txt★追加する       |
|                 | - greenworks.js           -->                               - greenworks.js                 |
|                 | - lib/                    -->                               - lib/                          |
|                 |   - greenworks-win64.node -->                                 - greenworks-win64.node       |
|                 |                       | - sdk/redistributable_bin         |                                 |
|                 |                       |   - steam_api.dll                 |                                 |
|                 |                       |   - win64/                        |                                 |
|                 |                       |     - steam_api64.dll             --> - steam_api64.dll             |
|                 |                       | - sdk/public/steam/lib/           |                                 |
|                 |                       |   - win64/                        |                                 |
|                 |                       |     - sdkencryptedappticket64.dll --> - sdkencryptedappticket64.dll |
```

のように各 zip から NW.js のディレクトリへコピーする。`steam_appid.txt` は appid を入れるが、開発用には 480 を入れる。

```text:steam_appid.txt ★追加する
480
```

最終的に NW.js には

```
nwjs-sdk-v0.89.0-win-x64/
  - ...
  - nw.exe
  - package.json                  ①追加する
  - assets/                       ②dist/ からコピー
  - favicon.ico                   ②dist/ からコピー
  - index.html                    ②dist/ からコピー
  - steam_appid.txt               ①追加する
  - greenworks.js                 ③Greenworks からコピー
  - lib/                          ③Greenworks からコピー
    - greenworks-win64.node       ③Greenworks からコピー
    - steam_api64.dll             ④Steamworks SDK からコピー
    - sdkencryptedappticket64.dll ④Steamworks SDK からコピー
```

のように、それぞれ作成＆コピーしてきたファイルが配置される。


## 3.2. Phaser のゲームから Greenworks 経由で Steamwork SDK の API を呼ぶ

あとは `index.html` 内のソースから `require('greenworks.js')` すれば Greenworks 経由で Steam の API を呼べる状態だが、ここで若干ハマった。

Vite でビルドするとソースの本体は `dist/` の `assets/` 内、`index-***.js` にバンドルされる。この時、Vite はブラウザコンテキストで動かせるようにソースをビルドするので、CommonJS ライブラリを読む時のように `greenworks.js` の拡張子を cjs にして `import * as greenworks from './greenworks.cjs';` などとインポートした場合、ビルド時にブラウザで動かないコードがエラーになり、うまくバンドルされなかった。(`greenworks.js` は NW.js や Node.js 上で動く想定なので `steam_appid.txt` を読み込む `require('fs')` などが普通にある)

### 3.2.1 Vite プラグインで解決

https://github.com/vitejs/vite/issues/13388

上記は同様の問題を Vite プラグインで解決している模様。

https://www.npmjs.com/package/vite-plugin-import-to-require

ただ、上記プラグインは試していないので、実際に該当プラグインを使って Greenworks が正しくバンドル＆動作するかは確認できていない。プラグインが [2023/06/08 の 1.0.0 リリースのみ](https://www.npmjs.com/package/vite-plugin-import-to-require)であり、どこまでメンテナンスされ続けるか不明瞭だったのでなんとなく避けた。

### 3.2.2. ブラウザの global 経由で greenworks を使う

今回、Steam 連携はコードのメインではないので、ビルド外で `require('greenworks.js')` したインスタンスを `window` 経由で参照する方法を試した。この場合 `dist/assets` 下の js には `greenworks.js` が含まれないため、3.1 のように手動で配置して対応している。

利用方法としては `assets/` の `index-***.js` 実行前に読み込んでおければどこでも良さそう。Phaser の `create-phaser-game` では `index.html` は以下のような感じで作られるが、

```html:index.html (Original)
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/png" href="/favicon.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/style.css">
    <title>Phaser - Template</title>
</head>

<body>
    <div id="app">
        <div id="game-container"></div>
    </div>
    <script type="module" src="src/main.js"></script>
</body>
</html>
```

ゲーム本体の読み込み前に `window.greenworks` を作り、Phaser の Scene などから参照できるようにした。

```diff_html:index.html (Modified)
 <!doctype html>
 <html lang="en">
 <head>
     <meta charset="UTF-8" />
     <link rel="icon" type="image/png" href="/favicon.png" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <link rel="stylesheet" href="/style.css">
     <title>Phaser - Template</title>
 </head>
 
 <body>
     <div id="app">
         <div id="game-container"></div>
     </div>
+    <script>
+        if (typeof nw !== 'undefined' && typeof nw.require === 'function') {
+            // NWJS環境
+            window.greenworks = require('greenworks.js');
+            console.log("Running in NWJS environment");
+            if (window.greenworks.init()) {
+                console.log("Steam initialized successfully");
+            } else {
+                console.log("Steam initialization failed");
+            }
+        } else if (typeof require !== 'undefined') {
+            // Node.js環境（NWJSのNode context含む）
+            console.log("Running in Node.js environment");
+        } else {
+            // ブラウザ環境
+            console.log("Running in browser environment");
+        }
+    </script>
     <script type="module" src="src/main.js"></script>
 </body>
 </html>
```

その後、ゲーム内のコードで

```javascript
        this.greenworks = window.greenworks || null;
        if (this.greenworks) {
            try {
                const username = this.greenworks.getSteamId().getPersonaName();
                console.log(`Logged in as: ${username}`);
            } catch (error) {
                console.error("Failed to get Steam username:", error);
            }
        }
```

上記のように window 経由で呼ぶことで、Steam の情報取得ができることを確認できた。ただし実行時にインスタンスが入るため Greenworks の API を IDE で参照することはできなそうだし、TypeScript できちんと呼べるかなども試せていない。

# 4. まとめ

[元記事](https://qiita.com/laineus/items/f8a9f908587d9d9e8087) は 2021 年での内容だったが、2024/07 時点でも Phaser 3 + Vite + NW.js 0.89.0 + Greenworks 0.17.0 + Steamworks SDK 1.60 が動くことを確認できた。

結局のところ、Phaser 3 でのゲーム実装のほとんどはブラウザコンテキストで動くように作られるので、Native アプリをどのように作るのかは Steam 連携用のライブラリをどれにするかで決めるのが楽そうだった。Greenworks は NW.js 用のビルドを出しているので NW.js が利用しやすそうだが、Steamworks.js + Electron 方式が活発になるようなことがあれば NW.js でなくて Electron の方が実装しやすいというケースも今後出てくるかもしれない。開発的にコストをかけたくないところなので、ここに依存して全体のパッケージ構成を変えるなどもあまりしたくないところ。

## 4.1. Steam Overlay 問題

解決できていないので、調べたメモだけ。

### 4.1.1. Construct 3 での知見

Phaser と同様の HTML5 を利用するゲームエンジンに [Construct 3](https://www.construct.net/en) がある。ブラウザ上の IDE 提供や有償プランもある大きめのプロダクトで、プロジェクトの export をする時に Windows 向けとして [NW.js をターゲットにすることもでき](https://www.construct.net/en/tutorials/using-greenworks-steam-2853)、チェックボックスには Steam 対応というオプションもあるようだ。

https://www.construct.net/en/make-games/manuals/construct-3/interface/dialogs/nw-js-options

> Export for Steam
> Change the configuration to improve compatibility with Steam. This sets the command line options --in-process-gpu and --disable-windows10-custom-titlebar, and also forces the window to constantly redraw to improve compatibility with the Steam Overlay.

↑内部的には例の `--in-process-gpu` やらをつける挙動になるらしい。(有料版でしか使えない機能なので未検証)

https://www.construct.net/en/forum/construct-3/general-discussion-7/steam-overlay-broken-179536

Construct 3 の Steam 周りの対応をメンテナンスしてきたらしい方が上記スレッドでレポートしているが、最新版の完全な対応はまだできておらず、「最も安定して動く NW.js 0.72.0 で動かすしかない」という感じで、NW.js の古いバージョンを利用する案を進めている。NW.js [0.72.0](https://nwjs.io/blog/v0.72.0/) は Chromium 109 + Node 18.9.1 で 2023/01/14 にリリースされていて、Greenworks のリリースが止まっていた期間 (2018/11 ~ 2023/11) のため該当 Node 向けバイナリは公式に無い状態。ので、[元記事](https://qiita.com/laineus/items/f8a9f908587d9d9e8087) にリンクがある＆この方もスレッドで言及している

https://greenworks-prebuilds.armaldio.xyz/?runtime=nw.js&version=v111&tag=v0.8.0%20-%20Steamworks%20v1.58a

という Greenworks の該当 NW.js カスタムビルドのリンクから該当バージョンのバイナリを落とす必要がありそう。

試しに動かそうと思い、スレッドを参考に `nwjs-sdk-v0.72.0-win-x64` + `steamworks_sdk_158a` + `greenworks-v0.15.0-nw-v0.82.0-win-64` (の、`greenworks-win64.node` をダウンロードしたファイルに変えた版) を動かしてみたが、自分の環境だと Overlay しないというかクライアントが急に落ちたり、1. ~ 3. 項で試した NW.js 0.89 版より不安定になったのであまり検証できていない。

### 4.1.2. Overlay 問題で動かない部分

試してみて気付いたのは、Overlay がうまくいってない状態であっても右下から実績解除のポップアップなど出すことはできるので、ゲームによってはまぁなくてもなんとかなるかな… といった感じ。