Unityで文字の3Dモデルを作る（FlyingText3Dアセット編）

文字を3Dモデルとして扱う事で、巨大な文字を積み上げたり転がしたりしたいと思い、試してみました。
前回は[VTextというアセットを試してみましたが、Dynamic Batching問題があって断念しました](http://qiita.com/arumani/items/5c693e2c52d81affc905)。

結論を先に書くと、このFlyingText3Dは動作はもちろん、ドキュメントがとても分かりやすく詳細で、APIも必要なものが揃っているため、とても良い感じです。

#FlyingText3Dについて
Asset Storeで公開されている有料アセットです。
**FlyingText3D**
https://www.assetstore.unity3d.com/en/#!/content/3627

※クリックでYouTubeの説明動画に飛びます
[![FlyingText3D説明動画](https://img.youtube.com/vi/YZvyolCu2AU/0.jpg)](https://youtu.be/YZvyolCu2AU)

> Requires Unity 3.5.7 or higher.
> Create real dynamic 3D text from TrueType fonts right in Unity, quickly and easily! Everything you need for killer title screens and menus, or anywhere else you need to go beyond bitmaps. One simple command will make dynamic text at runtime, either as a single object or as multiple objects, where each one can have a collider and rigidbody. Or you can make text 3D prefabs directly in the editor.

価格は $19.95 （2016年9月11日時点）

#確認環境
- Unity v5.4.0f3
- Xcode v7.3.1
- MacBook Pro (OS X 10.11.5)

#デモを動かす
このアセットは全てimportするだけで動作します。

0. FlyingText3Dアセットをインポート（全てチェックでOK）
1. **Assets/FlyingText3D/Demos/GameOver Demo/_GameOver** シーンを開く
2. 実行

[![GAME OVERデモ動画](https://img.youtube.com/vi/0MhYEirfE-4/0.jpg)](https://youtu.be/0MhYEirfE-4)

#日本語フォントの利用
結論から書くと、日本語は使えますが若干の手順が必要です。
上記のデモシーンでそのまま試します。

##フォントファイルを用意する
今回はサンプルとしてIPAが公開している[IPAexフォント](http://ipafont.ipa.go.jp/node17)を利用します。
1. 使いたいフォントの **.ttf** ファイルを用意する
2. Finder上で拡張子を .ttf -> **.bytes** に変更する。例) ipaexm.ttf -> ipaexm.bytes
3. このファイルを **Assets** 以下の任意の場所に置く

※拡張子を変える理由は、このアセットとしてはフォントの生データを扱う必要があるが、.ttfだとUnityがフォントとして扱ってしまいそれを許さないため、と説明されています。
参考: Unityマニュアル[アセットバンドルにバイナリデータを含めて読み込む](http://docs.unity3d.com/ja/current/Manual/binarydata.html)

##フォントを指定する
1. Hierarchy で **FlyingText** を選択する
2. Inspector の **Font Settings** で、下図のように chintzy というフォントが指定されている右の選択アイコンをクリック
![flyingtext3d_japanese_sample2.png](https://qiita-image-store.s3.amazonaws.com/0/42051/942de6a5-daa1-54e7-5871-d6b47c70cc45.png "このアイコンをクリックしてフォント選択")
3. 上記で準備したフォントファイルを指定する。例) ipaexm
4. 実行する（この時点でフォントの読み込みに問題が無いかどうかの確認）
5. GameOver Demo ディレクトリにある **GameOverControl.js** をエディタで開き、12行目あたりにある"GAME\<br\>OVER"を日本語に書き換える

```javascript
FlyingText.GetObjects ("ゲーム<br>オーバー")
```

実行すると日本語が表示される。
![flyingtext3d_japanese_sample1.png](https://qiita-image-store.s3.amazonaws.com/0/42051/99afd684-2d99-ba90-30e9-a13b35ef578d.png "日本語フォントが表示されている")

##スクリプトファイルの文字エンコーディングについて
日本語（マルチバイト文字）を扱う場合、文字エンコーディングは以下にする必要があります。（ドキュメントのP.17参照）
- **C#: UTF-16**
- UnityScript: UTF-8 または UTF-16

##フォントの複数指定について
上記の手順では既存のフォント指定を変更していますが、追加する事も可能です。これは、1つのFlyingTextオブジェクト中で複数種類のフォントを利用したい場合に用います。

1. **Font settings** の **+ Add Font** をクリックする
2. フォント名の右の選択アイコンで、追加したいフォントを選ぶ
3. **Character settings** の **Default Font** で、無指定の場合に用いるフォント番号を指定する。

手順3.に注意して下さい。複数フォントを指定した場合、何も指定しないと手順3.で指定したものが常に採用されます。つまりそのフォントデータに指定した文字が含まれていない場合（英字フォントで日本語を表示した場合など）、次のフォントから検索されたりはせず、単に表示をスキップされるという事です。

この例では、IPAex明朝フォントを追加した上で、デフォルトをそちら（#1）にしています。
![flyingtext3d_japanese_sample3.png](https://qiita-image-store.s3.amazonaws.com/0/42051/b081c372-d23e-5470-52d6-e009d3327297.png "フォント番号1を指定している")
4. デフォルト指定したフォント以外を使う場合は、文字列中に **Tag**（マークアップのタグ）を書きます。閉じるタグは不要（無視される）、クォートも不要という事で見た目ちょっとモヤモヤしますが、無駄にパースさせても仕方ないので我慢します。

```javascript
FlyingText.GetObjects ("<size=0.5>ゲーム<br><font=0><size=1>OVER")
```
この例は以下の指定を行っています
- **\<size\>**で文字の大きさ
- **\<br\>**で改行
- **\<font\>**で使用するフォント番号

実行すると無事に2種類フォント混在で表示されました。
![flyingtext3d_japanese_sample4.png](https://qiita-image-store.s3.amazonaws.com/0/42051/09afe0ca-0d19-9b9b-7563-700c5abadb21.png "2種類のフォントが混在している")

#スクリプトで動的に文字を生成する
##(1)文字列全体を単一オブジェクトとして扱う
0. シーンに空のGame Objectを追加する
1. そのオブジェクトに **Add Component -> FlyingText3D -> FlyingText**でコンポーネントを追加する
2. 追加したコンポーネントの **Font settings -> + Add Font**でフォントを追加し、利用するフォントを指定する（上記のデモ手順参照）
3. 同オブジェクトに、更にAdd Componentで新規スクリプトを追加する

```c#:OneObjectSample.cs
using UnityEngine;
using System.Collections;

public class OneObjectSample : MonoBehaviour {

	void Start () {
		FlyingText.GetObject("これは1つのGame Objectです");
	}
	
	void Update () {
	}
}
```
実行結果は以下です（全体が見えるように位置は適当に手動でずらしました）
![flyingtext3d_coding_1.png](https://qiita-image-store.s3.amazonaws.com/0/42051/a43fd732-f64f-2729-5569-27cf95dbc6ba.png "flyingtext3d_coding_1.png")

**GetObject()**は、指定した文字列を1つのMeshとして作ります。
![flyingtext3d_mesh_1.png](https://qiita-image-store.s3.amazonaws.com/0/42051/880d433d-4aaf-7866-7322-b60e67b21b06.png "flyingtext3d_mesh_1.png")


ここでは必須パラメータである文字列のみ指定しましたが、位置やサイズ、マテリアルや行間などを指定する事もできます。これらを指定しなかった場合は、手順2.で追加したコンポーネントの **Inspectorで設定した値**が使われます。もしくは、 **コードでもデフォルト値を設定**する事ができます。詳しくはドキュメントを参照して下さい。

##(2)1文字ごとに別のオブジェクトとして扱う
スクリプト以外は[(1)文字列全体を単一オブジェクトとして扱う](##(1)文字列全体を単一オブジェクトとして扱う)と同じです。

なお例として文字に重力を与えたかったので、各文字にRigidbodyをつける設定をしました。
**Flying TextのInspector -> GameObject settings -> Add rigidbodies にチェック**
![flyingtext3d_coding_3.png](https://qiita-image-store.s3.amazonaws.com/0/42051/6cf92f95-fae0-807c-7e75-1ddb31910a14.png "flyingtext3d_coding_3.png")

```c#:MultiObjectSample.cs
using UnityEngine;
using System.Collections;

public class MultiObjectSample : MonoBehaviour {

	void Start () {
		GameObject objectParent = FlyingText.GetObjects("1文字ごとに別オブジェクト");
		Rigidbody[] rigidBodies = objectParent.GetComponentsInChildren<Rigidbody>();
		for(int i = 0; i < rigidBodies.Length; i++) {
			if(Random.value < 0.5f) {
				Rigidbody rb = rigidBodies[i];
				rb.useGravity = false;
			}
		}
	}
	
	void Update () {
	}
}
```
実行結果。重力を無効にされなかった文字（ランダム）のみ落ちていきます。
![flyingtext3d_coding_2.png](https://qiita-image-store.s3.amazonaws.com/0/42051/eb99c6e7-9f90-3535-fb67-1bc1bf467905.png "flyingtext3d_coding_2.png")

**GetObjects()**は、1文字ごとにメッシュを分けて生成した上で、それらを保持する親となるGame Objectを作成します。
![flyingtext3d_mesh_2.png](https://qiita-image-store.s3.amazonaws.com/0/42051/fc282966-618e-4a0c-c701-3143ed4c5e93.png "flyingtext3d_mesh_2.png")

この例では先述の通りRigidbodyを作成するオプションをつけているので、各文字のRigidbodyを **GetComponentsInChildren<Rigidbody>**で取得する事ができます。

なおこの2関数以外にも、以下のようなAPIが用意されています。

- 最初から配列で文字単位のGameObjectを作る
- 文字列を更新する
- 初回のモデル作成時間によるカクつきを避けるために事前にモデルを作る


以上により、もともと私がアセットを試した目的である、巨大な文字を転がしたり積み上げたりというのが実現できそうな事が分かりました。

#補足
##初期化処理の実行タイミング
FlyingText3Dの初期化処理はAwake()で行われます。初期化処理前に自前スクリプトからGetObjectなどを呼び出してしまうとエラーになるため、実行順が確実に初期化処理よりも後になるようにします。
Start()やUpdate()であればAwake()より確実に後になります（[Unity - マニュアル: イベント関数の実行順](http://docs.unity3d.com/ja/current/Manual/ExecutionOrder.html)）。もし何らかの理由で順序を指定したい場合は **Edit -> Project Seetings -> Script Execution Order**で指定します。