【Unity2D】追尾弾をつくる

弾幕STGでよくある追尾弾をつくります。

※注意(10/23 追記)
この記事ではRigidBody2Dコンポネントのvelocityを使っていますが
Vector3.MoveTowardsを使う方法が一番簡単です。
使い方は以下の通りです。

gameObject.transform.position = Vector3.MoveTowards(gameObject.transform.position, player.transform.position, distance);

gamebjectは弾、playerはプレイヤー、つまり目的地、ターゲットです。
このメソッドの3つ目の引数、distanceには距離が入るらしいです。
Time.deltaTimeが入れられていることが多いですが、私はまだよく分かっていないです。
これを使ってうごかしたものが以下です。
とてもきれいに動いてくれます。

以下は、velocityを使う本記事の内容です。

弾の移動はRigidBody2Dコンポネントのvelocityを使います。
使い方は以下です。

gameObject.GetComponent<RigidBody2D>().velocity=Vector2;

右辺の「Vector2」に適当なベクトルを入れると、
思った方向に動いてくれるのですが
方向については以下の記事で書いているので
こちらでは詳しくは触れません。

aoaoaoaoaoaoaoi.hatenablog.com

今回は、敵から自機に向かってくる追尾弾を作るので
「弾から自機に向かうベクトル」を何度も計算して
軌道を修正しながら移動させる必要があります。

ベクトルの向きは「弾から自機へのベクトル」です。
これは、「原点から自機へのベクトル」から「原点から弾へのベクトル」を引いて出します。
これを、Updateメソッドの中で常に計算してvelocityに入れれば、
弾は思った通り常にプレイヤーを追いかけてくれます。
しかし、一つ問題があります。
それは、速度です。

単純にvelocityに求めたベクトルを入れただけでは、
弾の速度が変化していってしまいます。
ベクトルの向きは弾の飛んでいく向きを表します。
では、速度は何によって決まるのかというと
それはベクトルの長さです。
弾は時間がたつにつれ、プレイヤーに近づきます。
そのため、
弾からプレイヤーへと伸びるベクトルの長さは段々と小さくなっていき
速度が遅くなってしまうのです。
以下では、青い三角を自機、赤い丸を弾としています。
f:id:aoaoaoaoaoaoaoi:20181021185732p:plain
f:id:aoaoaoaoaoaoaoi:20181021185740p:plain
ここで、必要となってくるものが正規化です。
ベクトルの正規化とは、
向きをそのままに保ちながら、長さを1にすることです。
これは、Unityで「Vector3.normalized」が用意されています。
この正規化で一度ベクトルの長さを1にし、
任意の速度をかけることで
任意の一定の速度を保つことができます。
以下では、
ベクトルを正規化してから速度3をかけて
弾を3という一定の速度に保ちながらプレイヤーを追尾させています。

コード例

以上のことを踏まえて、書いたコードが以下です。
以下のスクリプト敵につけています
弾からプレイヤーへのベクトルをUpdateメソッド内で計算し直すために
リストに弾のプレハブを保存して使用しています。
またここでは、プレイヤーを永遠に追いかける弾をつくっていますが
実際のゲームに使う際は、
敵からプレイヤーへの追尾弾は回避できるものにしておく方がいいと思います。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TamaToPlayerFollow : MonoBehaviour
{
    //プレイヤー
    public GameObject player;
    //弾のプレハブ
    public GameObject tama;
    //弾を一秒ごとに打ち出す
    private float targetTime = 1.0f;
    private float currentTime = 0;
    //弾を保存しておくリスト
    private List<GameObject> list = new List<GameObject>(); 

    // Update is called once per frame
    void Update()
    {
        //弾を一秒ごとに打ち出すためのもの
        currentTime += Time.deltaTime;
        if(targetTime < currentTime)
        {
            currentTime = 0;
            //敵の位置を保存
            var pos = this.gameObject.transform.position ;
            //弾のプレハブを作成
            var t = Instantiate(tama) as GameObject;
            //リストに弾を保存しておく
            list.Add(t);
            //弾の初期位置を敵の位置にする
            t.transform.position = pos;
        }
        //リストから一つずつ弾を取り出してベクトルの向きを修正する
        foreach (var l in list)
        {
            //弾のvelocityにベクトルを入れる
            //弾からプレイヤーへのベクトルを求めて、正規化し任意の速さ3をかける
            l.GetComponent<Rigidbody2D>().velocity = (player.transform.position - l.transform.position).normalized*3;
        }
    }
}

参考

3Dを基礎から勉強する 正規化
【Unity】ベクトルを正規化する