目的
Unityで位置からシューティングゲームを作成していきましょう!
以下のようなゲームを作成します。
全体のステップとしては、
【開始】⇒【ゲームをする】⇒【終了】⇒【結果表示】⇒【データの保存】⇒【初めから】
の繰り返しで再現します。
つまり、Unity Editorから独立できるところまで行う!!
学べる事
以下のようなゲームで必須機能を実装していきます。
・シューティングゲーム
・データ保存(CSV)
・ランキング表示
・時間制限表示
・倒した敵表示
・敵の移動
・敵破壊のエフェクト
必要なもの
・パソコン(Windows推奨、MacBookでもできる)
・Unity2019.4.13f推奨(ほかのバージョンでも問題ないと思う)
GitHubでスクリプトは公開してあります。
①弾の動作を作成
ステップの確認
Step1
目標イメージ
以下のような、弾が等速直線運動、ホーミングして目標座標に飛んでいく機能を作成する。
【等速直線運動】
【ホーミング】
オブジェクトの配置
スクリプト
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);
}
}
等速直線運動の動作確認
ホーミングする方法
スクリプトを少し書き換えるだけ
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);
}
}
ホーミングの動作確認
Step2~Step4
目標イメージ
以下のような連射をできるようにする。
そのためには、弾オブジェクトをプレハブ化して、1秒ごとにプレハブをインスタンス化する。
プレハブ化しよう
以下のように、オブジェクトをprojectにドラッグするだけでプレハブ化完了。
プレハブ化できたら、Hierarchyにある弾オブジェクトは削除しましょう。
Step1スクリプトの改変
このページのスクリプトで弾オブジェクトが向かう位置は、「public GameObject target;」で決定しているため手動でアタッチしてあげないといけない。
※しかし、インスタンス化した時に毎回手でアタッチすることは、できないのでスクリプトから自動でアタッチできるように変更する。
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の位置からに変更しました。
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をアタッチする
動作確認
Step5
到達目標のイメージ
以下のように増えすぎたインスタンス化を自動削除して動作が重くならない対策をしよう。
スクリプト
弾オブジェクトが生成されてから5秒後に消滅するスクリプトを作成。
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);
}
}
}
弾オブジェクトのプレハブにスクリプトをアタッチ
動作確認①
5秒後に最初の弾オブジェクトが消滅するので弾オブジェクトが5個以上増えないことが確認できる。
Destoryの関数について調べてみる
公式サイト:
を見てみるとDestoryに時間指定をすることができる。
※スクリプトを作成しなくてもできるので、こちらを使って簡潔にする。
よって、インスタンス作成時のstartに「Destory(this.gameobject, 5.0f);」を入れておけば同じことが再現できる。
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」を作成せずにすむのでオススメ。
②敵の破壊動作を作成
ステップの確認
Step1~Step3
目的
弾オブジェクトと敵オブジェクトに「Rigidbody」をつける
敵オブジェクトの「Is Trigger」をオンにする
スクリプトの作成
以下のスクリプトを敵オブジェクトにアタッチします。
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);
}
}
動作確認
Step4
目的
以下のような感じで弾オブジェクトが敵オブジェクト衝突時にエフェクトをつける。
パーティクルの作成(エフェクト)
こちらの記事を参考にさせていただきました、
プレハブ化
作成したパーティクルをプレハブ化しておきましょう。
爆発なので「Explotion」にしておきました。
スクリプトの作成
衝突時のときに発生させるので、step1~step3で作成した「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);
}
}
動作確認
Step5
目的
点数を保持するスクリプトの作成
点数の加算時にデータを保持しておきたいので、アクセスが簡単にできる「ScriptableObject」を使っていきます。
新しく「DataScripts.cs」を作成しましょう。
以下の「Enemy_killed」に敵オブジェクトを倒した数を加算していきます。
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」を新しく作成していきます。
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の作成
作成したスクリプトのアタッチ
動作確認
③Plyaerの動作を作成
ステップの確認
Step1~Step2
目的
「Target」と「Main Camera」を「Player」の子オブジェクトにする
「Player」の子オブジェクトにすることで「Player」の動き、回転にカメラとターゲットが追従するため、
「Player」の向いている方向に自動でカメラが移動し、自動で弾が向いている方向に発車されるようになる。
※敵オブジェクトは増やしておきました。
Playerを操作するスクリプトを作成する
ここまででも、いい感じになりましたがGameScene上ですべてを完結させるため
「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]にアタッチする
動作確認
④シューティングゲームを仕上げる
ステップの確認
Step1~Step2
目的
※敵オブジェクトは、省略してますがコピーして以下のように増やしてあります。
スクリプトを作成する
X方向のみ動くスクリプト「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」を作成。
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);
}
}
動作確認
敵同士の衝突判定を避ける
現在の設定だと敵同士が衝突して、爆発してしまうのでスクリプトを見直す。
スクリプトの見直し
「Collision_Bullet.cs」を改良していく。
UnityのTag機能を利用して、衝突したオブジェクトの判別を行っていく。
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」にする。
動作確認
番外編
ステップの確認
目的
従来のスクリプトで左右上下の回転機能を追加すると左のようになる。
これをFPSのようなカメラワークに変更する(右図)
本記事では、番外編になる「Player操作スクリプトを改良しよう」を行っていきます。
Player操作スクリプトの改良
現在のスクリプトだと左右の回転に上下の回転が加わるとカメラワークがおかしくなってしまう。
カメラがだんだん傾いてしまうのを修正する
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);
}
}
}
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);
}
}
カメラワーク改良後 動作確認
Step3~Step4
目的
スクリプト
タイマーカウント用に「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」を追加しておく。
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を用意する
動作確認
Step5
目的
UIを作っていく(デザイン)
データ管理用のCSVを用意する
ランキングを表示するためのデータを用意します。
内容は以下のような感じにします。
csv名前:savedata.csv
【テキスト】
name num_enemy
AAA 5
BBB 10
CCC 7
DDD 12
EEE 3
FFF 3
【配置Path】
配置場所はプロジェクトの直下です。プロジェクト名が[Shooting]の場合
「Shooting/savedata.csv」となる感じです。
CSVを使ってデータベースを作っていく。
こちらを参考にして「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;
}
}
}
表示をするスクリプトを作成する
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をアタッチをする①
動作確認①
ソートを入れる
このままだと順位は分からないのでソートをしていきます。
こちらのサイトがとても分かりやすかったので使っていきます
独自クラスでListの作成方法
List並べ替えスクリプト
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; }
}
動作確認②
ランキングに登録をする
ランキング登録スクリプト
ボタンを押したときに「敵オブジェクトを倒した数」と「InputFieldに入れた名前」をcsvに登録するスクリプト「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をアタッチをする②
ボタンにクリック機能を追加する
動作確認③
ランキング表示に登録したユーザも反映させる
ランキングのオブジェクトがSetActive(true)になったときにランキング機能を呼び出せばよい。
そのため、「ShowRank.cs」のStartをOnEnableに変更する。
詳しくは、こちらが分かりやすかったので参考に
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」オブジェクトの子オブジェクトに変更する。
動作確認④
10体倒したとしておきます。
「あああ」と入力するとランキングに表示されているのが分かります。
Step5
目的
ゲームを仕上げます。
今のままだと終了するたびに、Unity Editorで再生しなおす必要がありますが、本来ゲームはEditorに依存しません。
なので今回は、【開始】⇒【ゲームをする】⇒【終了】⇒【結果表示】⇒【データの保存】⇒【初めから】の繰り返しを再現します。
つまり、Unity Editorから独立できるところまで行う!!
ステップの確認
今回は最終ステップでゲーム全体を仕上げていきます!
ほとんどが今までの応用ですので、今回初めて出る機能だけ紹介します。
1.Title画面を作成(新しいシーン)
新しくSceneを作成する。
Buttonにページ移動機能をつける
以下のようなスクリプトを作成して、ボタンの「On Click()」機能にアタッチする。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ButtonClickGameStart : MonoBehaviour
{
public void BottonClikStart(){
SceneManager.LoadScene("MainGame");
}
}
※ゲームシーンを「MainGame」という名前にしておく必要がある!
動作確認①
2.ランキングに「登録する」か「登録しないか」決めるボタンをつける
このページで作成した「ゲーム終了」画面にボタンをつけて、ランキングに登録するかそのまま終わるかの選択ができるようにする。
Buttonに機能をつけていく
①「登録する」を押した場合⇒ランキング登録画面を表示する
「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()」機能を設定する
②「しないで終了」を押した場合⇒「Title」画面に戻る
「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()」機能を設定する
ランキング表示時間を10秒にして、自動で「Title」に移動
ここまででほぼ完成しましたが、「登録する」を押した場合ランキングが表示されます。
ランキング画面には、ボタンをつけていないので「Title」画面に戻ることができない。
そこでランキングが表示されてから10秒で「Tilte」に自動移動するようにする。
スクリプトの改良
「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秒待ってからの画面移動)
ゲーム終了後の「敵を倒した数」のカウントを回避
ゲームが終了した後も、敵を倒すと点数が加算されてしまうので、それを修正。
点数を加算するスクリプトは、「Collision_Bullet.cs」なので、そこに「DataScripts.cs」の「Finish_Game」で分岐をする。
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などで自作してもよしです。