【Unity(C#)】ゲームオブジェクトを追尾する(MoveToWards)

　　
　　
　　
この記事は

**『プログラミング完全未経験からUnityでの開発現場に迎え入れてもらえた世界一の幸せ者』**

の記事です。そのつもりでお読みください。
　　

##オブジェクトを追尾

まず、ターゲットとなるゲームオブジェクトと追尾するゲームオブジェクトを用意します。

イメージとしては、ターゲットとなるゲームオブジェクトのポジションに向かって
追尾するゲームオブジェクトのポジションが一定の速度で更新(プラス・マイナス)されるといった感じです。

まずはコードです。
追尾するゲームオブジェクト が`this.gameObject`です。(つまり追尾するゲームオブジェクトにAdd Componentする)
 
```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveTowards : MonoBehaviour
{

    public GameObject targetObj;
    public GameObject explosion;

    MeshRenderer targetMesh;
    MeshRenderer thisObjMesh;

    Coroutine coroutine;

    float x_Abs;
    float y_Abs;
    float z_Abs;

    [SerializeField]
    float speedParameter = 10;

    void Start()
    {
        targetMesh = targetObj.GetComponent<MeshRenderer>();
        thisObjMesh = this.gameObject.GetComponent<MeshRenderer>();
    }

    void Update()
    {
        x_Abs = Mathf.Abs(this.gameObject.transform.position.x - targetObj.transform.position.x);
        y_Abs = Mathf.Abs(this.gameObject.transform.position.y - targetObj.transform.position.y);
        z_Abs = Mathf.Abs(this.gameObject.transform.position.z - targetObj.transform.position.z);

        if (coroutine == null)
        {
            coroutine = StartCoroutine(MoveCoroutine());
        }
    }

    IEnumerator MoveCoroutine()
    {
        float speed = speedParameter * Time.deltaTime;

        while (x_Abs > 0 || y_Abs > 0 || z_Abs > 0)
        {

            yield return new WaitForEndOfFrame();
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetObj.transform.position, speed);
        }

        print("重なった");
    }

   void OnTriggerEnter(Collider other)
    {
        //ターゲットにしたオブジェクトにタグをつけとく
        if(other.gameObject.tag == "Target")
        {
            explosion.SetActive(true);
            targetMesh.enabled = false;
            thisObjMesh.enabled = false;
        }
    }

}
```
##Mathf.Abs
今回はコルーチンで二点間の距離（x,y,z座標すべて）が0になるまで追尾させています。
【[コルーチンはこちら](https://qiita.com/OKsaiyowa/items/d0f59ae0c97b29118a80)】

なので、二点間の距離を求める必要があります。
しかし、**3D空間内を自由に動き回れる**という前提があれば、
お互いの座標が必ずしも正の値を示すとは限らないので、二点間の距離を絶対値で求める必要があります。

そこで`Mathf.Abs(float f)`を利用しました。
また、この値は追尾するオブジェクトのポジションが変わるとともに変化するので
常にUpdate内で監視する必要があります。

```C#
 x_Abs = Mathf.Abs(this.gameObject.transform.position.x - targetObj.transform.position.x);
 y_Abs = Mathf.Abs(this.gameObject.transform.position.y - targetObj.transform.position.y);
 z_Abs = Mathf.Abs(this.gameObject.transform.position.z - targetObj.transform.position.z);
```

`x,y,z座標それぞれの絶対値どれか一つでも0以上`がコルーチン内のループの条件になっています。

```C#
while (x_Abs > 0 || y_Abs > 0 || z_Abs > 0)
        {

            yield return new WaitForEndOfFrame();
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetObj.transform.position, speed);
        }
```

追記：2019/02/01
絶対値など使わなくてもよかった。まあこんな日もあるよね。

```C#
while (this.gameObject.transform.position != targetObj.transform.position)
        {

            yield return new WaitForEndOfFrame();
            this.gameObject.transform.position = Vector3.MoveTowards(this.gameObject.transform.position, targetObj.transform.position, speed);
        }
```

次はこの処理の中の`Vector3.MoveTowards`の解説です。


##MoveTowards

使い方は
`追尾(移動)したいオブジェクトのポジション = Vector3.MoveTowards(追尾(移動)したいオブジェクトのポジション,ターゲットのポジション,移動速度)`

です。

私が最初よくやりがちだったのは、this.gameObject.transform.positionを仮のVector3型変数を入れて使うことです。

たとえば、このような感じ↓です。

```C#
Vector3 tmpVec = this.gameObject.transform.position;
tmpVec = Vector3.MoveTowards(tmpVec, targetObj.transform.position, speed);
```
見やすくなってますが、動きません。
仮の入れ物の値が変わっただけで、動かしたいゲームオブジェクトの値は変わりません。

【[参考リンク] (https://qiita.com/motokitsu/items/69dcb4cada1cfcfb8b4e)】
↑ここによくわかる解説があります。

もし、ターゲットが移動するプレイヤーであればプレーヤーの座標の値も変化するので、
MoveTowardsの第二引数も仮のVector3型変数に入れっぱなしで使うべきではないです。

もちろん、仮の入れ物に代入して、再度代入し直すことできれいに書くことはできます。

```C#
Vector3 tmpVec; 

void Update()
    {
     tmpVec = this.gameObject.transform.position;
    }

//~省略
//targetObjが動く場合はtargetObj.transform.positionで直接取得
tmpVec = Vector3.MoveTowards(tmpVec, targetObj.transform.position, speed);
this.gameObject.transform.position = tmpVec;
```

##爆発
重なったらAssetStoreで適当にパクってきたフリーの爆発エフェクトのアクティブをオンにして
衝突した二つのオブジェクトのメッシュをオフにしてます。

```C#
void OnTriggerEnter(Collider other)
    {
        //ターゲットにしたオブジェクトにタグをつけとく
        if(other.gameObject.tag == "Target")
        {
            explosion.SetActive(true);
            targetMesh.enabled = false;
            thisObjMesh.enabled = false;
        }
    }
```
![GifCapture-201902010130356517.gif](https://qiita-image-store.s3.amazonaws.com/0/244071/05830dde-e108-d334-d50e-89f8e22855d7.gif)

GIF画像がチカチカするやつの直し方誰か教えてください。