LoginSignup
0
2

More than 1 year has passed since last update.

【シューティングゲーム】Unityでシューティングゲームを作ろう!(完全版)

Last updated at Posted at 2021-09-12

目的

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

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

44.gif

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

つまり、Unity Editorから独立できるところまで行う!!

学べる事

以下のようなゲームで必須機能を実装していきます。
・シューティングゲーム
・データ保存(CSV)
・ランキング表示
・時間制限表示
・倒した敵表示
・敵の移動
・敵破壊のエフェクト

必要なもの

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

GitHubでスクリプトは公開してあります。

①弾の動作を作成

ステップの確認

image.png

Step1

目標イメージ

以下のような、弾が等速直線運動、ホーミングして目標座標に飛んでいく機能を作成する。
【等速直線運動】
3.gif
【ホーミング】
5.gif

オブジェクトの配置

以下のように弾と目標座標を配置する。
image.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

等速直線運動の動作確認

3.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

Step2~Step4

目標イメージ

以下のような連射をできるようにする。
6.gif
そのためには、弾オブジェクトをプレハブ化して、1秒ごとにプレハブをインスタンス化する。

プレハブ化しよう

以下のように、オブジェクトをprojectにドラッグするだけでプレハブ化完了。
プレハブ化できたら、Hierarchyにある弾オブジェクトは削除しましょう。
7.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);
    }
}


インスタンス化

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

インスタンス生成の「位置」を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

動作確認

6.gif

Step5

到達目標のイメージ

以下のように増えすぎたインスタンス化を自動削除して動作が重くならない対策をしよう。
image.png

スクリプト

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

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

動作確認①

5秒後に最初の弾オブジェクトが消滅するので弾オブジェクトが5個以上増えないことが確認できる。
10.gif

Destoryの関数について調べてみる

公式サイト:

を見てみると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

②敵の破壊動作を作成

ステップの確認

image.png

Step1~Step3

目的

弾衝突時の検知をして敵を倒す機能をつける
12.gif

弾オブジェクトと敵オブジェクトに「Rigidbody」をつける

13.gif

敵オブジェクトの「Is Trigger」をオンにする

image.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

Step4

目的

以下のような感じで弾オブジェクトが敵オブジェクト衝突時にエフェクトをつける。
14.gif

パーティクルの作成(エフェクト)

こちらの記事を参考にさせていただきました、

プレハブ化

作成したパーティクルをプレハブ化しておきましょう。
爆発なので「Explotion」にしておきました。
image.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

Step5

目的

以下のように敵オブジェクトを倒した数を点数として表示する
17.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

作成したスクリプトのアタッチ

18.gif

動作確認

17.gif

③Plyaerの動作を作成

ステップの確認

image.png

Step1~Step2

目的

20.gif

「Target」と「Main Camera」を「Player」の子オブジェクトにする

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

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

19.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

動作確認

20.gif

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

ステップの確認

image.png

Step1~Step2

目的

25.gif

※敵オブジェクトは、省略してますがコピーして以下のように増やしてあります。
image.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

敵同士の衝突判定を避ける

現在の設定だと敵同士が衝突して、爆発してしまうのでスクリプトを見直す。
23.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

動作確認

25.gif

敵オブジェクト同士の衝突もなくなりましたね。
26.gif

番外編

ステップの確認

image.png

目的

従来のスクリプトで左右上下の回転機能を追加すると左のようになる。
これをFPSのようなカメラワークに変更する(右図)
31.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
参考にしたサイト

改良後 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

Step3~Step4

目的

時間制限をつけて、終了の表示をつける。
30.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

動作確認

30.gif

Step5

目的

ランキングをつける。登録できるようにする。
43.gif

UIを作っていく(デザイン)

Imageの作成↓
33.gif

Textの作成↓
34.gif

Textの設定はこちら↓
image.png

最終的なデザイン↓
image.png

データ管理用のCSVを用意する

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

csv名前:savedata.csv
image.png
【テキスト】
name num_enemy
AAA 5
BBB 10
CCC 7
DDD 12
EEE 3
FFF 3

1.textを作成して、名前を.csvに変更する
savedata1.gif

2.csvをExcelで開く
savedata2.gif

3.【テキスト】の内容を入力する
savedata3.gif

4.番外編:.txtで開いてみる
savedata4.gif

【配置Path】
配置場所はプロジェクトの直下です。プロジェクト名が[Shooting]の場合

「Shooting/savedata.csv」となる感じです。

savedata6.gif

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

こちらを参考にして「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

動作確認①

35.gif

ソートを入れる

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

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

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);

        //順位6番までを表示
        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

ランキングに登録をする

以下のようなUIを作成する。
image.png
【構成】
image.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

ボタンにクリック機能を追加する

40.gif

動作確認③

41.gif

以下のように追加される
image.png

ランキング表示に登録したユーザも反映させる

ランキングのオブジェクトがSetActive(true)になったときにランキング機能を呼び出せばよい。
そのため、「ShowRank.cs」のStartを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);

        //順位6番までを表示
        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

動作確認④

10体倒したとしておきます。
「あああ」と入力するとランキングに表示されているのが分かります。
43.gif

Step5

目的

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

つまり、Unity Editorから独立できるところまで行う!!
44.gif

ステップの確認

今回は最終ステップでゲーム全体を仕上げていきます!

ほとんどが今までの応用ですので、今回初めて出る機能だけ紹介します。

1.Title画面を作成(新しいシーン)

新しくSceneを作成する。

47.gif

以下のような画面を作成する
image.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

※ゲームシーンを「MainGame」という名前にしておく必要がある!

image.png

動作確認①

これで「Title」からゲーム開始ができるようになった!
48.gif

2.ランキングに「登録する」か「登録しないか」決めるボタンをつける

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

下のような感じで作成をする。
image.png

構成は以下のよう
image.png

Buttonに機能をつけていく

①「登録する」を押した場合⇒ランキング登録画面を表示する

45.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

②「しないで終了」を押した場合⇒「Title」画面に戻る

46.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

ランキング表示時間を10秒にして、自動で「Title」に移動

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

そこでランキングが表示されてから10秒で「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);

        //順位6番までを表示
        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は、編集で10秒を早送りしてます。(実際は10秒待ってからの画面移動)
49.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

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2