【GameMaker:Studio2】重力反転アクションゲームのチュートリアル

# 始めに
今回はシンプルなアクションゲームとして、重力を反転するアクションゲームを作ります。

# 素材のダウンロード
素材は以下のURLからダウンロードできます。
http://syun777.sakura.ne.jp/tmp/gms2/gms2_gravity.zip

# プロジェクトの新規作成
新規プロジェクトを作成します。
プロジェクト名は「gravity」としておきます。
![031.png](https://qiita-image-store.s3.amazonaws.com/0/43772/e836bda2-0eec-970b-b724-9a9093476500.png)

# フレームレートの変更
メニューバーの近くにある、歯車のアイコンをクリックして、Game Optionを開きます。
![039.png](https://qiita-image-store.s3.amazonaws.com/0/43772/bbdaf633-29e4-339f-3af9-2995412adc2f.png)
Main Options > General > Game frames per second の値を 60 に変更してきます。

# プレイヤーの作成
プレイヤーを作成します。
新規にオブジェクトを作成して、Nameは「objPlayer」とします。
オブジェクトのスプライトを作成し、Nameは「sprPlayer」とします。
スプライト画像は「player_strip4.png」をインポートします。
![032.png](https://qiita-image-store.s3.amazonaws.com/0/43772/bab6baae-67f3-1ffe-1c99-8ad4710626c7.png)

Createイベントを作成します。
アクションは「Set Gravity Force」と「Set Friction」を配置します。

* Set Gravity Force
 * Force: 0.4
* Set Friction
 * Friction: 0.3

![033.png](https://qiita-image-store.s3.amazonaws.com/0/43772/5adb19a5-5652-5d6f-70db-168312af45f0.png)

Draw - Drawイベントを作成します。
そして「Draw Sprite」アクションを配置します。

* Sprite: sprPlayer
* X: 0 (Relativeにチェックを入れる)
* Y: 0 (Relativeにチェックを入れる)
* Frame: 0

![034.png](https://qiita-image-store.s3.amazonaws.com/0/43772/f0b45619-e6f6-7a15-cbd4-2f80487fe142.png)

これは、スプライトを描画するアクションとなります。
スプライトの 0 フレームのみを描画したいので、このようにしました。
なおフレームとはスプライトで取り込んだ画像のコマ数のことです。
今回使用している画像は合計 4フレーム存在しています。
![035.png](https://qiita-image-store.s3.amazonaws.com/0/43772/0cbc517f-334d-8341-6338-c3060846c5d1.png)

では、room0 を開いて、objPlayer を配置します。
開始すると、プレイヤーが落下していくことを確認できます。

ルームのサイズですが、1024x768はやや広いので、`800 x 480` に変更しておきます。
![036.png](https://qiita-image-store.s3.amazonaws.com/0/43772/7f6e08da-9860-d9b6-5680-9aa7ed73f099.png)

なお、ルームのグリッドの線が薄くてよく見えない場合は、
メニューの File > Preference から、Room Editor > Grid Settings > Grid line colour
で線の色を変えることができます。
![037.png](https://qiita-image-store.s3.amazonaws.com/0/43772/7463f255-c0ed-70c9-3ae5-43c9210edd9d.png)

# 重力反転アクションの実装
Spaceキーをおしたら、重力を判定するようにします。
objPlayerを開いて、Key Press - Spaceイベントを作成します。
アクションは、「Set Gravity Force」「Set Speed」「Do Effect」を配置します

* Set Gravity Forceアクションを配置
    * Force: gravity * -1
* Set Speedアクションを配置
    * Type: Vertical
    * Speed: 0
* Do Effectアクションを配置
    * Type: Ring
    * Where: Below Objects
    * Size: Small
    * X: 16 (Relativeにチェックを入れる)
    * Y: 16 (Relativeにチェックを入れる)

![040.png](https://qiita-image-store.s3.amazonaws.com/0/43772/73d82a2e-cea2-7a21-ddc7-a23ce39b07d6.png)

これは、

1. 重力 (gravity) に -1 をかけることで重力を逆にして
2. 縦方向の速度を 0 にして
3. エフェクトを発生する

という処理になります。
実行すると、Spaceキーを押す度に重力方向が逆になります。

# 画面外に出たらリスタートするようにする
プレイヤーが画面外に出てもそのままゲームが続いてしまうので、ゲームをリスタートするようにしてみます。
objPlayerを開き、Outside Roomイベントを作成します。
アクションには「Restart Game」を配置します。
![041.png](https://qiita-image-store.s3.amazonaws.com/0/43772/16b8f3f7-362d-acc4-25fc-96018aaba81c.png)

実行すると、プレイヤーが画面外に出ると自動的にリスタートします。

# 重力反転時にプレイヤーのスプライトを変える
重力反転時に、プレイヤーのスプライト画像も逆さまになるようにして、落下方向をわかりやすくします。
objPlayerの Drawイベントを開き、「If Variable」「Else」アクションを配置します。

* If Variableアクションを配置
    * Variable: gravity
    * Not: チェックを入れない
    * Is: Greater
    * Value: 0
    * Draw Spriteアクションをぶら下げる
* Elseアクションを配置
    * Draw Spriteアクションを配置
        * Sprite: sprPlayer
        * X: 0 (Relativeにチェックを入れる)
        * Y: 0 (Relativeにチェックを入れる)
        * Frame: 2

![042.png](https://qiita-image-store.s3.amazonaws.com/0/43772/41158bb3-0b10-f85f-b0a3-d7932aadff48.png)

「If Variable」アクションの下に作成済みの「Draw Sprite」アクションをぶら下げ、
「Else」アクションの下に新しく作成した「Draw Sprite」アクションをぶら下げる形になります。

なお、それぞれ、「Empty」の文字のところにドラッグ＆ドロップして、下から線がつながるようにしてください。
実行すると、Spaceキーで重力を反転する度に、プレイヤーのスプライトの上下がひっくり返るようになります。

# 左右に動かせるようにする
プレイヤーを左右に動かせるようにします。
まず、objPlayerの「Variable Definitions」をクリックします。
![043.png](https://qiita-image-store.s3.amazonaws.com/0/43772/bebb4c86-2226-a727-a805-be877db6e8d8.png)

Add をクリックして、以下のように設定します。

* Name: is_left
* Default: 0
* Type: Integer

`is_left` は左側を向いているかどうかの変数となります。
![044.png](https://qiita-image-store.s3.amazonaws.com/0/43772/4143c12a-412e-2c81-a3db-b11be94cbd7c.png)

左右移動のイベントを作成します。
まずは、左移動の判定をするために、Key Down - Leftイベントを作成します。
アクションには、「Set Speed」「Assign Variable」を配置します。

* Set Speedアクションを配置
    * Type: Horizontal
    * Speed: -4 (Relativeにチェックを入れない)
* Assign Variableアクションを配置
    * Name: is_left
    * Value: 1 (Relativeにチェックを入れない)

![045.png](https://qiita-image-store.s3.amazonaws.com/0/43772/25a36dbd-a1f7-cef3-48b4-fc9dfdc987ce.png)

変数「`is_left`」に1を入れることで、左を向いたことを保存しています。

次に、右移動判定用に、Key Down - Rightイベントを作成します。

* Set Speedアクションを配置
    * Type: Horizontal
    * Speed: 4 (Relativeにチェックを入れない)
* Assign Variable
    * Name: is_left
    * Value: 0 (Relativeにチェックを入れない)

![046.png](https://qiita-image-store.s3.amazonaws.com/0/43772/554bb442-53e4-7aba-e592-ea8d0617f1af.png)

Drawイベントを開いて、Draw Spriteのパラメータを修正します。

* 1つめのDraw Sprite
    * Frame: is_left
* 2つ目のDraw Sprite
    * Frame: 2 + is_left

![047.png](https://qiita-image-store.s3.amazonaws.com/0/43772/90eb4343-1698-b89b-3623-1b96e45cba93.png)

実行すると左右移動できるようになり、移動方向に合わせてスプライトが左右に反転するようになります。

# 床を作成する
プレイヤーが設置できる床オブジェクトを作成します。
オブジェクトを作成し、Nameは `objFloor` とします。

![049.png](https://qiita-image-store.s3.amazonaws.com/0/43772/220549e6-aae2-83a5-ab06-38576fbecf29.png)

続けてスプライトを作成し、Nameは `sprFloor` とし、`floor.png` をインポートします。
![050.png](https://qiita-image-store.s3.amazonaws.com/0/43772/ee90add1-ff62-b14a-17c3-2ab7a1c9ad58.png)
![051.png](https://qiita-image-store.s3.amazonaws.com/0/43772/e4619766-7341-156c-5476-21f62545a8fe.png)

`objPlayer` を開いて床との当たり判定を作成します。
`Add Event > Collision > objFloor` で、床オブジェクトとの当たり判定イベントを作成します。

床との当たり判定は以下のルールで行います。

1. 重力が下向きであれば、上方向へ少し跳ね返る
2. 重力が上向きであれば、下方向へ少し跳ね返る

まずは「If Variable」アクションを配置します。
設定は以下のとおりです。

* Variable: gravity
* Not: チェックを入れない
* Is: Greater
* Value: 0

![052.png](https://qiita-image-store.s3.amazonaws.com/0/43772/f4295f70-f59c-9dd7-a066-9727dea049af.png)

これは、重力を表す変数「gravity」が 0 より大きければ、というルールを定義するものです。
これに対して、「Set Speed」アクションを配置します。
設定値は以下のとおりです。

* Type: Vertical
* Speed: -10

![053.png](https://qiita-image-store.s3.amazonaws.com/0/43772/695d957f-d81c-77be-d824-5ab575c7d68c.png)

「If Variable」の "Empty" のところにドラッグ＆ドロップします。

続けて、「Else」アクションを配置します。
これは、「If Variable」が成立しなかった際に実行される条件となります。
![054.png](https://qiita-image-store.s3.amazonaws.com/0/43772/d8b79fc9-e68e-7006-3a9c-4ce78fb4b592.png)

これにも「Set Speed」アクションをぶら下げます。

* Type: Vertical
* Speed: 10

![055.png](https://qiita-image-store.s3.amazonaws.com/0/43772/c788340e-c746-b97e-5910-3f050383c4c5.png)

重力が逆さだった場合の処理なので、下方向に跳ね返るアクションとなります。

最後に、当たった床を消したいので、「Destroy Instance」アクションを配置します。
一番下に配置すると、「Else」アクションにぶら下がってしまうので、一番上に配置しました。
![056.png](https://qiita-image-store.s3.amazonaws.com/0/43772/53189ae1-2164-f3d5-b72a-1d074453ee3c.png)

「Else」にぶら下げてしまうと、重力が反転した状態でしか、床が消滅しないので注意してください。
一番下に配置したい場合は、「Else」アクションのすぐ下にドラッグ＆ドロップすると、「Else」の条件とは関係なく発生するようになります。

「Destroy Instance」アクションですが、このままだと、objPlayer自身を消滅させる処理となります。
アクションにある▼をクリックして対象を変更します。
![057.png](https://qiita-image-store.s3.amazonaws.com/0/43772/7eef698c-ad7b-62fc-7c0e-862b7b098e9c.png)

表示されたウィンドウから「other」を選択します。
![058.png](https://qiita-image-store.s3.amazonaws.com/0/43772/bb3e50f6-2548-8ce5-1574-21ca9ed2cfb8.png)

「other」というのは、衝突イベント内で外からやってきた他人のオブジェクトとなります。
それに反して「self」というのは自分自身 (ここでは objPlayer) となります。
また、「objFloor」を選ぶと、存在する「objFloor」をすべて消滅させてしまうので、これを選ばないように注意してください。

では、room内に objFloorを配置します。
![059.png](https://qiita-image-store.s3.amazonaws.com/0/43772/71f485c0-d8c1-4c72-88aa-a27cfb40c0cd.png)

テスト用なので、どんな配置でも問題ありません。
「Alt」キーを押しながら、左ドラッグで複数のオブジェクトをまとめて配置できるので、この方法で配置すると楽にできます。

では実行して動作を確認します。
下方向への落下中に床に当たると上に飛び跳ね、上方向への上昇中に床に当たると下に押し返される動きとなります。

# 反転に制限を入れる
無制限に重力反転できてしまうと、自由に空中浮遊できてしまうので、反転を制限するルールを追加します。

* 反転できるのは1回のみ
* ただし床に接触すると、再び反転できる

反転できるかどうかの変数を追加します。
`objPlayer` を開いて、「Variable Definitions」をクリックして、以下の変数を追加します。

* Name: can_reverse
* Default: True
* Type: Boolean

![060.png](https://qiita-image-store.s3.amazonaws.com/0/43772/74aa8bec-61af-cb45-1f4b-f3049e8dc33a.png)

これは、変数「can_reverse」を追加し、取り得る値を「真偽値(Boolean)」としています。
「真偽値」とは「真(true)」または「偽(false)」の2つのみを取る値となります。いわゆる「フラグ」というものに該当します。フラグが立ったというのは、この値が「true」になり、なんらかの条件を満たした、ということです。
ここでは、初期値(Default)を true にして、最初は反転できる、ということにしています。

これを使って、重力反転に制限を加えます。Key Press - Spaceイベントを開いて、「If Variable」アクションを追加します。

* Variable: can_reverse
* Not: チェックを入れない
* Is: Equal
* Value: true

![061.png](https://qiita-image-store.s3.amazonaws.com/0/43772/6f5e707d-5611-0980-caa1-d76f4b63e6cf.png)

「Set Gravity Force」「Set Speed」「Do Effect」それぞれをの「If Variable」にぶら下げます。これにより、"can_reverse" が trueのときのみ重力反転できるようになります。
ただ、これだけではフラグは下がらないので、フラグを下げるアクションを配置します。

* Name: can_reverse
* Value: false (Relativeにチェックを入れない)

![062.png](https://qiita-image-store.s3.amazonaws.com/0/43772/c883e21c-b382-04cc-8a65-20cfec7738df.png)

「If Variable」と「Set Gravity Force」の間に入れます。
実行すると、重力反転が一度のみできるようになっています。

続けて、objFloorに衝突したときの挙動を修正します。
objPlayerの Collision - objFloor イベントを開き、「Assign Variable」アクションを追加します。

* Name: can_reverse
* Value: true (Relativeにチェックを入れない)

![063.png](https://qiita-image-store.s3.amazonaws.com/0/43772/025ee308-d4a1-30f0-dfdc-38a47c410e29.png)

これで床に接触したら、重力反転が再びできるようになります。
実行して、動作を確認しましょう。

# これから先は
ここまでできれば、あとは障害物やゴールを作ることでゲームが完成します。
今までの応用で実装できるのでよかったら挑戦してみてください。

* 障害物を作る
 * objSpikeを作成
 * sprSpikeを作成
 * objPlayer > Collision > objSpikeを作成 → Restart Gameアクションを配置
* ゴールを作る
 * objGoalを作成
 * sprGoalを作成
 * objPlayer > Collision > objGoalを作成 → Exit Gameアクションを配置