【GameMaker:Studio2】メッシュ変形による疑似3D背景の作り方

[GOING BEYOND 2D WITH SIMPLE TRICKS (AND USING 2D TRICKS IN 3D)](https://www.yoyogames.com/blog/511/going-beyond-2d-with-simple-tricks-and-using-2d-tri)という、Jet Lancerで使われた疑似3D技術の紹介を参考に作ってみました。

![100.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/d9a10c2d-f4a2-859f-097b-18e636ccc920.gif)

こんな感じの疑似3D背景が作れるようになります。

# Draw Sprite Pos Fixed を購入する

ここで少し残念なお知らせが……。GameMaker標準関数だとメッシュ変形処理に問題があるので、独自にメッシュ変形処理を作るか、有料アセットを使う必要があります。

有料アセットを使う場合は [Draw Sprite Pos Fixed](https://marketplace.yoyogames.com/assets/4720/draw-sprite-pos-fixed) を購入します。

問題点というのは、GameMakerには [draw_sprite_pos()](https://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/drawing/sprites_and_tiles/draw_sprite_pos.html) というメッシュ変形の関数があるのですが、これを使って変形するとテクスチャが真ん中でパキッと折れてしまいます。

![020.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/e355070f-70e8-e999-8aa2-d41d7bc0923d.gif)

有料アセットが左側で、右が標準関数です。左側の FIXED はスムーズに変形できていますが、右側の STANDARD は真ん中で折れてしまっています。

購入する場合は以下のページから

* https://marketplace.yoyogames.com/assets/4720/draw-sprite-pos-fixed

<img width="613" alt="008.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/f4b03a92-08f2-a97d-7afd-45296ce0df2e.png">

$1.99 (約230円) で購入可能です。
購入後は、GameMakerで新規プロジェクトを作成し、メニューから Marketplace > My Library でライブラリを開きます。

<img width="419" alt="009.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/278e1772-1a38-6551-a576-f6f104d260e4.png">

ライブラリが表示されたら「更新」ボタンをクリック。

<img width="716" alt="010.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/65bd250c-ae89-138c-2a81-454c3e4f486e.png">

次にダウンロードをクリック。

<img width="542" alt="011.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/b81a9e38-505d-64b4-72fc-649d569fc55a.png">

すると、インポートボタンが押せるようになるのでこれをクリック。

<img width="526" alt="012.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/73c8e53a-9102-75c0-f2c2-ff8d8cef01b0.png">

「Add All」をクリックして、Import Resources に各種リソースを追加して、「Import」ボタンでプロジェクト内にインポートします。

<img width="562" alt="013.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/5305e65a-5366-d2a1-5c59-6729a9aa0a54.png">

そしてインポートしたらまずは動作確認……。「r_test」ルームを一番上に持ってきます。

<img width="444" alt="017.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/1b3276e1-d775-4a3b-6c18-2786d18163bd.png">

そして実行するとメッシュ変形の動作チェックができます。

![020.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/b99d158f-2eea-d735-d46f-7491dd737741.gif)

確認できたら次は、メッシュ変形させる画像スプライトを追加。今回は「ぴぽや」様の背景画像を使用します。

![ScreenShot_2018_0913_20_56_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/10a43ac5-a42d-49b0-5762-3b8f85bc46f3.png)
https://pipoya.net/blog/pipoyamapchipassets_working/

この画像を「sp_bg」スプライトとして登録します。

<img width="494" alt="Trick3D_-_GameMaker_Studio_2_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/71944e38-d9aa-b48e-ab00-9a601f1df288.png">

そして、「obj_Bg」オブジェクトを作成。
Createイベントに以下のGMLを追加します。

```js:Createイベント
vertex_format_begin();
vertex_format_add_colour();
vertex_format_add_position();
vertex_format_add_normal();
global.format_perspective = vertex_format_end();
```

これは draw_sprite_pos_fixed() を使用するのに必要な頂点フォーマットとなります。

そして Drawイベントに以下のコードを追加

```js:Drawイベント
// First we calculate some offsets for the background with the mouse as focus point.
// 最初に、マウスをフォーカスポイントとして、背景のオフセットをいくつか計算します。
// xの移動範囲は (-0.5〜0.5) * room_width * 2 → (-room_width〜room_width)
// xの移動値の基準は画面中心からの距離
off_x = (1 - mouse_x / room_width - 0.5) * room_width * 2;
// yの移動範囲は (0.0〜1.0) * room_height/2 → (0〜room_height/2) ※画面上部にいるほど大きな値になる
// yの移動値は画面上から離れているほど、上に移動する
off_y = (1 - mouse_y / room_height) * room_height/2;

// Then we set the y-positions for the horizon and ground using those offsets.
// 次に、これらのオフセットを使用して地平線と地面のy位置を設定します。
// 地平線は2倍の速さで上下に移動する
horizon_y = room_height / 4 + off_y * 2;
ground_y = room_height + off_y;

// Now we do the same for x for each corner.
// 各コーナーのxについて同じことを行います。
// 地平線はルームサイズ固定
h_x1 = 0;
h_x2 = room_width;
// 地面はルームサイズx3
g_x1 = off_x - room_width;
g_x2 = off_x + room_width * 2;

// Lastly we draw our background sprite stretched to those 4 points.
// 最後に、これらの4ポイントに引き伸ばされた背景スプライトを描画します。
draw_sprite_pos_fixed(sp_bg, 0, h_x1, horizon_y, h_x2, horizon_y, g_x2, ground_y, g_x1, ground_y, c_white, 1);


// Draw debug.
text_x = 8;
text_y = 8;
text_dy = 16;
draw_text(text_x, text_y, "off_x: " + string(off_x)); text_y += text_dy;
draw_text(text_x, text_y, "off_y: " + string(off_y)); text_y += text_dy;
draw_text(text_x, text_y, "horizon_y: " + string(horizon_y)); text_y += text_dy;
draw_text(text_x, text_y, "ground_y: " + string(ground_y)); text_y += text_dy;
draw_text(text_x, text_y, "h_x1: " + string(h_x1)); text_y += text_dy;
draw_text(text_x, text_y, "h_x2: " + string(h_x2)); text_y += text_dy;
draw_text(text_x, text_y, "g_x1: " + string(g_x1)); text_y += text_dy;
draw_text(text_x, text_y, "g_x2: " + string(g_x2)); text_y += text_dy;
```

後半の Draw Debugの部分はデバッグ表示なのでなくても動きます。
そして実行……。

マウスカーソルの動きに合わせて背景が3Dっぽく動きます。

![100.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/12c66a30-85dc-f969-c693-e9b7257030fc.gif)

この仕組みを説明したのが以下の画像です。

<img width="785" alt="名称未設定.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/8663eead-04f3-f595-caec-615099aa7b35.png">

遠近法では、近くにあるものを大きく、遠くにあるものを小さく描きます。その応用で、近く（下部）にあるものを拡大して描画し、遠く（上部）にあるものを縮めているわけです。
それと図にはないですが、スクロール速度も近く（下部）が速く、遠く（上部）は遅くなっていたりします。

# 参考

* GOING BEYOND 2D WITH SIMPLE TRICKS (AND USING 2D TRICKS IN 3D)
    * https://www.yoyogames.com/blog/511/going-beyond-2d-with-simple-tricks-and-using-2d-tri