はじめに
今回はアクションゲームやシューティングゲームなどでよく使う、ホーミングミサイルの実装方法を解説します。
使用する関数
ホーミングミサイルを実装するにあたって、以下の関数を使うと簡単に実装できます。
- instance_nearest(): 一番近いオブジェクトを取得する →一番近い敵を追尾するため
- angle_difference(): 2つの角度の差を求める →ホーミングは旋回軌道により追尾を行う
- point_direction(): 2点間のなす角度を求める →目標に命中する角度を求める
- point_distance(): 2点間のなす距離を求める →一定距離内に達したら追尾を開始する
使用する素材
使用する素材はこちらにアップロードしているので、あらかじめダウンロードしておきます。
http://www.syun777.sakura.ne.jp/tmp/gms2/gms2_horming.zip
プロジェクトの作成
今回は GML を使用するので、GameMakerLanguage
のプロジェクトを作成します
プロジェクトの設定
歯車アイコンをクリックして、Game Options
を開きます。
Game frames per second
の値を 60
にして、フレームレートを 60 にします。
プレイヤーの作成
オブジェクトを作成し、名前を objPlayer
とし、スプライト作成します。
スプライトの名前を sprPlayer
とします。
Import
ボタンを押して、素材画像 player_strip2.png
をインポートします。
アニメーションフレーム( Frames Per Second
)を 3
とします。
今回はスプライトの原点が中央であると都合が良いので、原点を Middle Centre
に変更します。
プレイヤーの配置
objPlayer
をルームに配置します。 room0
を開きます。
Room Editor > Properties > Room Settings
から Width
を 800
、Height
を 480
に変更します。
ルームサイズを変更したら、objPlayer
をルームの左側に配置します。
実行して、objPlayerが表示されるのを確認します。
プレイヤーの移動処理
objPlayer
を開いて、移動処理を作成します。
Add Event > Step > Step
でStepイベントを作成します。
コードは以下のように記述します。
// マウス座標との差を求める
var dy = mouse_y - y;
// 差の10分の1を足しこむ.
y += dy * 0.1;
このコードは現在の座標とマウス座標との差を求め、その差の10分の1をじわじわ足しこむ処理となります。
実行してみて動作を確認します。マウスの位置にプレイヤーが追従する動きをするようになりました。
ただ、プレイヤーが画面外に出て行ってしまうので、それを防ぐコードを追加します。
// マウス座標との差を求める
var dy = mouse_y - y;
// 差の10分の1を足しこむ.
y += dy * 0.1;
// 画面外に出ないようにする
y = median(32, y, room_height-32);
median関数を使うことで、yの値が 32〜room_height-32 の範囲に収まるようにしました。
実行して、プレイヤーが画面外に出ないことを確認します。
ミサイルの実装
ホーミングミサイルを実装します。
オブジェクトを作成して、名前を objShot
とします。
スプライトを作成し、名前を sprShot
にして、Import
ボタンを押して、人参の画像 carrot.png
をインポートします。
人参のスプライトの原点を中央にするため、Middle Centre
に変更します
objShot
のイベントを追加します。
Add Event > Other > Outside Room
を選択して、Outside Room
イベントを追加します。
コードは以下のように記述します。
instance_destroy();
ルーム外に出たら破棄処理を行います。
さらに、Add Event > Destroy
で破棄イベントを追加します。
コードは以下のように記述して、破棄されたらエフェクトを発生させるようにします。
effect_create_above(ef_explosion, x, y, 1, c_white);
プレイヤーからミサイルを発射する
objPlayer
を開いて、Stepイベントのコードを以下のように修正します。
// マウス座標との差を求める
var dy = mouse_y - y;
// 差の10分の1を足しこむ.
y += dy * 0.1;
// 画面外に出ないようにする
y = median(32, y, room_height-32);
// ※※※※※※※※※※※
// ここから追加
// ※※※※※※※※※※※
if(mouse_check_button_pressed(mb_left)) {
// 左クリックでミサイルを発射
var inst = instance_create_depth(x, y, 0, objShot);
with(inst) {
// 0度方向に5の速度で発射
motion_add(0, 5);
}
}
マウスの左クリックで右側に向かって発射するようにしました。
では、実行して人参ミサイルが発射できることを確認します。
敵オブジェクトを作成
新しくオブジェクトを追加して、名前を objEnemy
とします。
スプライトを追加して、名前を sprEnemy
とし、Import
ボタンを押して、素材画像 nasu.png
をインポートします。
そして、原点を中央 (MiddleCentre) に変更しておきます。
objEnemy
のイベントを追加します。
Add Event > Step > Step
を選び、Stepイベントを追加します。
Stepイベントには以下のように記述します。
// 下に動かす
vspeed = 2;
// 画面外に出たら反対側から出現させる
move_wrap(true, true, 0);
ホーミングで命中させる対象を動かすだけなので、下に動かして画面外に出たら反対側に移動するだけのコードです。
では敵を配置します。room0を開いて、objEnemy
を配置します。
それぞれの敵が、おおよそ均等に配置されていれば問題ありません。
では実行して、敵が上から下に移動するのを確認します。
ミサイルと敵との当たり判定を実装
objEnemy
を開いて Add Event > Collision > objShot
を作成し、人参ミサイルとの衝突イベントを作成します。
コリジョンイベントのコードは以下のように記述します。
with(other) {
// ミサイルを消す
instance_destroy();
}
衝突したミサイルを消すだけのコードです。
では実行して、敵にミサイルを当てると消滅することを確認します。
ホーミングの軌道を作成する
ようやくホーミングの軌道の実装です。
objShot
を開いて、Add Event > Step > Step
でStepイベントを作成します。
// ①一番近い敵を探す
var inst = instance_nearest(x, y, objEnemy);
if(inst == noone) {
// 見つからなかったので何もしない
return;
}
// ②目標に対する角度を計算する
var to_dir = point_direction(x, y, inst.x, inst.y);
// ③現在の角度と目標への角度の差を求める
var diff = angle_difference(image_angle, to_dir);
if(diff > 0) {
// ④-a. プラスは時計回り
image_angle -= 1;
}
else {
// ④-b. マイナスは反時計回り
image_angle += 1;
}
// ⑤回転値を移動角度にも反映する
direction = image_angle;
コードを詳しく解説すると、①のところで一番近い敵を探し、
②でそれに対する角度を計算します。
そして、計算した角度差を元に旋回処理を行っているのが④と⑤となります。
angle_difference()
で得られる数値の符合が逆のような気がしますが、そういうものとして反転して扱います。
一定の範囲内に入ったら追尾するようにする
objShot
を開いて、Stepイベントを修正します。
// ①一番近い敵を探す
var inst = instance_nearest(x, y, objEnemy);
if(inst == noone) {
// 見つからなかったので何もしない
return;
}
// ※ここを追加
var to_dist = point_distance(x, y, inst.x, inst.y);
if(to_dist > 100) {
// 100px以内に入らないと追尾しない
return;
}
// ※ここまで追加
// ②目標に対する角度を計算する
var to_dir = point_direction(x, y, inst.x, inst.y);
// ③現在の角度と目標への角度の差を求める
var diff = angle_difference(image_angle, to_dir);
if(diff > 0) {
// ④-a. プラスは時計回り
image_angle -= 3; // ※旋回速度を修正
}
else {
// ④-b. マイナスは反時計回り
image_angle += 3; // ※旋回速度を修正
}
// ⑤回転値を移動角度にも反映する
direction = image_angle;
※の部分が修正箇所です。
②の角度計算の前に距離を計算し、半径100px以内に入らないと追尾しないようにしています。
また、④の旋回速度の部分を少し大きくしています。
実行すると、基本的にまっすぐ飛びますが、敵の近くに行くと旋回して追尾するようになります。
距離制限のホーミングは、あまり使い道はないように思えるかもしれませんが、敵が発射するホーミングミサイルでこのような追尾条件を入れておくと、回避しやすくなって面白さが増す可能性があります。
あと、追尾範囲が見た目でわかりにくいので追尾する範囲を描画するようにしてみます。
objShot
を開いて、 Add Event > Draw > Draw GUI
イベントを追加し、以下のコードを追加します。
draw_circle(x, y, 100-32, true);
追尾範囲は半径100pxなのですが、ナスの半径が32pxなので、100-32となっています。
最後に
ここで作ったホーミングは、一番近い敵を追尾するようにしていますが、例えばロックオンした敵のみを追尾するなど、ゲームシステムによって柔軟に作り変えても良いと思います。
以上、ホーミングの作り方でした。