【GameMaker:Studio2】人生が変わる拡張機能「GMLive」の紹介

現在、[GameMaker Studio2日本語化プロジェクト](https://paratranz.cn/projects/1100) に関わっているのですが、その Discord で「**これは人生が変わる！**」と話題に上がった「GMLive」のついての紹介となります。

# GMLiveとは

> GMLiveは、GameMaker StudioおよびGameMaker Studio 2（Mac IDEを含む）にライブコーディング/インタラクティブプログラミングを導入する拡張機能です。

> これは選択したスクリプトやイベントが変更・保存したときに、再コンパイルして再起動する必要なく、ゲームの途中で再読み込みすることができます。（ホットリロード）

> これにより、特にコンパイル時間の長いプロジェクトやプラットフォームでは、開発期間を大幅に短縮できます。

> 拡張機能はすべてのモジュールで動作します。ブラウザ、モバイルデバイス、またはコンソールでゲームを実行しながら、リモートでコードを更新することもできます。

* 公式ページ
 * https://yellowafterlife.itch.io/gamemaker-live

GameMakerでホットリロードを実装するには、通常は `Included Files` にパラメータを逃して再読み込みする機能を実装する必要があるのですが、この拡張機能を使用すると、スクリプトやスプライトといったリソースもリロードできてしまう……という素晴らしいものです。……これは、試してみないと……！！

# 導入手順
ということで導入手順です。
まずは [GMLiveの公式サイト](https://yellowafterlife.itch.io/gamemaker-live) に移動して、購入を行う必要があります。

![GMLive_gml_by_YellowAfterlife.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/1ca52cb4-3c38-effb-3275-bac795c857d2.png)

$29.95 (≒¥3,300くらい) とそれなりのお値段ですが、物は試しということで購入。
なお、支払いはクレジットカードとPaypalが使用可能です。

## ダウンロード

購入をすると自動でダウンロード画面に遷移します。またメールアドレスにも同じURLが送られるので、そこから開くこともできます。

![Download_GMLive_gml_by_YellowAfterlife_-_itch_io.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/9440f5c5-a116-ca43-f30d-172ab207d38a.png)

GameMaker Studio 2であれば、赤線で囲んだいずれかのファイルをダウンロードします。

* **GMLiveForGMS2_and_catch_error.yymp**: "catch_error" という拡張機能を使っている場合
* **GMLiveForGMS2_pre_2_2_4.yymp**:GameMaker Studio2 v2.2.4以下を使用している場合 (v2.1.5まで利用可能)
* **GMLiveForGMS2.yymp**: 上記に該当しない場合

現時点 (2020/5/16) の最新バージョンは "v2.2.5" なので最新版を使用していれば、`GMLiveForGMS2.yymp` で問題ないです。

## 初期セットアップ
[GMLiveの公式ドキュメント](https://yal.cc/r/17/gmlive/) には以下の手順でセットアップを行うように書かれています。

1. GMLiveアセットをプロジェクトにインポートします。（itch.ioでは、.yympファイルをワークスペース領域にドラッグする必要があります）
2. パッケージからアセット（Extension、Object、Included Files）をインポートします
3. Macでは、Windows固有のexeおよびndllファイルのインポートを省略します。
プロジェクトの最初の部屋にobj_gmliveを配置します

ということで、まずは *.yymp ファイルをプロジェクトにドラッグ＆ドロップします。
![スクリーンショット_2020_05_16_9_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/1450d060-f63f-3f83-6de3-8c0b8deb70e7.png)

するとインポート確認ダイアログが表示されるので、YESボタンを押してインポートします。
![GMLiveTest_-_GameMaker_Studio_2_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/c149f0a3-ed60-823c-9f70-5e63c4ab019b.png)

次にインポートするファイル選択ダイアログが表示されるので、`Add All` ボタンをクリックして "Resources to Import" にインポート対象ファイルを移動させて、"import"ボタンをクリックします。
![GMLiveTest_-_GameMaker_Studio_2_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/c77d9f74-8eda-cdcb-5017-bcc9db60bf93.png)

すると、Objectに `obj_gmlive`、Included Filesの `GMLiveフォルダ` に GMLive 関連の実行ファイルとDLL、Extensionsに `GMLive` が追加されます。
![GMLiveTest_-_GameMaker_Studio_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/7ee14996-add4-78e5-3778-c652e89a5b59.png)

（※なお、macOS環境では *.exe / *.ndll ファイルが不要となりますが、インポートしても問題なく動作します）

## スタートアップ
プロジェクトの設定は公式ドキュメントには以下のように書かれています。

1. Included Filesの `gmlive-server.exe` を右クリックし、[Open in Explorer]を選んでエクスプローラーを開きます（Macでは「Open in Finder」）
2. Windowsでは、`gmlive-server.exe` をダブルクリックします。
Macでは、ディレクトリでターミナルを開き、`neko gmlive-server.n` を実行します。（詳細については、[gmlive-server](https://yal.cc/r/17/gmlive/#gmlive-server) を参照してください）
ゲームを再コンパイルするときは、実行したままにすることができます。新しいゲームインスタンスが接続するたびにプロジェクトディレクトリが再スキャンされます
3. 目的のスクリプトとイベントにGMLive関数呼び出しを追加します。
イベントはGMLタイプのみ使用可能です（GMLブロックのDnDではありません）
4. ゲームを実行します
5. 「ライブ」スクリプト/イベントを変更して、ゲーム内の変化を確認します（すべてが正しく行われた場合）。 gmlive-serverのウィンドウがステータスの更新を報告します

ということでこの手順に従って実行していきます。

まずは GMLiveサーバーの実行から。
Included Filesの GMLive を右クリックしてコンテキストメニューを表示します。
![GMLiveTest_-_GameMaker_Studio_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/f7b3fea8-400f-901d-de66-3877110ddfee.png)

macOS環境なので表示が異なっていますが、Windowsでも一番下の項目に「Open in Explorer」があるはず……

つぎに `gmlive-server.exe` をダブルクリックで実行。macOSの場合はターミナルから `neko gmlive-server.n` を実行します

![neko.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/34d4dcf0-b85e-b654-49fc-bc98ec16ba4f.png)
 
5100番ポートを使用してプロセス間通信するような感じです。
この画面を終了するとサーバーが停止してしまうので、起動したままにします。

次にテスト用のオブジェクトの作成。テスト用なので緑色の矩形を表示するだけのオブジェクトを `obj_game` として作りました。
![GMLiveTest_-_GameMaker_Studio_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/e683a949-2c8c-1cf9-142e-dc08b640b125.png)

次にイベントの作成。テスト用に Stepイベントを作成……
![GMLiveTest_-_GameMaker_Studio_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/9e8fc7f5-3f40-cdad-e136-1b2d9d57c56c.png)

コードを以下のように記述します。

```js:obj_gameのStepイベント
if(live_call()) return live_result;

x = 100;
y = 100;
```

このオブジェクト(obj_game)と `obj_gmlive` をルームに配置します。
![GMLiveTest_-_GameMaker_Studio_2_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/8fa20988-dbb2-a960-d30a-9b6a28e0dffe.png)

そして実行……。
![reload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/f240b919-a841-c3c1-ea52-6f51ad12c48a.gif)

……

……！？

スクリプトの修正がリアルタイムに反映……？

！！……！？

こ、これは……！？

![surprise.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/403447d1-9dec-4d02-848e-b842a2c96762.png)

これは人生変わる……！


あと未検証ですが、`obj_gmlive` は persistant なので、起動時のルームのみに配置すればよさそうな感じです。

## ルームのリロードを行う方法

以下の動画で手順が紹介されています。

* Using room reloading in GMLive
 * https://www.youtube.com/watch?v=_35-xnHQE54&feature=emb_logo

1. 空のルームを作成 (rm_live) 。レイヤー情報はすべて削除
2. 空のオブジェクトを作成 (obj_blank)
3. ルームリロード用のスクリプトを作成 (scr_room_update)して以下のように記述する

```js:scr_room_update
room_goto_live(argument0);
```

４. リロードしたいルームに各種オブジェクトと `obj_gmlive` を配置
５. `obj_gmlive` を開いて Createイベントに以下のコードを記述する

```js:obj_gmliveのCreateイベント
/// @description Don't forget to place this object in the first room!
if (instance_number(obj_gmlive) > 1) {
	var first = instance_find(obj_gmlive, 0);
	if (id != first) { instance_destroy(); exit; }
}
#macro live_enabled 1

// a workaround for a GM bug where extension doesn't initialize in debug mode:
if (!variable_global_exists("g_live_log_script")) live_preinit();

// change IP/port here if you connect to remote gmlive-server:
live_init(1, "http://localhost:5100", "");

// ------------------------------------
// 以下を追加
live_blank_object = obj_blank;
live_blank_room   = rm_live;
live_room_updated = scr_room_update;
room_set_live(room1, true); // リロードを行うルームを設定
```

これで実行すると、ルームでのオブジェクト配置を変更して保存すると、リアルタイムで位置が更新されます。
![room_reload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/6fb86416-1cd7-8d8f-47d4-c1fac11ce310.gif)