見出し画像

【GMS2 Knowledge】サーフェイス入門(1)

まず第一に既にすごく詳しい説明をされているページがありますので、一緒にご覧いただけると幸いです。
ところどころ説明が飛んでるかもしれないので…。

サーフェイスはGMS2において強力なビジュアル機能です。
しかし、扱いが難しいのでなかなか敬遠されがち。

そこで、こんなことに気を付ければ使いやすいよ!レッツエンジョイ・サーフェイス!ってな内容を書いていこうと思います。

そもそもサーフェイスって何なんだよ

そうですね。サーフェイスって何なんだよって話ですね。
UnityでいうところのRenderTextureに近いかもしれません。

まっさらなキャンバスに絵を描いたり、ゲームのキャプチャ画像を貼り付けたりして、好きなサイズや位置に描いた画像を描画できるような機能です。
GMSでは最終的な描画は「Application Surface」というサーフェイスに描画されてアプリケーションの表示が行われています。

詳しい話に触れると、
サーフェイスを作った時点でGPUメモリ内に画像としての領域を確保します。
このGPUメモリ内の画像に絵を描いたりして表示するのがサーフェイス機能なのです。

サーフェイスの4つの注意点(公式ドキュメント意訳)

一つ目は、「Application Surface」を除くサーフェイスはすべて揮発性(原文直訳)です。
つまり、何かの拍子ですぐに消えます。そして消える条件は様々なので、条件を特定して防ぐことは難しいです。
回避方法としてはサーフェイスを参照する前に、無ければ作る という処理を挟む事です。
二つ目は、サーフェイスはGPUメモリの領域を占有し続けるということです。
上記の説明のように 無ければ作る でいいのですが、作り続けるとGPUメモリを圧迫してパフォーマンスを著しく低下させます。
できる限り少なく、小さく保つことが重要です。
三つ目は、Drawイベントでサーフェイスを作ることです。
まずCreateイベントでサーフェイスを作るとApplication Surfaceと同じインデックスを取得してしまう可能性があります。
こうなると混乱が起こります。
また、Surfaceはドロー関数で操作を行うものが多くあるため、ドローイベントで作成することをお勧めします。
四つ目は、座標に関してです。
サーフェイスに描画する場合は、ローカル座標になるため、サーフェイスの左上は必ず[0,0]になります。

まずはサーフェイスを作ってみよう

まぁとりあえず、作ってから考えよう!
ということで、サーフェイスを作るためには以下のコードをドローイベントに書きます。

surf = surface_create(w, h);

wとhには横幅・縦幅のピクセルサイズを指定します。
これをドローイベントで書くと、サーフェイスを毎フレーム作ってしまうので、無ければ作る を実装しましょう。

if(!surface_exists(surf))
{
   surf = surface_create(w, h);
}

これで、無ければ作ってからサーフェイスを扱うことができます。
この時点では透明なキャンバスが作られただけですし、Application Surfaceに描画もしてないので、画面上は何も起きません。

サーフェイスに何か描画してみよう

サーフェイスを作ったら、サーフェイスにターゲットを合わせます。

surface_set_target(surf);

この記述以降のドロー関数はすべて指定したサーフェイスの中で行われます。
ここでエラーが出る場合は無ければ作るがうまく行ってないか、変数の参照を間違えているか、スペルミスです。多分。

draw_set_color(c_blue);
draw_circle(100, 100, 10, false);
draw_set_color(c_white);

ドロー関数に慣れている方には見覚えのある簡単なコードです。
青い円を描くだけです。

ここでの注意点は「サーフェイスの左上を基点とした座標を指定する」ことです。

描画したい処理がすべて終わったらサーフェイスのターゲットを外します。

surface_reset_target();

さて、ここまでで描画は終わりました。

え?実行したけど何も出ないって?

それはそのはず。
今作った「surf」をApplication Surfaceに描画しなければ何も表示はされません。

では、Application Surfaceにターゲットを合わせて…

というのは、やらなくて大丈夫です。

draw_surface(surf, 50, 50);

これで座標[50,50]にsurfが描かれました。
GMSではターゲットを指定しなければ、自動的にApplication Surfaceにターゲットされるようになっています。(一部のドローイベントを除く)

つまり最終結果としては、座標[150,150]の位置に直径10pxの青い円が描かれているはずです。
(グローバル座標[50,50]に、ローカル座標[100,100]に円を描いたので、結果はグローバル座標としては[150,150]の位置に円が描かれます。

実行結果(Runを押して実行してください)

サーフェイスをクリアする

さて、サーフェイスで円を描画できたわけですが、これだと円が動いた時に不自然な結果になります。

surf = noone;
lx = 100;
ly = 100;

#define draw
var w = 300;
var h = 300;
lx += sin(current_time / 100) * 10;

if(!surface_exists(surf))
{
   surf = surface_create(w, h);
}

surface_set_target(surf);
   draw_set_color(c_blue);
   draw_circle(lx, ly, 10, false);
   draw_set_color(c_white);
surface_reset_target();

draw_surface(surf, 50, 50);

実行結果(Runを押して実行してください)

円のx座標を時間によって動かしているだけなのですが、青い棒が出来ています。
サーフェイスは、手動でクリアしないと前フレームの情報を残した状態になります。

では、サーフェイスをターゲットした次の行に以下のコードを書きます。

draw_clear_alpha(c_black, 0);

これで、指定した色とアルファ値で塗りつぶしてクリアした状態になります。

実行結果(Runを押して実行してください)

ばっちり動きました。

サーフェイスを解放する

これはGPUメモリ上からサーフェイスを解放してメモリを空けることを意味します。
destroyイベントなんかに書いておくといいかもしれません。

surface_free(surf);

これだけで解放できます。
解放しないとGPUメモリに残る可能性があるので、不要になったら消していきましょう。


今回はまずはサーフェイスを作る というところだけを書きました。
次はサーフェイス描画周りについて詳しく書いていきます。