[GameMaker: Studio] 光源周辺だけ明るくする方法・その2

gms_light_sample_2

前回のサンプルでは光源用のスプライトを用意しましたが、今回はさらに簡略化して単純な円のみでやってみます。

暗くするためのマスク用surfaceを使用するところまでは同じですが、そのsurfaceは事前に黒ベタ(この例では青ですが)で塗りつぶしておきます。

surface_set_target(global.main_surface_mask);
draw_clear_alpha(c_navy, 1);
surface_reset_target();

光源部分の描画はスプライトを使わずにdraw_circleで塗り潰し円を描きますが、ここに今回のポイントがあります。

通常、draw系の命令は透明度はそのまま透明色として扱われるため、すでに塗ってある部分をくり貫くことはできませんが、alpha blendを無効にすることで透明度も含めて上書きすることが可能となります。

surface_set_target(global.main_surface_mask);
draw_enable_alphablend(false);    // alpha blendを無効
draw_set_alpha(0);    // 透明度を設定
draw_set_colour(c_black);  // このカラー設定はなくてもOK
draw_circle(x, y - 20, 50, false);
surface_reset_target();

くり貫きが終わったら各描画設定を元に戻しておきましょう。

draw_set_alpha(1);    // 透明度を戻しておかないと通常の描画がされなくなります
draw_enable_alphablend(true);    // alpha blendも有効に戻すのを忘れずに

この方法を使うことで、先にベタ塗りしてあるマスク用surfaceの任意の部分を透明にすることができます。
あとはこれを元のゲーム画面に重ねるだけです。

この例では光源毎にくり貫き処理をしていますが、ds_queueに座標だけを格納しておいて、メインループのDraw Endで一括処理した方が高速です。

…と、blend modeを使わない方法を解説しましたが、やはりこの方法でもHTML5では正しく描画されません。

HTML5 canvasでもglobalCompositeOperationという命令によって合成方法が選べることになっていて、GameMaker: StudioHTML5エクスポートでもきちんとこの命令で出力していますが、困ったことにブラウザ側でこのglobalCompositeOperationの扱いがおかしなことになってしまっていることが原因です。

高速なPCであれば自前でピクセル単位でくり貫くという強引な方法もできますが、さすがに重過ぎるので現実的ではありません。
シミュレーションゲームにおける「移動可能部分だけ明るくする」ような感じで、暗くする部分をある程度ブロック化して塗り潰す方法であれば、面積次第では実用できるかもしれません。