[Unity]シンプルな音ゲーの作り方 part2 ゲーム製作


# 前提
あくまで，シンプルに作る方法なので実装に問題がたくさんあります！

# リンク
[part1](http://qiita.com/Teach/items/12a2e3b2f8c09dbbc5bd)
part2 : この記事


## 2. ゲームシーンの作成
1. CSVを読み込む
2. タイミングに合わせてノーツを生成
3. ノーツが降ってくるようにする．
4. タイミングよく押したらGoodにする．



## 1. CSVを読み込む
まずは新しいシーンを作成してください．ここではGameSceneと名付けました．

そして以下の画像のようにUIを作成してください．![スクリーンショット 2016-09-17 18.10.49.png](https://qiita-image-store.s3.amazonaws.com/0/113504/d4089805-9ddf-cc89-5244-63f007ca37a9.png "スクリーンショット 2016-09-17 18.10.49.png")

ただし，ピンク色のバーはCubeです．
![スクリーンショット 2016-09-17 18.13.00.png](https://qiita-image-store.s3.amazonaws.com/0/113504/e17fd1d8-7527-47e6-6c3c-fbeeccb7ac57.png "スクリーンショット 2016-09-17 18.13.00.png")


カメラのProjectionをOrthographicにすることでこのように映ります．
![スクリーンショット 2016-09-17 18.12.37.jpg](https://qiita-image-store.s3.amazonaws.com/0/113504/8b24b92f-2611-90c2-4d7e-dd198c875b91.jpeg "スクリーンショット 2016-09-17 18.12.37.jpg")

RigidBodyをアタッチして，UseGravityのチェックを外します．
(RigidBodyをアタッチしないと当たり判定とれないため)

GameControllerというScriptを作成し，以下のように入力してください．

```csharp:GameManager.cs
using UnityEngine;
using System.Collections;
using System.IO;
using System;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    private float[] _timing;
    private int[] _lineNum;

    public string filePass;

    void Start(){
        _timing = new float[1024];
        _lineNum = new int[1024];
        LoadCSV ();
    }

    void LoadCSV(){
        
        TextAsset csv = Resources.Load (filePass) as TextAsset;
		Debug.Log(csv.text);
        StringReader reader = new StringReader (csv.text);
		
		int i = 0;
        while (reader.Peek () > -1) {

            string line = reader.ReadLine ();
            string[] values = line.Split (',');
            for (int j = 0; j < values.Length; j++) {
                _timing [i] = float.Parse( values [0] );
                _lineNum [i] = int.Parse( values [1] );
            }
            i++;
        }
    }
}

```

GameControllerという名前の空のオブジェクトを作成し，アタッチしてください．

FilePassには「CSV/TestTiming」と入力してください．

![スクリーンショット 2018-08-23 16.54.11.png](https://qiita-image-store.s3.amazonaws.com/0/113504/0e03017f-bed4-9e31-8b88-b04a3eecb7a3.png "スクリーンショット 2018-08-23 16.54.11.png")


## 2. タイミングに合わせてノーツを生成

GameControllerを以下のように修正してください．

```GameController.cs

using UnityEngine;
using System.Collections;
using System.IO;
using System;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

	public GameObject[] notes;
	private float[] _timing;
	private int[] _lineNum;

	public string filePass;
	private int _notesCount = 0;

	private AudioSource _audioSource;
	private float _startTime = 0;

	public float timeOffset = -1;

	private bool _isPlaying = false;
	public GameObject startButton;


	void Start(){
		_audioSource = GameObject.Find ("GameMusic").GetComponent<AudioSource> ();
		_timing = new float[1024];
		_lineNum = new int[1024];
		LoadCSV ();
	}

	void Update () {
		if (_isPlaying) {
			CheckNextNotes ();
		}
			
	}

	public void StartGame(){
		startButton.SetActive (false);
		_startTime = Time.time;
		_audioSource.Play ();
		_isPlaying = true;
	}

	void CheckNextNotes(){
		while (_timing [_notesCount] + timeOffset < GetMusicTime () && _timing [_notesCount] != 0) {
			SpawnNotes (_lineNum[_notesCount]);
			_notesCount++;
		}
	}

	void SpawnNotes(int num){
		Instantiate (notes[num], 
			new Vector3 (-4.0f + (2.0f * num), 10.0f, 0),
			Quaternion.identity);
	}

    void LoadCSV(){
        
        TextAsset csv = Resources.Load (filePass) as TextAsset;
		Debug.Log(csv.text);
        StringReader reader = new StringReader (csv.text);
		
		int i = 0;
        while (reader.Peek () > -1) {

            string line = reader.ReadLine ();
            string[] values = line.Split (',');
            for (int j = 0; j < values.Length; j++) {
                _timing [i] = float.Parse( values [0] );
                _lineNum [i] = int.Parse( values [1] );
            }
            i++;
        }
    }

	float GetMusicTime(){
		return Time.time - _startTime;
	}
}

```

次にノーツプレハブを作成します．
![スクリーンショット 2016-09-17 18.26.07.png](https://qiita-image-store.s3.amazonaws.com/0/113504/11f6a3a9-f9e9-281a-46d6-1e9e10877dee.png "スクリーンショット 2016-09-17 18.26.07.png")

OnTriggerを使うのでBoxColliderのIsTriggerにチェックをいれます。

これの同じTranformのNotesを
Notes0~4まで作成し，プレハブ化してください．
そして，それらをGameControllerにアタッチしてください．

また，startButtonのところにLineの下にあるボタンをアタッチしてください．
![スクリーンショット 2018-08-23 17.04.18.png](https://qiita-image-store.s3.amazonaws.com/0/113504/ca38f3c1-34ee-8436-a225-9d3ae3507a42.png "スクリーンショット 2018-08-23 17.04.18.png")

また，音源を以下のように作成します

![スクリーンショット 2018-08-23 17.08.33.jpg](https://qiita-image-store.s3.amazonaws.com/0/113504/a4418103-7b40-04c0-8ecf-1849c94f6215.jpeg "スクリーンショット 2018-08-23 17.08.33.jpg")

StartButtonがクリックされた時にGameControllerのStartGame()を呼ぶように設定

![スクリーンショット 2018-08-23 17.17.24.jpg](https://qiita-image-store.s3.amazonaws.com/0/113504/819c11ba-f339-56fa-7437-5c1d29612c3e.jpeg "スクリーンショット 2018-08-23 17.17.24.jpg")

再生してStartボタンを押すことによって
音楽に合わせて以下のようにノーツが生成されていればOKです．

![スクリーンショット 2018-08-23 17.13.20.png](https://qiita-image-store.s3.amazonaws.com/0/113504/5ff822b7-2d5c-bbce-0590-64c0eefba270.png "スクリーンショット 2018-08-23 17.13.20.png")



### 3. ノーツが降ってくるようにする．

以下のようにNotesScriptを作って5つのNotesにアタッチしてください．

```csharp:NotesScript.cs
using UnityEngine;
using System.Collections;

public class NotesScript : MonoBehaviour {

	void Update () {
		this.transform.position += Vector3.down * 10 * Time.deltaTime;
		if (this.transform.position.y < -5.0f) {
			Debug.Log("false");
			Destroy (this.gameObject);
		}
	}

```

これでStartするとノーツが降ってくるようになります．


### 4. タイミングよく押したらGoodにする．
まず入力キーを取得するロジックを作ります．
GameUtilクラスを以下のように作成してください．

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

public static class GameUtil {

	public static KeyCode GetKeyCodeByLineNum (int lineNum) {
		switch (lineNum) {
			case 0:
				return KeyCode.D;
			case 1:
				return KeyCode.F;
			case 2:
				return KeyCode.G;
			case 3:
				return KeyCode.H;
			case 4:
				return KeyCode.J;
			default:
				return KeyCode.None;
		}
	}
}

```

(TimingMakerのほうのKeyCodeもGameUtilからとってきたほうがいいです．)




NotesScriptとGameControllerを以下のように修正してください．

```csharp:NotesScript.cs
using System.Collections;
using UnityEngine;

public class NotesScript : MonoBehaviour {

	public int lineNum;
	private GameController _gameController;
	private bool isInLine = false;
	private KeyCode _lineKey;

	void Start () {
		_gameController = GameObject.Find ("GameController").GetComponent<GameController> ();
		_lineKey = GameUtil.GetKeyCodeByLineNum(lineNum);
	}

	void Update () {
		this.transform.position += Vector3.down * 10 * Time.deltaTime;

		if (this.transform.position.y < -5.0f) {
			Debug.Log ("false");
			Destroy (this.gameObject);
		}

		if(isInLine){
			CheckInput(_lineKey);
		}
	}

	void OnTriggerEnter (Collider other) {
		isInLine = true;
	}

	void OnTriggerExit (Collider other) {
		isInLine = false;
	}

	void CheckInput (KeyCode key) {

		if (Input.GetKeyDown (key)) {
			_gameController.GoodTimingFunc (lineNum);
			Destroy (this.gameObject);
		}
	}
}
```

スコアを追加します．

```GameController.cs
using UnityEngine;
using System.Collections;
using System.IO;
using System;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

	public GameObject[] notes;
	private float[] _timing;
	private int[] _lineNum;

	public string filePass;
	private int _notesCount = 0;

	private AudioSource _audioSource;
	private float _startTime = 0;

	public float timeOffset = -1;

	private bool _isPlaying = false;
	public GameObject startButton;

	public Text scoreText;
	private int _score = 0;

	void Start(){
		_audioSource = GameObject.Find ("GameMusic").GetComponent<AudioSource> ();
		_timing = new float[1024];
		_lineNum = new int[1024];
		LoadCSV ();
	}

	void Update () {
		if (_isPlaying) {
			CheckNextNotes ();
			scoreText.text = _score.ToString ();
		}
			
	}

	public void StartGame(){
		startButton.SetActive (false);
		_startTime = Time.time;
		_audioSource.Play ();
		_isPlaying = true;
	}

	void CheckNextNotes(){
		while (_timing [_notesCount] + timeOffset < GetMusicTime () && _timing [_notesCount] != 0) {
			SpawnNotes (_lineNum[_notesCount]);
			_notesCount++;
		}
	}

	void SpawnNotes(int num){
		Instantiate (notes[num], 
			new Vector3 (-4.0f + (2.0f * num), 10.0f, 0),
			Quaternion.identity);
	}

	void LoadCSV(){
		int i = 0, j;
		TextAsset csv = Resources.Load (filePass) as TextAsset;
		StringReader reader = new StringReader (csv.text);
		while (reader.Peek () > -1) {
			
			string line = reader.ReadLine ();
			string[] values = line.Split (',');
			for (j = 0; j < values.Length; j++) {
				_timing [i] = float.Parse( values [0] );
				_lineNum [i] = int.Parse( values [1] );
			}
			i++;
		}
	}

	float GetMusicTime(){
		return Time.time - _startTime;
	}

	public void GoodTimingFunc(int num){
		Debug.Log ("Line:" + num + " good!");
		Debug.Log (GetMusicTime());

		_score++;
	}
}

```

Notes0~4のNotesScriptのlineNumを各番号に変えてください
例：Notes3 => lineNum に3をいれる

GameControllerの設定を以下のようにする．
![スクリーンショット 2016-09-17 18.34.44.png](https://qiita-image-store.s3.amazonaws.com/0/113504/5bad557a-f7f6-f683-aa60-2f5bc21446d1.png "スクリーンショット 2016-09-17 18.34.44.png")



完成．

# 修正
昔書いたコードでつっこみどころがかなりありますが，とりあえず
エラーが発生してまうミスあったのでそこを含めて修正しました．

Githubの方もエフェクト含めたものがあったので更新しました．
実装はかなり適当だったので参考程度にしていただけると！

![effect.gif](https://qiita-image-store.s3.amazonaws.com/0/113504/a9c5b44f-fd3e-a876-bb46-9c301c4c1e97.gif "effect.gif")