Unityでタイピングゲームを作る．

# はじめに
## About
この記事はUnity5(3D)を用いて簡単なタイピングゲームを作る，身内向けの講座のために作られた資料です。
以下のgithubのページからからTypingSystemモジュールをダウンロードしてください。

- [TypingSystemForUnity](https://github.com/icoico/TypingGameSystemForUnity)

> 今回はこの中身について詳しくは解説しません。興味ある方は中を覗いて見てください。

## サンプルゲームを見てみよう
- [こちら](https://dl.dropboxusercontent.com/u/75891514/TypinGameDemo/TypinGameDemo.html)で今回作るもののサンプルがプレイできます。(googleChromeではプレイできません)
- 文字を入力し続けるだけの単調なものです。

## TypingSystemの説明
TypingSystem.csにはTypingSystemというクラスが用意されており，タイピングゲームにおける文字列のタイピング判定を簡単にしてくれます。
主に以下のような使い方をします。

- 入力すべき文字列を設定する(初期化)。

- 入力されたアルファベット(キー)を渡す。[^1]
[^1]: TypingSystem単体ではキー入力受付処理を行いません。

- 入力判定の結果を受け取る。

> 例　「ドラえもん」という文字列のタイピングを行う。

> - TypyngSystemクラスに「ドラえもん」という文字列を渡して初期化する。

> - キー入力を監視し、入力されたキーをクラスに渡す。

>  - すると入力されたキーが正しいキーかどうかを判定し、内部で処理してくれます。「ドラえもん」という文字の場合は最初は「d」が正しいキーで、次は「o」、次は「r」、「a」、…といった感じですね。

> - すべて入力終わったかどうかを調べて、クリア処理を行う。


では実際に使ってみましょう～。

# ゲーム作成

## プロジェクトの作成　～　背景づくり

まずはUnityで新規プロジェクトを作成しましょう。
できたら、まずシーンを保存します。適当な名前で保存しましょう。

次に、背景を作りましょう。適当にPlaneを使います。
シーンに「3D Object」の「Plane」を追加します。
そして、メインカメラに映るように角度やサイズを調整します。メインカメラの方を動かしても構いません。
余裕のある人は見やすいマテリアルを設定して色を変えてみましょう。

![unity1.jpg](https://qiita-image-store.s3.amazonaws.com/0/82356/12466af1-fbc7-14a8-dcea-fb3906518fec.jpeg)

## テキストを配置
入力する文字を表示するために、テキスト配置しましょう。
今回は3DTextを使います。
「3D Object」から「3D Text」を追加しましょう。

文字サイズの調整をします。
デフォルトのままだと、transformのスケールを弄って大きくすると、荒いフォントになると思います。
これはテキストを小さいフォントサイズで出力した後大きくしているだけなので、解像度が低いままだから起きる現象です。
InspectorのTextMeshコンポーネントのFontSizeを大きくしましょう。
そしてその後、スケールを弄って画面に収まるように調節します。

また、Anchorの項目から、UpperLeftになっているのを、UpperCenterに変更します。

このオブジェクトをコピーして、同じものをもう一つ作ります。
こちらは入力すべきアルファベットを表示するTextです。名前をわかりやすい別の名前にしましょう。

さらに、この二つのオブジェクトをまとめて管理したいので、親オブジェクトを同じにしていきます。
空のオブジェクトを作成（名前をTypingObjectとしておきましょう。）し、そのオブジェクトに上で作成した二つのテキストオブジェクトをドラッグ＆ドロップします。
これでオブジェクトの親子関係ができました。

![unity2.jpg](https://qiita-image-store.s3.amazonaws.com/0/82356/c3088141-3495-96fe-f9e9-df49eb4e5fd6.jpeg)

![unity3.jpg](https://qiita-image-store.s3.amazonaws.com/0/82356/f61c1cbd-70b4-5348-9ec1-14f5d03ab3dd.jpeg)

## スクリプトを書こう
いよいよスクリプトを書いていきます。

### テキストへの参照を追加
先ほど作成したTypingObjectにスクリプトをアタッチします。
まずは二つのテキストへの参照を追加しましょう。変数名は何でも良いです。

```csharp
public TextMesh stringTextMesh;
public TextMesh alphabetTextMesh;
```

追加したら、保存してUnityに戻ります。
InspectorのScriptに、TextMeshの欄が増えているかと思います。
publicで指定したメンバ変数は、Inspectorに表示されるようになり、ここで中身を設定できます。
それぞれに子オブジェクトのテキストオブジェクトをドラッグ＆ドロップして設定しましょう。
![unity4.jpg](https://qiita-image-store.s3.amazonaws.com/0/82356/0122a47c-1514-5bea-eb71-8f17cf080a64.jpeg)

### テキストを操作する
次に、テキストを更新するための関数を作成します。
スクリプトに以下の関数を追加します。


```csharp
void UpdateText(){
	stringTextMesh.text = "<color=red>かきくけこ</color>";
	alphabetTextMesh.text = "kakikukeko";
}
```

そしてStart()の中身でUpdateText()を読んでやります。

```csharp
void Start () {
	UpdateText ();
}
```

それではデバッグしてみましょう。
テキストが変わりましたか？

### TypingSystemの導入
いよいよTypingSystemクラスを使います。
プロジェクトのフォルダのAssetsにダウンロードしたTypingSystem.csを追加します。
UnityのProjectビューにTypingSystemが追加されたことを確認してください。

次に、先ほどのスクリプトに戻ってTypingSystemを呼び出す準備をします。
メンバ変数の宣言をするため、以下の文をスクリプトに追加しましょう。

```csharp
TypingSystem ts;
```

さらに、TypingSystemの初期化を行います。
以下のコードをStart()に追加しましょう。

```csharp
ts = new TypingSystem ();
```

ではTypingSystemの使い方を説明していきます。
最初の方に説明した項目を思い出しながらやっていきましょう。

### 文字列の初期化を行う

まずはTypingSystemに入力すべき文字の情報を与えましょう。
> 注意してほしいですが、現在ゲームオブジェクトのテキストに書いてある内容と、入力すべき文字の情報はまだリンクしていません。

以下のコードを書いて初期化文字列を与えてやります。文字は何でもいいです。

```csharp:Start()
ts.SetInputString ("どんこうれっしゃ");
```

さらに、UpdateTextの内容を書き換えます。

```csharp
void UpdateText(){
	stringTextMesh.text = "<color=red>" + ts.GetInputedString() + "</color>" + ts.GetRestString();
	alphabetTextMesh.text = "<color=red>" + ts.GetInputedKey() + "</color>" + ts.GetRestKey();
}
```

ここで、一旦デバッグしてみましょう。
テキストが、「どんこうれっしゃ」になりましたか？
これで実際のゲーム画面のテキストと、TypingSystem内で処理するテキストのリンクができました。

### キー入力を受け付ける
ここまでくればあと少しで、基本的な部分が完成します。

とりあえず、以下のコードをUpdate()内に記述してみてください。
コピペで構いません。

```csharp
string[] keys = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "-",};
foreach (string key in keys) {
	if(Input.GetKeyDown(key)){
		if (ts.InputKey (key) == 1) {
			UpdateText ();
		}
		break;
	}
}
```

> string[] keysは受け付けるキーの配列です。Update内で毎回生成したくないという人はメンバ変数にして別で初期化しましょう。

これで実行してみてください。
タイピングできるようになりましたか？

![unity5.jpg](https://qiita-image-store.s3.amazonaws.com/0/82356/351e97a6-cd35-eda5-4c4c-5dd1d4d20589.jpeg)

## 単語帳を作る
さて、基本が完成したところで、入力する単語を準備した単語帳を作りましょう。

ダウンロードしたTypingSystemの中にDictionary.txtと言うものがあるので、とりあえずそれをコピーしてきてプロジェクトフォルダに追加しましょう。
このテキストファイルに単語を列挙して、スクリプトで読みこみます。

### Dictionaryクラスの作成

まず、空のオブジェクトを追加しましょう。適当名前を付けてください。
そして新しいスクリプトを追加します。名前はDictionaryにします。

このスクリプトの役割は、ランダムな単語を取得して返す、というものです。
そのために、内部で単語帳を読みこみます。

スクリプトを記述していきましょう。
いかのメンバ変数を追加します。

```csharp:Dictionary.cs
public TextAsset resouce;
string[] words;
```

そして、次の関数を追加します。

```csharp:Dictionary.cs
void LoadDictionary(){
	string trimedData = resouce.text.Replace ("\r", "");
	char[] dem = {'\n'};
	words = trimedData.Split(dem);
}
```

この関数は、リソース（単語帳）から単語を読みこんで、配列に格納していく関数です。
この関数を、Start()ではなくAwake()という関数内で呼びます。

```csharp:Dictionary.cs
void Awake(){
	LoadDictionary ();
}
```

次に、ランダムな単語を返してくれる関数を作ります。

```csharp:Dictionary.cs
public string GetRandomWord(){
	return words[Random.Range(0, words.Length)];
}
```

最期に、Unityに戻ってInspectorからresouceにdictionary.txtをドラッグ＆ドロップしておきましょう。
これで準備ができました。

![unity6.jpg](https://qiita-image-store.s3.amazonaws.com/0/82356/d8512a3b-6627-accb-ac32-532729e14b1a.jpeg)


> 補足：なんでTypingObject内で単語帳を読みこまないの？
> > A. それでも構いませんが、いくつかの理由から今回は避けています。
まず、TypingObjectはいくつも生成する可能性があり、毎回リソースを読みこむのは効率的ではありません。
静的なメンバにすれば共有できて1度の読み込みで済みますが、「複数のタイピングオブジェクトが共有の辞書機能を持つ。」よりも「複数のタイピングオブジェクトが同じ辞書オブジェクトを持っている。」の方がオブジェクト指向的です。別に前者でも悪くないんですけどね。
あとは、別のオブジェクトのクラスを参照する練習をするという意味もあります。

### DictionaryクラスをTypingObjectで使う。

では、TypingObject.csに戻りましょう。
メンバ変数に以下を追加します。

```csharp:TypingObject.cs
public Dictionary dictionary;
```

いつものようにUnity側からDictionaryオブジェクトをこの変数にセットしておきます。

あとは、typingSystemのSetInputStringするときにこのdictionaryからランダムな単語を取得してセットしてやります。引数を以下のように変えてやります。

```csharp:TypingObject.cs
ts.SetInputString (dictionary.GetRandomWord());
```

デバッグしてみましょう。単語帳から読みこまれていますか？

### 1単語をタイプし終わると次の単語に移る

このままでは入力し終わっても次の単語がでてきません。
出てくるようにTypingObjectを更新しましょう。

Update()内に、以下の文を追加します。

```csharp:TypingObject.cs
if (ts.isEnded ()) {
	ts.SetInputString (dictionary.GetRandomWord());
	UpdateText ();
}
```

isEnded()はTypingSystemの関数で、入力が終わっていた場合trueを返します。
入力が終わっていた場合、次の単語をセットします。
これで完了です。デバッグしてみましょう。


----

おつかれさまです。これでタイピング部分の一通りの解説は終わりです。
あとはここまで作成した物を使って、ゲームにしていきましょう。