【シューティングゲーム】Unityでシューティングゲームを作ろう！（完全版）

# 目的
Unityで位置からシューティングゲームを作成していきましょう！

以下のようなゲームを作成します。

![44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/55a7dd59-f19e-0b1f-7f92-d636f38bb4a1.gif)

全体のステップとしては、
【開始】⇒【ゲームをする】⇒【終了】⇒【結果表示】⇒【データの保存】⇒【初めから】
の繰り返しで再現します。

つまり、Unity Editorから独立できるところまで行う！！
# 学べる事
以下のようなゲームで必須機能を実装していきます。
・シューティングゲーム
・データ保存（CSV）
・ランキング表示
・時間制限表示
・倒した敵表示
・敵の移動
・敵破壊のエフェクト

# 必要なもの
・パソコン（Windows推奨、MacBookでもできる）
・Unity2019.4.13f推奨（ほかのバージョンでも問題ないと思う）

# GitHubでスクリプトは公開してあります。
https://github.com/sassa4771/Shooting_Game_Unity2019.4.13f1

# ①弾の動作を作成

### ステップの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/56d7467a-068a-cb8b-3dca-98eeb7f240ce.png)

## Step1
### 目標イメージ
以下のような、弾が等速直線運動、ホーミングして目標座標に飛んでいく機能を作成する。
【等速直線運動】
![3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/93a8b77a-f2b6-e52e-cf67-791ebf1f75d1.gif)
【ホーミング】
![5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/791ba47d-f4f9-349a-d705-945c8ca9b36b.gif)

### オブジェクトの配置
以下のように弾と目標座標を配置する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/821d8904-d990-9d69-6951-2f2a6d6010f7.png)

### スクリプト

```Move_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move_Bullet : MonoBehaviour
{

    public GameObject target;
    Vector3 direction;
    float speed = 5.0f;

    public void Start()
    {
        direction = target.transform.position;    
    }

    public void Update()
    {
        float step = speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, direction, step);
    }
}
```

上記のスクリプトを弾オブジェクトにアタッチする。
![4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/d99ac0d0-0f57-af21-bd65-f943079d640a.gif)

### 等速直線運動の動作確認
![3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/c1ae0fa5-b66b-83c1-9cb6-ff480f4d5295.gif)

### ホーミングする方法
スクリプトを少し書き換えるだけ

```Move_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move_Bullet : MonoBehaviour
{
    public GameObject target;
    Vector3 direction;
    float speed = 5.0f;

    public void Start()
    {
        direction = target.transform.position;
    }

    public void Update()
    {
        //※ここを追加↓
        direction = target.transform.position;
        float step = speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, direction, step);
    }
}

```

### ホーミングの動作確認
![5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/91794e68-d682-37c6-ebdd-c196bf650f1c.gif)

## Step2~Step4
### 目標イメージ
以下のような連射をできるようにする。
![6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/90cc6459-558f-1f85-8f26-1d2ce173a2a6.gif)
そのためには、弾オブジェクトをプレハブ化して、１秒ごとにプレハブをインスタンス化する。

### プレハブ化しよう
以下のように、オブジェクトをprojectにドラッグするだけでプレハブ化完了。
プレハブ化できたら、Hierarchyにある弾オブジェクトは削除しましょう。
![7.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/f1135da0-5f45-16b8-e53b-aaad12324033.gif)

### Step1スクリプトの改変

このページのスクリプトで弾オブジェクトが向かう位置は、「public GameObject target;」で決定しているため手動でアタッチしてあげないといけない。
※しかし、インスタンス化した時に毎回手でアタッチすることは、できないのでスクリプトから自動でアタッチできるように変更する。

```改変後Move_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move_Bullet : MonoBehaviour
{
    public GameObject target;
    Vector3 direction;
    float speed = 10.0f;

    public void Start()
    {
        //Targetオブジェクトを探して自動的に取得
        target = GameObject.Find("Target").gameObject;
        direction = target.transform.position;
    }

    public void Update()
    {
        float step = speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, direction, step);
    }
}


```

### インスタンス化
インスタンス化のスクリプトについては、こちらで解説してるスクリプトを改変する。

https://qiita.com/sassa4771/items/35c2240a333d42b1517a

インスタンス生成の「位置」をPlayerの位置からに変更しました。

```Instanse_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Instanse_Bullet : MonoBehaviour
{
    public float timeOut = 1.0f;
    private float timeElapsed;
    public GameObject cube;
    public GameObject Player;

    void Update()
    {
        //timeElapsedに経過時間(Time.deltaTime)を加算 
        timeElapsed += Time.deltaTime;
        if (timeElapsed >= timeOut)
        {
            //InstantiateでGameObject生成 
            //Instantiate(複製するGameObject,位置,回転)の順番で記載 
            Instantiate(cube, Player.transform.position, Quaternion.identity);
            //timeElapsedを0.0fに戻す 
            timeElapsed = 0.0f;
        }
    }
}

```

### Instanse_Bullet.csをアタッチする
![8.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/bd374915-570f-d6c3-8f19-bdb6b4f3920f.gif)

### 動作確認
![6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/bf8e0dc1-6e47-4476-daf6-55f6ec62de14.gif)

## Step5
### 到達目標のイメージ
以下のように増えすぎたインスタンス化を自動削除して動作が重くならない対策をしよう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/12f708f9-6eb6-732c-f327-7fafca399ff1.png)

### スクリプト
弾オブジェクトが生成されてから５秒後に消滅するスクリプトを作成。

```Destroy_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Destroy_Bullet : MonoBehaviour
{
    private float currentTime;
    private float TimeOut = 5.0f;

    // Update is called once per frame
    void Update()
    {
        currentTime += Time.deltaTime;
        if(currentTime > TimeOut)
        {
            Destroy(this.gameObject);
        }
    }
}

```

### 弾オブジェクトのプレハブにスクリプトをアタッチ
以下のようにプレハブにスクリプトをアタッチする。
![9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/408edbb4-2aa2-990e-b46b-78a70aa1a42a.gif)

### 動作確認①
５秒後に最初の弾オブジェクトが消滅するので弾オブジェクトが５個以上増えないことが確認できる。
![10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/91f79607-e2e1-984b-6598-b404bb937315.gif)

### Destoryの関数について調べてみる
公式サイト：

https://docs.unity3d.com/ScriptReference/Object.Destroy.html

を見てみるとDestoryに時間指定をすることができる。

※スクリプトを作成しなくてもできるので、こちらを使って簡潔にする。
よって、インスタンス作成時のstartに「Destory(this.gameobject, 5.0f);」を入れておけば同じことが再現できる。
Move_Bullet.csに追加する。

```追加後Move_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move_Bullet : MonoBehaviour
{
    public GameObject target;
    Vector3 direction;
    float speed = 10.0f;

    public void Start()
    {
        //【追加】Destoryを5.0f後指定で追加
        Destroy(this.gameObject, 5.0f);
        target = GameObject.Find("Target").gameObject;
        direction = target.transform.position;
    }

    public void Update()
    {
        float step = speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, direction, step);
    }
}

```

### 動作確認②
動作確認①と同様の動作が確認できました。
こちらのほうが「Destroy_Bullet.cs」を作成せずにすむのでオススメ。
![11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/d51bb58a-bf73-0852-8e30-c0956d6b4ec5.gif)

# ②敵の破壊動作を作成

### ステップの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/9d72028c-2ef8-de37-50ba-4c1a4c8cc66f.png)

## Step1~Step3

### 目的
弾衝突時の検知をして敵を倒す機能をつける
![12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/539b8bd3-dd58-40f7-9eaa-383cf28fbb6a.gif)

### 弾オブジェクトと敵オブジェクトに「Rigidbody」をつける
![13.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/7026db66-fe7f-2958-e02b-7fa4ca7895e6.gif)

### 敵オブジェクトの「Is Trigger」をオンにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/30063009-0c41-0fc5-fc2a-7b8edebb7010.png)

### スクリプトの作成
以下のスクリプトを敵オブジェクトにアタッチします。

```Collision_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collision_Bullet : MonoBehaviour
{
    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("破壊");
        //敵オブジェクトの破壊
        Destroy(this.gameObject);
        //衝突した弾オブジェクトの破壊
        Destroy(other.gameObject);
    }
}
```

### 動作確認
これでStep3までうまく動作しました。
![12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/78d4b7c2-eb35-db08-c6eb-2c7735541e45.gif)

## Step4

### 目的
以下のような感じで弾オブジェクトが敵オブジェクト衝突時にエフェクトをつける。
![14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/f883706e-8771-f527-c918-71d150dd7b09.gif)

### パーティクルの作成（エフェクト）
こちらの記事を参考にさせていただきました、

https://styly.cc/ja/tips/explosion01/#i

### プレハブ化
作成したパーティクルをプレハブ化しておきましょう。
爆発なので「Explotion」にしておきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/6c1f5690-02e2-9963-063a-e99988b09688.png)

### スクリプトの作成
衝突時のときに発生させるので、step1~step3で作成した「Collision_Bullet.cs」を変更していきましょう。

```Collision_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collision_Bullet : MonoBehaviour
{
    //【追加】ここにプレハブをアタッチする
    public GameObject Explosion;

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("破壊");
        Destroy(this.gameObject);
        Destroy(other.gameObject);

        //【追加】インスタンス化
        GameObject instance_explosion = Instantiate(Explosion, this.transform.position, Quaternion.identity);
        //【追加】一度エフェクトが見れたら後はいらないので削除
        Destroy(instance_explosion.gameObject, 2.0f);
    }
}

```

### 動作確認
![14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/94513f9a-7d27-8a2a-fa00-ae0c8f9ebded.gif)

## Step5

### 目的
以下のように敵オブジェクトを倒した数を点数として表示する
![17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/7838e6c8-8cfc-2fe3-5b9f-7fff6af8f603.gif)

### 点数を保持するスクリプトの作成
点数の加算時にデータを保持しておきたいので、アクセスが簡単にできる「ScriptableObject」を使っていきます。
新しく「DataScripts.cs」を作成しましょう。
以下の「Enemy_killed」に敵オブジェクトを倒した数を加算していきます。

```DataScripts.cs

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

public class DataScripts : ScriptableObject
{
    public static int Enemy_killed = 0;    //ループの回数の同期をとるため（Player_exportCsvScriptに依存）
}

```

### UIテキストに点数を表示するスクリプト
Unity UIテキストに先ほどの「Enemy_killed」を表示するためのスクリプトを作成していきましょう。
そのために、「Show_Enemy_Killed_Num.cs」を新しく作成していきます。


```Show_Enemy_Killed_Num.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Show_Enemy_Killed_Num : MonoBehaviour
{
    //点数を表示するUI Textをここでアタッチ
    public Text num_enemy_killed;

    // Update is called once per frame
    void Update()
    {
        //毎フレームごとに点数を書き換える
        num_enemy_killed.text = "倒した数： " + DataScripts.Enemy_killed.ToString() + " 体";
    }
}
```

### 点数を表示するText UIの作成
![15.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/b6fb3fc2-f733-1674-5d81-d7eca96e7124.gif)

### 作成したスクリプトのアタッチ
![18.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/262c4f27-d5a8-6821-534e-0b2cbe8072cb.gif)


### 動作確認
![17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/c1ad4a4e-0a5a-2937-981f-10fb9b18bab0.gif)

# ③Plyaerの動作を作成

### ステップの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/dbac7e80-d667-6fea-ae2e-326f15e80017.png)

## Step1~Step2

### 目的
![20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/105e4c0f-d457-3130-0433-4c5a5d1703dd.gif)


### 「Target」と「Main Camera」を「Player」の子オブジェクトにする
「Player」の子オブジェクトにすることで「Player」の動き、回転にカメラとターゲットが追従するため、
「Player」の向いている方向に自動でカメラが移動し、自動で弾が向いている方向に発車されるようになる。

※敵オブジェクトは増やしておきました。

![19.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/23e18c9e-32c0-b74a-0385-633ec8213a3f.gif)

### Playerを操作するスクリプトを作成する
ここまででも、いい感じになりましたがGameScene上ですべてを完結させるため
「Player_Controller.cs」を作成していきます。

```Player_Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_Controller : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey("right"))
        {
            transform.Rotate(0, 0.1f, 0);
        }
        if (Input.GetKey("left"))
        {
            transform.Rotate(0, -0.1f, 0);
        }
    }
}
```

### スクリプトをアタッチする
作成した「Player_Controller.cs」を「Player]にアタッチする
![21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/677e6959-a388-dc89-8232-e1b941a7a1bc.gif)


### 動作確認
![20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/105e4c0f-d457-3130-0433-4c5a5d1703dd.gif)

# ④シューティングゲームを仕上げる

### ステップの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/f59774a0-445a-5f28-c6e6-1352aaad3666.png)

## Step1~Step2

### 目的
![25.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/e49e20eb-395e-c1db-5698-b552e675e053.gif)

※敵オブジェクトは、省略してますがコピーして以下のように増やしてあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/2fd2e412-443a-d8ae-a45d-0ab370fe117f.png)

### スクリプトを作成する
X方向のみ動くスクリプト「Move_Enemy1.cs」を作成。

```Move_Enemy1.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move_Enemy1 : MonoBehaviour
{
    float speed;
    float radius = 0.01f;

    private void Start()
    {
        speed = Random.Range(0f, 2.0f);
    }
    void Update()
    {
        float x = transform.position.x + radius * Mathf.Sin(Time.time * speed);
        transform.position = new Vector3(x, transform.position.y, transform.position.z);
    }
}
```

X方向とY方向動くスクリプト「Move_Enemy2.cs」を作成。

```Move_Enemy2.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move_Enemy2 : MonoBehaviour
{
    float speed;
    float radius = 0.01f;

    private void Start()
    {
        speed = Random.Range(0f, 2.0f);
    }
    void Update()
    {
        float x = transform.position.x + radius * Mathf.Sin(Time.time * speed);
        float z = transform.position.z + radius * Mathf.Sin(Time.time * speed);
        transform.position = new Vector3(x, transform.position.y, z);
    }
}
```

### 動作確認
作成したスクリプトを敵オブジェクトにアタッチする。
![22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/5706eeff-dd4b-1f94-a4f5-9e7bad293c91.gif)

### 敵同士の衝突判定を避ける
現在の設定だと敵同士が衝突して、爆発してしまうのでスクリプトを見直す。
![23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/18bfeba6-372d-aac7-cc7b-58b7ea2927a6.gif)

### スクリプトの見直し
「Collision_Bullet.cs」を改良していく。
UnityのTag機能を利用して、衝突したオブジェクトの判別を行っていく。

```改良後　Collision_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collision_Bullet : MonoBehaviour
{
    public GameObject Explosion;

    private void OnTriggerEnter(Collider other)
    {
        //【追加】Tagを使って衝突した時のオブジェクトを選別
        if (other.gameObject.tag == "Bullet")
        {
            Debug.Log("破壊");
            Destroy(this.gameObject);
            Destroy(other.gameObject);

            GameObject instance_explosion = Instantiate(Explosion, this.transform.position, Quaternion.identity);
            Destroy(instance_explosion.gameObject, 2.0f);

            DataScripts.Enemy_killed++;
        }
    }
}
```

### Tagを作成していく
「Inspector」の「Tag」をクリックして、「Add Tag...」を選択。
新しいTagを追加する。Tag名は「Bullet」にする。

![24.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/bf41073a-0adc-e11a-2dcc-b4013931d70b.gif)

### 動作確認
![25.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/77fbc4e7-67e4-c12d-8f78-82d2b138955f.gif)

敵オブジェクト同士の衝突もなくなりましたね。
![26.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/8c42fca3-23b9-e48a-d9b4-2b4a46de0988.gif)

## 番外編

### ステップの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/f59774a0-445a-5f28-c6e6-1352aaad3666.png)

### 目的
従来のスクリプトで左右上下の回転機能を追加すると左のようになる。
これをFPSのようなカメラワークに変更する（右図）
![31.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/c7ce78e6-b6e8-4204-fad7-f421b3217aa9.gif)

本記事では、番外編になる「Player操作スクリプトを改良しよう」を行っていきます。

### Player操作スクリプトの改良
現在のスクリプトだと左右の回転に上下の回転が加わるとカメラワークがおかしくなってしまう。
カメラがだんだん傾いてしまうのを修正する

```改良前　Player_Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_Controller : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey("down"))
        {
            transform.Rotate(0.1f, 0, 0);
        }
        if (Input.GetKey("up"))
        {
            transform.Rotate(-0.1f, 0, 0);
        }
        if (Input.GetKey("right"))
        {
            transform.Rotate(0, 0.1f, 0);
        }
        if (Input.GetKey("left"))
        {
            transform.Rotate(0, -0.1f, 0);
        }
    }
}
```

![27.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/9598f5bb-b51a-515e-9f89-ba60fef6deff.gif)
参考にしたサイト

https://teratail.com/questions/47561

```改良後　Player_Controller.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_Controller : MonoBehaviour
{

    public float rotSpeed = 1.0f;
    float rotX = 0.0f, rotY = 0.0f;

    void FixedUpdate()
    {
        // Read input 
        float axisH = Input.GetAxis("Horizontal");
        float axisV = Input.GetAxis("Vertical");

        rotX += axisV * rotSpeed;
        rotY -= axisH * rotSpeed;

        rotX = Mathf.Clamp(rotX, -90.0f, 90.0f);
        while (rotY < 0.0f) { rotY += 360.0f; }
        while (rotY > 360.0f) { rotY -= 360.0f; }

        transform.eulerAngles = new Vector3(-rotX, -rotY, 0.0f);
    }
}
```

### カメラワーク改良後　動作確認
![28.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/00fde28d-2ef0-7acc-55fe-630fe255933a.gif)

## Step3~Step4
### 目的
時間制限をつけて、終了の表示をつける。
![30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/389fc2c1-dac7-96ed-5eef-03d5ddfedf61.gif)

### スクリプト
タイマーカウント用に「TimeCount.cs」を作成する。

```TimeCount.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TimeCount : MonoBehaviour
{
    public Text FinishText;
    public GameObject ShowFinishText;

    //タイマーカウント用
    private float TimeOut = 60f;
    private float CurrentTime = 0;

    // Update is called once per frame
    void Update()
    {
        CurrentTime += Time.deltaTime;

        if(CurrentTime > TimeOut)
        {
            TimeUp();
        }

        ChangeText(CurrentTime);
    }

    void ChangeText(float Time)
    {
        if (!DataScripts.Finish_Game)
        {
            FinishText.text = (TimeOut - Time).ToString("f1") + "秒";
        }
    }

    void TimeUp()
    {
        DataScripts.Finish_Game = true;
        ShowFinishText.SetActive(true);
    }
}
```

ゲームが終了したことが分かるように「DataScripts.cs」に「Finish_Game」を追加しておく。

```DataScripts.cs

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

public class DataScripts : ScriptableObject
{
    public static int Enemy_killed = 0;    //ループの回数の同期をとるため（Player_exportCsvScriptに依存）
    public static bool Finish_Game = false;
}
```

### Unity UIでTextとImageを用意する
![32.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/aa15953a-cae3-64ec-fbe1-27629571ec6b.gif)

### 動作確認
![30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/7ca3be1a-10b4-028f-5598-7c96f6bf34b3.gif)

## Step5
### 目的
ランキングをつける。登録できるようにする。
![43.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/b437aec7-5020-50c7-96d8-5b9d777cf1a7.gif)

### UIを作っていく（デザイン）

Imageの作成↓
![33.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/ce1ebec4-2bb3-882d-a322-e499f55761b8.gif)

Textの作成↓
![34.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/828eb4bf-f4e6-87c2-7fae-0c9449d14cba.gif)

Textの設定はこちら↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/cac155ec-0124-2d12-db93-b923a03817a3.png)

最終的なデザイン↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/2115733e-384f-3aca-66c9-8a437cfae65c.png)

### データ管理用のCSVを用意する
ランキングを表示するためのデータを用意します。
内容は以下のような感じにします。

csv名前：savedata.csv
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/d006fd94-0520-5b1b-0332-c58deaa5940c.png)
【テキスト】
name	num_enemy
AAA	5
BBB	10
CCC	7
DDD	12
EEE	3
FFF	3

1.textを作成して、名前を.csvに変更する
![savedata1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/090dfe01-410d-037e-c6b1-f3b8a2e42e50.gif)


2.csvをExcelで開く
![savedata2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/dde7d149-8395-e9e1-9012-9d0865762196.gif)

3.【テキスト】の内容を入力する
![savedata3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/e48350a9-233d-8b21-6aae-1ced74d17b46.gif)

4.番外編：.txtで開いてみる
![savedata4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/6764ed30-7c99-d0bf-0b31-97adae757236.gif)

【配置Path】
配置場所はプロジェクトの直下です。プロジェクト名が[Shooting]の場合<br>
「Shooting/savedata.csv」となる感じです。<br>

![savedata6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/e775e3ae-c263-4a7c-6a9c-413fd657fb07.gif)


### CSVを使ってデータベースを作っていく。

https://qiita.com/sassa4771/items/f45d2af2770f2bd8917d

こちらを参考にして「exportCsvScript.cs」スクリプトを改良します。

```exportCsvScript.cs
using UnityEngine;
using System.Text;
using System.IO;
using System.Collections.Generic;

public class exportCsvScript : MonoBehaviour
{
    private List<string[]> csvDatas = new List<string[]>(); // CSVの中身を入れるリスト

    // ファイル書き出し(上書き）
    public void OverWriteCSV(string[] header, string[] data, string path)
    {
        // 現在のフォルダにsaveData.csvを出力する(決まった場所に出力したい場合は絶対パスを指定してください)
        // 引数説明：第1引数→ファイル出力先, 第2引数→ファイルに追記(true)or上書き(false), 第3引数→エンコード
        // falseにすると、ファイルを新規作成する
        StreamWriter sw = new StreamWriter(path, false, Encoding.GetEncoding("UTF-8"));

        string h = string.Join(",", header);
        sw.WriteLine(h);

        //ここで","を付けた形にする(str2に配列strに","を付けたものを代入する）
        string d = string.Join(",", data);

        //ここで初めて書き込む
        sw.WriteLine(d);

        // StreamWriterを閉じる
        sw.Close();


        Debug.Log(path);
    }


    // ファイル書き出し（追加）
    public void AppendCSV(string[] data, string path)
    {
        // 現在のフォルダにsaveData.csvを出力する(決まった場所に出力したい場合は絶対パスを指定してください)
        // 引数説明：第1引数→ファイル出力先, 第2引数→ファイルに追記(true)or上書き(false), 第3引数→エンコード
        // falseにすると、ファイルを新規作成する
        StreamWriter sw = new StreamWriter(path, true, Encoding.GetEncoding("UTF-8"));

        //ここで","を付けた形にする(str2に配列strに","を付けたものを代入する）
        string d = string.Join(",", data);

        //ここで初めて書き込む
        sw.WriteLine(d);

        // StreamWriterを閉じる
        sw.Close();

        Debug.Log(path);
    }

    //csvファイルの読み出し
    public List<string[]> ReadCSV(string path)
    {
        // ファイル読み込み
        // 引数説明：第1引数→ファイル読込先, 第2引数→エンコード
        //Unityのプロジェクトフォルダー内にある場合
        StreamReader sr = new StreamReader(path, Encoding.GetEncoding("UTF-8"));
        string line;

        // 行がnullじゃない間(つまり次の行がある場合は)、処理をする。→最後の行まで読みだす。
        while ((line = sr.ReadLine()) != null)
        {
            // コンソールに出力
            Debug.Log(line);
            csvDatas.Add(line.Split(','));
        }

        // StreamReaderを閉じる
        sr.Close();

        Debug.Log(path);

        return csvDatas;
    }

    //ファイルが存在するかを確認
    public bool CheckExixtCSV(string path)
    {
        if (System.IO.File.Exists(path))
        {
            Debug.Log("CSVファイルが存在するので追記します");
            return true;
        }
        else
        {
            Debug.Log("CSVファイルが存在しないので作成します");
            return false;
        }
    }
}
```
### 表示をするスクリプトを作成する

```ShowRank.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ShowRank : MonoBehaviour
{
    public exportCsvScript ecs;
    string path = "savedata.csv";
    List<string[]> csvresult;

    //Text
    public Text Rank1;
    public Text Rank2;
    public Text Rank3;
    public Text Rank4;
    public Text Rank5;
    public Text Rank6;
    int i=0;

    // Start is called before the first frame update
    void Start()
    {
        csvresult = ecs.ReadCSV(path);

        //csvファイルの中身を一つずつ表示する
        foreach (string[] ds in csvresult)
        {
            if (i != 0)
            {
                switch (i)
                {
                    case 1:
                        Rank1.text = ds[0] + " " + ds[1] + "体";
                        break;

                    case 2:
                        Rank2.text = ds[0] + " " + ds[1] + "体";
                        break;

                    case 3:
                        Rank3.text = ds[0] + " " + ds[1] + "体";
                        break;

                    case 4:
                        Rank4.text = ds[0] + " " + ds[1] + "体";
                        break;

                    case 5:
                        Rank5.text = ds[0] + " " + ds[1] + "体";
                        break;

                    case 6:
                        Rank6.text = ds[0] + " " + ds[1] + "体";
                        break;

                    default:
                        break;
                }
            }
            i++;
        }
    }
}
```
### スクリプトにUIをアタッチをする①
![36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/604b663d-2126-2401-e7a5-e71280e4ae9e.gif)


### 動作確認①
![35.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/8d7dfd96-52db-002e-175c-f4b25df6538a.gif)


### ソートを入れる
このままだと順位は分からないのでソートをしていきます。
こちらのサイトがとても分かりやすかったので使っていきます

https://qiita.com/r-ngtm/items/f4fa55c77459f63a5228

独自クラスでListの作成方法

https://www.wwwmaplesyrup-cs6.work/entry/2019/11/08/130507


### List並べ替えスクリプト
ShowRank.csを改良する。

```改良後　ShowRank.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ShowRank : MonoBehaviour
{
    public exportCsvScript ecs;
    string path = "savedata.csv";
    List<string[]> csvresult;
    List<csv_sorted> cs = new List<csv_sorted>();

    //Text
    public Text Rank1;
    public Text Rank2;
    public Text Rank3;
    public Text Rank4;
    public Text Rank5;
    public Text Rank6;
    int i=1;

    // Start is called before the first frame update
    void Start()
    {    
        //【追加】
        csvresult = ecs.ReadCSV(path);
        make_csv_sort(csvresult);
        cs.Sort((a, b) => b.num_enemy - a.num_enemy);

        //順位６番までを表示
        foreach (var ds in cs)
        {
                switch (i)
                {
                    case 1:
                        Rank1.text = ds.name  + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 2:
                        Rank2.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 3:
                        Rank3.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 4:
                        Rank4.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 5:
                        Rank5.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 6:
                        Rank6.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    default:
                        break;
                }
            i++;
        }
    }

    //【追加】
    void make_csv_sort(List<string[]> csv)
    {
        int i = 0;
        foreach (string[] c in csv)
        {
            if (i != 0)
            {
                cs.Add(new csv_sorted { name = c[0], num_enemy = int.Parse(c[1]) });
            }
            i++;
        }
    }
}

//【追加】
public class csv_sorted
{
    public string name { get; set; }
    public int num_enemy { get; set; }
}
```

### 動作確認②
![37.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/eb83e125-da7e-e9f8-b4b8-5c57148a9a82.gif)

### ランキングに登録をする
以下のようなUIを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/d8883139-1e11-200a-3a13-0f966d1777c3.png)
【構成】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/89500bde-1e0c-74d1-650e-a856dd2e0cb4.png)

### ランキング登録スクリプト
ボタンを押したときに「敵オブジェクトを倒した数」と「InputFieldに入れた名前」をcsvに登録するスクリプト「Button_Regist.cs」を作成する。

```Button_Regist.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Button_Regist : MonoBehaviour
{
    public exportCsvScript ecs;
    public InputField inputfield;
    public GameObject Rank_Image;

    string path = "savedata.csv";
    string[] header = { "name", "num_enemy" };
    List<string[]> csvresult;

    public void ButtonClickRegist()
    {
        string[] data = { inputfield.text, DataScripts.Enemy_killed.ToString() };

        // ファイルが存在するかを確認
        if (ecs.CheckExixtCSV(path))
        {
            // ファイル追記（追加）
            ecs.AppendCSV(data, path);
        }
        else
        {
            // ファイル書き出し(上書き）
            ecs.OverWriteCSV(header, data, path);
        }

        Rank_Image.SetActive(true);
    }
}

```

### スクリプトにUIをアタッチをする②
![39.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/ed6c6232-c477-a592-5b80-494512220fb7.gif)

### ボタンにクリック機能を追加する
![40.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/34507326-89c9-95b9-31f5-cac603fc6f86.gif)

### 動作確認③
![41.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/506f5c8e-06fd-88b0-876c-4638f172a8d1.gif)

以下のように追加される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/4a250039-5d19-8756-e7fe-7e339f38e296.png)

### ランキング表示に登録したユーザも反映させる
ランキングのオブジェクトがSetActive(true)になったときにランキング機能を呼び出せばよい。
そのため、「ShowRank.cs」のStartをOnEnableに変更する。

詳しくは、こちらが分かりやすかったので参考に

https://dkrevel.com/unity-explain/how-to-call-start-awake-onenable/


```改変後　ShowRank.cs②
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ShowRank : MonoBehaviour
{
    public exportCsvScript ecs;
    string path = "savedata.csv";
    List<string[]> csvresult;
    List<csv_sorted> cs = new List<csv_sorted>();

    //Text
    public Text Rank1;
    public Text Rank2;
    public Text Rank3;
    public Text Rank4;
    public Text Rank5;
    public Text Rank6;
    int i=1;

    // Start is called before the first frame update
    void OnEnable()
    {    
        csvresult = ecs.ReadCSV(path);
        make_csv_sort(csvresult);
        cs.Sort((a, b) => b.num_enemy - a.num_enemy);

        //順位６番までを表示
        foreach (var ds in cs)
        {
                switch (i)
                {
                    case 1:
                        Rank1.text = ds.name  + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 2:
                        Rank2.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 3:
                        Rank3.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 4:
                        Rank4.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 5:
                        Rank5.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    case 6:
                        Rank6.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                        break;

                    default:
                        break;
                }
            i++;
        }
    }

    void make_csv_sort(List<string[]> csv)
    {
        int i = 0;
        foreach (string[] c in csv)
        {
            if (i != 0)
            {
                cs.Add(new csv_sorted { name = c[0], num_enemy = int.Parse(c[1]) });
            }
            i++;
        }
    }
}

public class csv_sorted
{
    public string name { get; set; }
    public int num_enemy { get; set; }
}

```

Hierarchyで「SHowRank」オブジェクトを「Rank_Image」オブジェクトの子オブジェクトに変更する。
![42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/3a5c7c63-b328-cd6d-a028-9f3e300a63b1.gif)

### 動作確認④
10体倒したとしておきます。
「あああ」と入力するとランキングに表示されているのが分かります。
![43.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/0ae4e149-8b20-25cd-51e2-c641a091eb69.gif)

# Step5
# 目的
ゲームを仕上げます。
今のままだと終了するたびに、Unity Editorで再生しなおす必要がありますが、本来ゲームはEditorに依存しません。
なので今回は、【開始】⇒【ゲームをする】⇒【終了】⇒【結果表示】⇒【データの保存】⇒【初めから】の繰り返しを再現します。

つまり、Unity Editorから独立できるところまで行う！！
![44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/55a7dd59-f19e-0b1f-7f92-d636f38bb4a1.gif)

# ステップの確認
### 今回は最終ステップでゲーム全体を仕上げていきます！
ほとんどが今までの応用ですので、今回初めて出る機能だけ紹介します。

## 1.Title画面を作成（新しいシーン）
### 新しくSceneを作成する。
![47.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/7b0589b5-ac47-3a45-e619-722f346611e5.gif)

以下のような画面を作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/94c882de-38ac-3d69-9676-90adf989a355.png)

### Buttonにページ移動機能をつける
以下のようなスクリプトを作成して、ボタンの「On Click()」機能にアタッチする。

```ButtonClickGameStart.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ButtonClickGameStart : MonoBehaviour
{
    public void BottonClikStart(){
        SceneManager.LoadScene("MainGame");
    }
}
```

ボタンにスクリプトをアタッチ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/3cb2d372-314f-38b5-4066-14654f12eee4.png)

### ※ゲームシーンを「MainGame」という名前にしておく必要がある！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/fc37e258-70d1-3678-b853-0af1a701fb5b.png)

## 動作確認①
これで「Title」からゲーム開始ができるようになった！
![48.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/62263061-5e9b-4481-c4d2-e56e16b674b1.gif)

### 2.ランキングに「登録する」か「登録しないか」決めるボタンをつける
このページで作成した「ゲーム終了」画面にボタンをつけて、ランキングに登録するかそのまま終わるかの選択ができるようにする。

下のような感じで作成をする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/a51127af-f9b9-f75d-04fa-cc30947c0f64.png)

構成は以下のよう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/8c26c571-3857-7009-6661-1f0594e0f1e0.png)

### Buttonに機能をつけていく
### ①「登録する」を押した場合⇒ランキング登録画面を表示する
![45.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/9e2d5440-9e96-c3ae-7200-4fbd5637e854.gif)

「ButtonClickShowRegister.cs」スクリプトを作成↓

```ButtonClickShowRegister.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ButtonClickShowRegister : MonoBehaviour
{
    public GameObject register;
    public void ButtonClickShow()
    {
        register.SetActive(true);
    }
}
```

スクリプトをボタンにアタッチして「On Click()」機能を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/1aa7a279-78a5-c4a4-867b-5f52daed1976.png)

### ②「しないで終了」を押した場合⇒「Title」画面に戻る
![46.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/dcab8591-9daa-8ec0-d01c-f4c5d80b0c9d.gif)

「ButtonClickFinishGame.cs」スクリプトを作成↓

```ButtonClickFinishGame.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ButtonClickFinishGame : MonoBehaviour
{
    public void BottonClikFinish()
    {
        SceneManager.LoadScene("Title");
    }
}
```

スクリプトをボタンにアタッチして「On Click()」機能を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/8097c1dd-6a10-2762-d6bf-fb3990638f32.png)

## ランキング表示時間を１０秒にして、自動で「Title」に移動
ここまででほぼ完成しましたが、「登録する」を押した場合ランキングが表示されます。
ランキング画面には、ボタンをつけていないので「Title」画面に戻ることができない。

そこでランキングが表示されてから１０秒で「Tilte」に自動移動するようにする。

### スクリプトの改良
「ShowRank.cs」スクリプトを改良する

```改良後　ShowRank.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class ShowRank : MonoBehaviour
{
    public exportCsvScript ecs;
    string path = "savedata.csv";
    List<string[]> csvresult;
    List<csv_sorted> cs = new List<csv_sorted>();

    //Text
    public Text Rank1;
    public Text Rank2;
    public Text Rank3;
    public Text Rank4;
    public Text Rank5;
    public Text Rank6;
    int i=1;

    //【追加】①
    private bool finish_bool = false;
    float timecount = 0;

    // Start is called before the first frame update
    void OnEnable()
    {    
        csvresult = ecs.ReadCSV(path);
        make_csv_sort(csvresult);
        cs.Sort((a, b) => b.num_enemy - a.num_enemy);

        //順位６番までを表示
        foreach (var ds in cs)
        {
            switch (i)
            {
                case 1:
                    Rank1.text = ds.name  + " " + ds.num_enemy.ToString() + "体";
                    break;

                case 2:
                    Rank2.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                    break;

                case 3:
                    Rank3.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                    break;

                case 4:
                    Rank4.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                    break;

                case 5:
                    Rank5.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                    break;

                case 6:
                    Rank6.text = ds.name + " " + ds.num_enemy.ToString() + "体";
                    break;

                default:
                    break;
            }

            i++;
        }
        //【追加】②
        finish_bool = true;
    }

    void make_csv_sort(List<string[]> csv)
    {
        int i = 0;
        foreach (string[] c in csv)
        {
            if (i != 0)
            {
                cs.Add(new csv_sorted { name = c[0], num_enemy = int.Parse(c[1]) });
            }
            i++;
        }
    }
    //【追加】③
    private void Update()
    {
        if (finish_bool)
        {
            timecount += Time.deltaTime;

            if (timecount > 10) SceneManager.LoadScene("Title");
        }
    }
}

public class csv_sorted
{
    public string name { get; set; }
    public int num_enemy { get; set; }
}
```

### 動作確認②
下のGIFは、編集で１０秒を早送りしてます。（実際は１０秒待ってからの画面移動）
![49.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/e6b08485-7cdc-590e-0e3e-fdc180fce4c4.gif)


### ゲーム終了後の「敵を倒した数」のカウントを回避
ゲームが終了した後も、敵を倒すと点数が加算されてしまうので、それを修正。
点数を加算するスクリプトは、「Collision_Bullet.cs」なので、そこに「DataScripts.cs」の「Finish_Game」で分岐をする。

```Collision_Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collision_Bullet : MonoBehaviour
{
    public GameObject Explosion;

    private void OnTriggerEnter(Collider other)
    {
        //【追加】Tagを使って衝突した時のオブジェクトを選別
        if (other.gameObject.tag == "Bullet")
        {
            Debug.Log("破壊");
            Destroy(this.gameObject);
            Destroy(other.gameObject);

            GameObject instance_explosion = Instantiate(Explosion, this.transform.position, Quaternion.identity);
            Destroy(instance_explosion.gameObject, 2.0f);

            //【追加】
            if (!DataScripts.Finish_Game)
            {
                DataScripts.Enemy_killed++;
            }
        }
    }
}
```

# ゲームの完成！！
お疲れさまでした。これでshootingゲームが一通り完成しました。
あとは、音や敵オブジェクトのデザインなどを変えれば、いろんな可能性が広がると思います。

Asset Storeを使ってもよし！Blenderなどで自作してもよしです。


![44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043128/55a7dd59-f19e-0b1f-7f92-d636f38bb4a1.gif)