【GameMaker:Studio】ポリラインでホーミングレーザーを作る

投稿者: | 2014年2月14日

今回はポリラインでホーミングレーザーを作る方法を紹介します。

■ホーミングレーザーとは?

有名なところでは「レイストーム」で敵をロックオンして発射するレーザーですね。

ステージ2のボスとかも撃ってきます。

■まずは残像を実装

まずは残像を実装します。原理としては、過去の自分の座標を覚えておいてまとめて描画します。

001

過去に遡るごとにα値を下げていくと、それっぽく見えます。

▼実装コード

まずCreateイベントで過去の座標を格納する配列を用意します。

/// 変数初期化
self.POS_MAX = 16; // 記憶する座標の最大数

self.xList = 0; // X座標の配列
self.yList = 0; // Y座標の配列

for(var i = POS_MAX-1; i >= 0; i--)
{
  self.xList[i] = mouse_x;
  self.yList[i] = mouse_y;
}

今回は配列の数を16としました。ここを大きくすると残像の数が多くなりますが、処理負荷が高くなります。

つづいて、Stepイベントで以下のように記述します。

/// 更新
// 過去の座標を更新
for(var i = POS_MAX-1; i > 0; i--)
{
  self.xList[i] = self.xList[i-1];
  self.yList[i] = self.yList[i-1];
}

// 現在の座標を先頭に入れる
self.xList[0] = mouse_x;
self.yList[0] = mouse_y;

配列を後ろからループして、先頭にある値を後ろにコピーしています。今回はマウスを位置を追いかけるようにしました。

最後にDrawイベントです

/// 描画
draw_set_color(c_white);
// アルファ値の減少値
var da = 1.0 / POS_MAX;
for(var i = 0; i < POS_MAX; i++)
{
  // 後ろになるほどα値を下げる
  draw_set_alpha(1 - da * i);
  
  // 値を取り出し
  var px = self.xList[i];
  var py = self.yList[i];
  
  // 円を描画
  draw_circle(px, py, 4, false);
}

002

実行するとマウスポインタの位置を追いかけるようにして、円が描かれます。これで残像の実装は完了です。

■ポリラインで実装する

これだと円が描かれるだけで、とてもレーザーという感じではないですよね。それっぽく見せるためにまずは板ポリでポリラインを実装します。

/// 描画
draw_set_color(c_white);
// アルファ値の減少値
var da = 1.0 / POS_MAX;

// 始点を保持(※1)
var xprev = self.xList[0];
var yprev = self.yList[0];

// 頂点定義開始(※2)
draw_primitive_begin(pr_trianglestrip);

for(var i = 0; i < POS_MAX; i++)
{
  // 後ろになるほどα値を下げる
  draw_set_alpha(1 - da * i);
  
  // 値を取り出し
  var px = self.xList[i];
  var py = self.yList[i];
  
  // 始点から終点への向き(※3)
  var dir = point_direction(xprev, yprev, px, py);
  // 線を広げるサイズ(※4)
  var thin = 4;
  var dx1 = lengthdir_x(thin, dir+90); // 左手方向に広げる(X)
  var dy1 = lengthdir_y(thin, dir+90); // 左手方向に広げる(Y)
  var dx2 = lengthdir_x(thin, dir-90); // 右手方向に広げる(X)
  var dy2 = lengthdir_y(thin, dir-90); // 右手方向に広げる(Y)
  
  // 頂点座標を定義(※5)
  var x1 = xprev + dx1;
  var y1 = yprev + dy1;
  var x2 = xprev + dx2;
  var y2 = yprev + dy2;
  draw_vertex(x1, y1);
  draw_vertex(x2, y2);
  
  // 過去の座標を更新(※6)
  xprev = px;
  yprev = py;
  
  if(i == POS_MAX-1)
  {
    // 最後だけ終端を描く(※7)
    var x3 = px + dx1;
    var y3 = py + dy1;
    var x4 = px + dx2;
    var y4 = py + dy2;
    draw_vertex(x3, y3);
    draw_vertex(x4, y4);
  }
}

// 頂点の定義を終了して描画する(※8)
draw_primitive_end();

003

それっぽい軌道が出るようになりました。

急に長いコードとなったので、順を追って説明します。

その前にまずポリゴンの仕組みを知っておく必要があります。ポリゴンは基本的に3角形しか描くことができません。そして今回はポリゴンの描画タイプに「トライアングル・ストリップ」という方式を使用しています。

▼トライアングル・ストリップについて

004

この方式は、3頂点までは他の方式と同じなのですが、4つ目以降は前の2頂点を共有して三角形ポリゴンを作成します。

005

この特性を利用することで、このように、ぐにゃりと曲がった軌道でも隙間なく描画できるようになります。

▼ソースコード解説

まずは※1の部分からです。トライアングル・ストリップで描画するには、1つ前の座標が必要となります。そのために「xprev」「yprev」に値を保持するようにしています。

続いて※2の部分ですが、draw_primitive_begin()という頂点を開始するときの関数を呼び出して、引数にpr_trianglestripを指定してトライアングル・ストリップを開始しています。

※3ですが、prevからpへの向きを求めています。これは※4で直角右手方向・左手方向に移動するベクトルを求めるために必要となります。

006

※5で、(x1, y1)が左手方向に変数”thin”のぶんだけ広げた座標を求めています。(x2, y2)は右手方向に広げています。そしてdraw_vertex()関数を呼び出し、頂点を定義しています。

※6では次のループで使うために現在の座標を過去の座標(prev)に格納しています。

※7は、最後だけ頂点座標を定義しています。

※8で頂点の定義を完了し、実際の描画を行っています。

■テクスチャを貼り付ける

ここまでの内容でもそれなりに使えると思いますが、よりリッチな表現をするため、テクスチャを貼り付けます。

ここではこの画像を使うようにしました。

laser

以下、実装例です。

// テクスチャリピートを有効にする(※1)
texture_set_repeat(true);
draw_set_color(c_white);
// 加算合成
draw_set_blend_mode(bm_add);

// アルファ値の減少値
var da = 1.0 / POS_MAX;

// 始点を保持
var xprev = self.xList[0];
var yprev = self.yList[0];
// テクスチャを取得・設定(※2)
var tex = background_get_texture(background0_laser);
draw_primitive_begin_texture(pr_trianglestrip, tex);

// UV座標のUの初期値(※3)
var u = 0;

for(var i = 1; i < POS_MAX; i++)
{
  draw_set_alpha(1 - da * i);
  
  var px = self.xList[i];
  var py = self.yList[i];
  
  // 始点から終点への向き
  var dir = point_direction(xprev, yprev, px, py);
  // 線を広げるサイズ
  var thin = 4;
  
  var dx1 = lengthdir_x(thin, dir+90); // 左手方向に広げる(X)
  var dy1 = lengthdir_y(thin, dir+90); // 左手方向に広げる(Y)
  var dx2 = lengthdir_x(thin, dir-90); // 右手方向に広げる(X)
  var dy2 = lengthdir_y(thin, dir-90); // 右手方向に広げる(Y)
  
  var x1 = xprev + dx1;
  var y1 = yprev + dy1;
  var x2 = xprev + dx2;
  var y2 = yprev + dy2;
  draw_vertex_texture(x1, y1, u, 0); // (※4)
  draw_vertex_texture(x2, y2, u, 1); // (※4)
  
  // 過去の座標を更新
  xprev = px;
  yprev = py;
  
  // U座標更新 (※5)
  u += 1;
  
  if(i == POS_MAX-1)
  {
    // 最後だけ終端を描く
    var x3 = px + dx1;
    var y3 = py + dy1;
    var x4 = px + dx2;
    var y4 = py + dy2;
    draw_vertex_texture(x3, y3, u, 0);
    draw_vertex_texture(x4, y4, u, 1);
  }
}
draw_primitive_end();

007

このようになりました(あまり違いが分からないかも…)。

違いだけを簡単に説明しますと、

まず※1でテクスチャリピートを有効にしています。こうすると実装が楽だったのでリピートするようにしました。

※2で、backgroundリソースをテクスチャとして取得して、draw_primitive_begin_texture()で設定をしています。

※3では、U座標の初期値を設定しています。この値は※5で1ずつインクリメントします。

※4はdraw_vertex_texture()関数に変更し、テクスチャ座標を設定しています。

■ホーミングの軌道について

今回は描画方法についてのみ説明しました。レイフォースのようなホーミングの軌道をするためには、旋回速度や移動速度をうまく調整して、それっぽく見せる必要があります。

RectBreakerでは、obj_hormingでmp_potential_settings() / mp_potential_step()を使ってホーミングの軌道を制御していますので、よろしければ参考にしてみてください。

■サンプル

こちらのページにサンプルとサンプルコードを添付しています。


■注意点

  • HTML5ではWebGLを有効にする必要があります
  • 登録した画像はTextureGroupを分けないとリピートした際、おかしな表示になることがあります
  • 登録する画像は正方形テクスチャである必要があります