[Unity2D]プレイヤーから一番近い敵の角度に向かって攻撃を飛ばす(オートエイムしてPrefabを飛ばす)[C#]



<br><br><br><br><br>



最近、趣味でしょぼしょぼゲームを作った。めっちゃ楽しかったけど、嘔吐しそうなほどつまづいたとこを備忘録。



<br><br><br><br><br>
![autoaim2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2707164/cbb28f8f-033a-2342-b30c-2915e6c22495.gif)



こういう自動で敵の位置に向かって攻撃が飛んでくやつ。
<br><br><br><br><br>
参考文献

[たのしい2Dゲームの作り方 Unityではじめるゲーム開発入門](https://www.amazon.co.jp/dp/4798164119/ref=cm_sw_r_tw_dp_VZ1CY1AC02NR0EBRVMM1)
<br><br><br><br><br>
本誌のおまけpdfで三角関数の解説がある。当時おまけpdfまで読み込んでいなくて、後から気がついて喪失感に襲われた。嘘だと言ってよバーニー。。。
<br><br><br><br><br>
## 環境
Windows 10 Pro 21H1
Unity 2020.3.18f1
Microsoft Visual Studio Community 2019 Version 16.11.15

<br><br><br><br><br>

## 準備するもの

・プレイヤーキャラ。紹介するコードは全部プレイヤーキャラにアタッチしたスクリプトに記述すること。
・攻撃エフェクト(複数画像のアニメーションにRigidbodyとColliderをつけたりしたPrefabファイルここでは変数nowPrefab)
・敵(全ての敵で共通のTagをつけている前提)
・算数するぞ！　っていう気持ち

<br><br>
![tagサンプル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2707164/79cebc64-0a2a-09db-718c-52cb841c99d5.png)

※敵キャラにつけたTagを利用する。ここでは「Tag Enemy がついているオブジェクト＝敵」という考え方で実装する。


<br><br><br><br><br>



## 概要


### 流れ
攻撃ボタンが押される
↓
自分から一番近い敵を探す
↓
一番近い敵に攻撃を飛ばす。
↓
連射を防ぐため攻撃中フラグを立てる


<br><br><br><br><br>



まずボタンを押すと攻撃。ここではデフォルトの　Fire1　を使う。

Update()でフレーム毎に「Fire1」が押されたか確認。押されたならAttack()メソッドへ。

```C#
//オートエイム用角度変数。後で使う。クラス内メソッド外に置く。
float degree = 0.0f;
float rad = 0.0f;
public GameObject nowPrefab;    //発射したいプレハブ。
public float shootSpeed;    //攻撃の速度力。
bool inAttack = false;               //攻撃中フラグ

 void Update()
    {
        if ((Input.GetButton("Fire1")))
        {
	    Attack();
        }
    }
```

<br><br><br><br><br>

肝心かなめの攻撃処理本体。
```C#
public void Attack()
    {
        if (inAttack == false)
        {
            inAttack = true;        //攻撃フラグ立てる。連続で攻撃が発生しないようにするため。
            //↓++++++++++++一番近い敵探し++++++++++++↓
            //  タグEnemyのオブジェクトをすべて取得し、10f以内の最も近いエネミーを取得する。
            GameObject nearestEnemy = null;    //前回の攻撃で一番近かった敵をリセット
            float minDis = 10f;    //オートエイム範囲。お好みで。
            GameObject[] enemys = GameObject.FindGameObjectsWithTag("Enemy");//Enemyタグがついたオブジェクトをすべて配列に格納。
            foreach (GameObject enemy in enemys)    //全Enemyオブジェクト入り配列をひとつづつループ。
            {
                float dis = Vector3.Distance(transform.position, enemy.transform.position);    //プレイヤーキャラとループ中の敵オブジェクトの距離を引き算して差を出す。
                if (dis < minDis)    //オートエイム範囲(10f)以内か確認
                {
                    minDis = dis;    //今んとこ一番近い敵との距離更新。次のループ用。
                    nearestEnemy = enemy;    //今んとこ一番近い敵オブジェクト更新。
                }
            }
	// foreach　が終わった時、nearestEnemyにプレイヤーキャラから一番近い敵が入ってる。
            //↑++++++++++++一番近い敵探し++++++++++++↑

            if (nearestEnemy != null)       //オートエイム有効nullチェック。10f以内にタグEnemy存在。
            {
            //↓++++++++++++オートエイム++++++++++++↓
                Vector2 dt = nearestEnemy.transform.position - transform.position;   //nearestEnemyとプレイヤーキャラの距離を引き算。
                rad = Mathf.Atan2(dt.y, dt.x);    //距離をラジアンへ。
                degree = rad * Mathf.Rad2Deg;    //ラジアンを角度へ。
               
                //回転
                Quaternion r = Quaternion.Euler(0, 0, degree);    //プレイヤーキャラからみたnearestEnemyへの角度でQuaternionを用意。
                //発射するPrefabを生成。発射したいnowPrefabをプレイヤーキャラの位置に、rの角度で生成する。
                GameObject SlashObj = Instantiate(nowPrefab, transform.position, r);
            //↑++++++++++++オートエイム++++++++++++↑
                //発射するパワーを用意。
                float x = Mathf.Cos(degree * Mathf.Deg2Rad);    //角度をラジアンに。Horizontal。
                float y = Mathf.Sin(degree * Mathf.Deg2Rad);    //角度をラジアンに。Vertical。
                Vector3 v = new Vector3(x, y) * shootSpeed;    //Vector3生成。 変数shootSpeedを大きくすると力が大きくなる。お好みで。

                Rigidbody2D body = SlashObj.GetComponent<Rigidbody2D>();    //SlashObjの当たり判定取得。
                body.AddForce(v, ForceMode2D.Impulse);    //SlashObjの当たり判定に力を加える。飛んでく。やったぜ。
                Invoke("StopAttack", 0.4f);    //連射を防ぐためのフラグ操作。
                Destroy(SlashObj, 0.3f);    //攻撃オブジェクトを0.3秒後に削除。射程にも影響する。
            }
          }
    }
```
<br><br><br><br><br>

```C#
 //攻撃ディレイ中の再攻撃禁止。Attack内でInvokeで呼ぶことでディレイをしている。
    public void StopAttack()
    {
        inAttack = false;    //攻撃中フラグ下ろす
    }
```
<br><br><br><br><br>


　まあまあ複雑。改造して使う際、一気にコピペしないで、少しづつ実装してDebug.Log出しながらの方がわかりやすいと思う。
<br><br>
　自作ゲームからかいつまんだコードなので、変数の宣言ぬけとか変なとこあったらごめん。
　ミスがあったらご報告いただければ嬉しいです。


<br><br><br><br><br><br><br><br><br><br>