【GameMaker:Studio2】ミニゲームチュートリアル (GML)

今回はGMLを使ってシンプルなミニゲームを作成します。

# プロジェクトの作成
プロジェクトを新規作成します。
今回は GML を使うので、`GameMaker Language` を選択します。
<img width="441" alt="001.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/9643c94c-fdac-e9a3-f490-63589e005cdc.png">

プロジェクト名は何でも構いません。

# 敵オブジェクトの作成
敵オブジェクトを作成します。
オブジェクト名は `objEnemy` として、スプライトを作成します。
<img width="257" alt="002.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/45930227-251f-9f00-d637-e97d9cb99e0e.png">

スプライト名は、 `sprEnemy` として、`Import` ボタンをクリックして、以下の画像をインポートします。
![tako.png](https://qiita-image-store.s3.amazonaws.com/0/43772/4499836e-19d9-8575-b3ee-c90aa630fdc0.png)
(右クリックで "名前をつけて保存" で "tako.png" として保存する)

<img width="429" alt="003.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/bbedac27-25e8-f98a-28cb-57d6d8150c4d.png">

画像を設定したら、`objEnemy` に戻って、`Add Event` をクリックして、`Create` イベントを作成します。
<img width="623" alt="004.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/e22468d1-ba7e-28d4-2512-dc6ac2989267.png">

スクリプトには以下を記述します。

```js:Createイベント
hspeed = random_range(-10, 10);
vspeed = random_range(-10, 10);
```

このスクリプトは `hspeed` / `vspeed` それぞれに -10〜10 のランダムな値を設定します。
これによりランダムな方向に移動するようになります。

# 敵オブジェクトをルームに配置する
room0 を開いて、たこ焼きを配置します。
<img width="742" alt="005.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/3d505e0a-baf7-11e0-9d64-7e6a8ba165b6.png">

場所はどこに置いても問題ありません。

では、実行して動作を確認します。
実行すると、たこ焼きが移動して画面外に出て行ってしまいます。

# たこ焼きを画面外に出ないようにする
たこ焼きが画面外に出ないようにします。
`objEnemy` を開いて、`Step` イベントを作成します。
<img width="536" alt="006.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/ad59cc3f-f2e9-49ef-8e07-9de9815fe7af.png">

スクリプトは以下のように記述します

```js:Stepイベント
if(x < 0) {
	// 左側に衝突
	x = 0;
	hspeed = -hspeed;
}
if(y < 0) {
	// 上に衝突
	y = 0;
	vspeed = -vspeed;
}
if(x > room_width) { // room_width はルームの幅
	// 右側に衝突
	x = room_width;
	hspeed = -hspeed;
}
if(y > room_height) { // room_height はルームの高さ
	// 下の衝突
	y = room_height;
	vspeed = -vspeed;
}
```

それぞれ画面の端まで移動したら、移動量を反転させることで跳ね返り移動を実装しています。
<img width="574" alt="007.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/95ece004-335f-7220-08cc-895ac324a63f.png">

GameMakerの座標系では、左上が  `(x, y) = (0, 0)` となっており、右端がルームの幅である `room_width` 下側がルームの高さである `room_height` を使って画面端に移動したかどうかを判定しています。

実行して動作を確認すると、たこ焼きが画面端でバウンドするようになりました。
ただ、右と下の跳ね返りが少しおかしな動きをしています。
この原因は `sprEnemy` の原点(基準となる座標) が左上となっているためです。
`sprEnemy` を開いてみると、`Origin` の値が `0 x 0` となっていて、左上が原点になっていることが確認できます。
<img width="385" alt="008.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/cfd2e4df-b652-799e-dc96-36e7429411e9.png">

右側や下側のぶつかりを正しい見た目になるように修正します。
`objEnemy` の `Step` イベントに記述したスクリプトを以下のように修正します。

```js:Stepイベント
if(x < 0) {
	// 左側に衝突
	x = 0;
	hspeed = -hspeed;
}
if(y < 0) {
	// 上に衝突
	y = 0;
	vspeed = -vspeed;
}
if(x > room_width-sprite_width) { // room_width はルームの幅
	// 右側に衝突
	x = room_width-sprite_width;
	hspeed = -hspeed;
}
if(y > room_height-sprite_height) { // room_height はルームの高さ
	// 下の衝突
	y = room_height-sprite_height;
	vspeed = -vspeed;
}
```

右側を `sprite_width` 下側を `sprite_height` ずらしています。
`sprite_width` はスプライトの横幅、`sprite_height` はスプライトの高さとなります。
<img width="495" alt="009.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/49ee3120-31e0-d7fe-a724-e066d711ee3a.png">

実行して正しく右側と下側で跳ね返るのを確認します。

# たこ焼きをクリックすると消すようにする
たこ焼きをクリックすると消えるようにします。
`objEnemy` を開いて、`Add Event > Mouse > Left Pressed` イベントを作成します。
<img width="538" alt="010.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/da17a850-4b6e-bd6d-9d01-960683c5db6b.png">

スクリプトは以下のように入力します。

```Js:LeftPressedイベント
instance_destroy();
```

実行すると、マウスクリックでたこ焼きが消えるようになります。
消えるエフェクトを入れます。
`objEnemy` を開いて、`Add Event > Destroy` イベントを作成します。

<img width="547" alt="Test010_-_GameMaker_Studio_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/a4ac7333-7de9-df4f-206c-6b4a7e524af9.png">

スクリプトは以下のように記述します。

```Js:LeftPressedイベント
effect_create_above(ef_explosion, x, y, 1, c_white);
```

実行してたこ焼きをクリックするとエフェクトが発生しますが、少しズレが起きています。
これは、たこ焼きの原点が左上なので、左上中心にエフェクトが発生しているためとなります。
<img width="451" alt="名称未設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/90bfb299-2b5e-0394-01d0-a7a8a22f251c.png">

これを修正するには、たこ焼きの幅の半分の位置、高さの半分の位置にエフェクトの発生位置をずらす必要があります。
<img width="533" alt="名称未設定.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/a7f7236b-5b81-7820-a69a-317a8087daa0.png">

`objEnemy` の `Destroy` イベントを開いてスクリプトを以下のように修正します。

```Js:Destroyイベント
effect_create_above(ef_explosion, x+sprite_width/2, y+sprite_height/2, 1, c_white);
```

実行すると正しい位置にエフェクトが発生するようになります。

# ゲームクリア判定を実装する
最後にゲームクリア判定を実装します。
オブジェクトを作成し、名前を `objGame` とします。
<img width="176" alt="020.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/4476f5ac-7b78-205e-880c-1e4e81fb2f75.png">

続けて、`Add Event > Step > Step` イベントを作成します。
<img width="504" alt="021.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/dd4753ee-dda3-a445-5fec-6baf104c62a1.png">

スクリプトは以下のように記述します。

```js:objGame>Stepイベント
if(instance_exists(objEnemy) == false) {
	show_message("GAME CLEAR");
	game_end(); // ゲーム終了
}
```

これは、objEnemy(たこ焼き)が全て消滅したら、”GAME CLEAR” というメッセージダイアログを表示した後、ゲームを終了させる記述となります。

では、この objGame を room0 に配置します。
<img width="423" alt="022.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/26525c5f-75f0-d19c-a5b7-718013ba60be.png">

objGameをルームに配置したら、実行して動作を確認します。
たこ焼きを全部クリックすると、”GAME CLEAR” というダイアログが表示されゲームが終了します。
<img width="524" alt="023.png" src="https://qiita-image-store.s3.amazonaws.com/0/43772/394a011f-0862-196f-467a-7d133ab81c1d.png">