見出し画像

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

今回はサーフェイス用にプロジェクトを作りました。
一緒にサーフェイスを教えてくれるヨーカン君です。

まずは前回のおさらい

サーフェイスというのは別の見えないところで描いた画像を、Application Surfaceという見えている画面に描く事で使えるキャンバス

この程度の認識で大丈夫です。
そして今日やるのは少し実践的な内容です。

画面をコピーして使ってみよう

先に結果を画像で出します。

ヨーカン君が二人!あ、これは鏡か…?

まずはサーフェイス用のオブジェクト作成

オブジェクト「oMirror」を作成します。
Draw Endイベントで以下のコードを書きました。

if(!variable_instance_exists(id,"surf")) surf = noone;
if(!surface_exists(surf))
{
	surf = surface_create(100,100);
}

surface_set_target(surf);
	draw_clear_alpha(0,0);

	//draw screen
	draw_surface_part_ext(application_surface, x-100, y, 100, 100, 0,0,1,1,c_white,1);


	//draw rectangle
	gpu_set_colorwriteenable(1,1,1,0);
	draw_set_color(c_blue);
	draw_set_alpha(0.2);

	draw_rectangle(0,0,100,100,false)

	draw_set_color(c_white);
	draw_set_alpha(1);
	
	gpu_set_colorwriteenable(1,1,1,1);
	
surface_reset_target();

draw_surface_ext(surf, x + 100, y, -1,1,0,c_white,1);
うわぁぁぁぁ!難しいコードがいっぱいだぁ!

はい、そう思えますね。わかります。
分解して見ていくので、ちょっとだけお付き合いください。

まず頭の5行目まで

if(!variable_instance_exists(id,"surf")) surf = noone;
if(!surface_exists(surf))
{
	surf = surface_create(100,100);
}

これはもう、おまじないみたいなもんですが、
最初の1行は「このインスタンスに変数が無ければ作る
その次は「サーフェイスが無ければ作る

そうです、面倒くさいのでCreateイベントで変数を定義しないでDraw Endイベントだけで済ませてます。

はい、次

surface_set_target(surf);
	draw_clear_alpha(0,0);

ここは前回やってますね。
まずサーフェイスにターゲットを合わせて、クリアしてます。

蛇足ですが、draw_clear_alphaの引数を、前回は「c_black」を使ってましたが、0だけならc_blackと一緒になります。

ほい、次

//draw screen
draw_surface_part_ext(application_surface, x-100, y, 100, 100, 0,0,1,1,c_white,1);

ちょっと引数が長いですが、draw_surface_part関数は指定したサーフェイスの指定した一部分だけを描画する関数です。
extは拡張版の関数で、拡大率や回転など、細かく指定できます。

Application Surfaceから、「oMirror」インスタンスのX座標から横幅分(100px)隣を切り抜いて描画してます。

図で表すと、青い部分「oMirror」は、この赤枠のところをApplication Surfaceからコピーして描画しています。

前回お話しましたが、Application Surfaceは見えている画面を指します。

何度も使うこの画像。
本当にこのイメージでいた方がわかりやすいと思うんですよね…。

さて、その次です。
見慣れない関数が出てきます。

	//draw rectangle
	gpu_set_colorwriteenable(1,1,1,0);

	draw_set_color(c_blue);
	draw_set_alpha(0.2);

	draw_rectangle(0,0,100,100,false)

	draw_set_color(c_white);
	draw_set_alpha(1);
	
	gpu_set_colorwriteenable(1,1,1,1);

間に挟まる四角を描いている部分に関しては、端折りますが、以下の関数について詳しく話しますので、頑張って解読してください。

gpu_set_colorwriteenable(1,1,1,0);
//gpu_set_colorwriteenable(true, true, true, false);

本当は引数は「true」と「false」です。

これは、GPUに対して命令を出す関数ですが、RGBAのうちどの書き込みを許可するか を示しています。

Photoshopなどの画像加工ソフトを使っていると大きな間違いをしやすいのですごく詳しく解説します。

まず、同じ描画タイミングの画像の合成は、基本的に上書きです。
これを念頭に以下の図を見てください。

oMirrorの下にダミー画像を置きました。

gpu_set_colorwriteenable(true, true, true, true);

上記のようにRGBAすべての書き込みを許可した状態だとこうなります。
不透明なはずのう〇こが透けているのがわかります。

これ、実はヨーカン君も透けてます。

ヨーカン君は色が濃いのでわかりづらいですが、薄っすらすけているのがわかるでしょうか?

レベル補正したら少しはわかりやすくなったかな?

gpu_set_colorwriteenable(true, true, true, false);

上記のようにA値を書き込まないように設定しなおしました。
すると以下のようになります。

今度はヨーカン君は透けていません。

しかし、大事なモノが欠けています。

ババァンッ

あぁ!う〇こが!
こりゃーどういう事でしょうか。

きっとここまで読んだ人はスタンド攻撃でも受けているように混乱している事でしょう。

混乱する理由は、画像編集ソフトに慣れている固定観念が関係しています。(知らんけど)

例えば、上のような図があります。
このピンクの円を、水色の四角に重ねると…

こうなる気がします。
でも、GMSの描画では…

こうなります。(Photoshopで作った疑似画像ですが、こんな感じです)

RGBAの値が上書きされているので、こうなるんです。

つまり、不透明だった水色の四角の上に半透明の円が上書きされると、
半透明(A値)が水色の四角に上書きされるので、
重なって上書きされた部分は水色の四角も半透明になっちゃうって仕組みです。

うーん、わかりづらいけど、そのまんまです。

それでは、もう一度戻ります。

gpu_set_colorwriteenable(true, true, true, false);

これにより、以下のような画像が生まれていましたね?

A値が書き込まれないのは分かったけど、青い半透明の四角は書き込まれてるやんけ!と…

そうお思いでしょう。

厳密にいうと、A値は書き込まれていません。サーフェイスには。

図で表すと、上のようになっています。

Surfに書き込んでいるのは、マージした画像になります。
ヨーカン君の上に青い半透明の四角が合成された状態までがマージした状態として1枚の画像として扱っています。

その1枚のマージした画像のA値を含んでSurfに書き込むとこうなるわけです。

いやぁん

そこで、A値を含めずにサーフェイスにマージ画像を書き込むことで…

ひゃっはー!

ここまで読んで、う〇こが消えている謎はわかりましたか?

そう、このoMirrorの表示しているサーフェイスはすでに不透明画像なんです。

だから、う〇この上に表示したら、そりゃう〇こは消えます。

では、コードに戻りますが、もう前回やったものしか残っていません。

	
surface_reset_target();

draw_surface_ext(surf, x + 100, y, -1,1,0,c_white,1);

サーフェイスのターゲットをリセットして、Application Surfaceに描画します。
この時、ミラーっぽくするためにxscaleを反転させています。

以上!

さて、かなり長くなりましたが、
実はこの半透明問題はもう1個大きな課題を抱えています。

それは次回にします。