[Unity]声優ハッカソン参加してきました「ブレイクデイズ」

2016/2/26,27に行われた 声優ハッカソンというのに参加してきました。 https://rakutenwebservice.doorkeeper.jp/events/37786
そこで「ブレイクデイズ」というゲームをつくりました。

ADX2を使ったサウンド周りのワークフローについての記事をtatmosさんが書いていたので、自分も書こうと思ってこの記事を書いています。

- tatmosさんの記事。必見。
    - http://qiita.com/tatmos/items/847ae3b57cebb627cce8

- ゲーム動画
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/eaJDcQSFrs4/0.jpg)](https://www.youtube.com/watch?v=eaJDcQSFrs4)

- 企画資料
    - http://www.slideshare.net/hogemaru_/rakkathon02-breakdays

# ゲーム内容

キャラがたくさんでてくるシンプルなRPGです。
なるべくタップするだけで進むようなゲームデザインが特徴です。


# 声の収録・ボイス周りのワーク

この記事はボイス周りのワークについて語りたいと思います。
今回のハッカソンでは声優からボイスを収録できるので、ボイスファイルが大量にできるということが予想されました。なのでADX2を使う作戦でいきました。私は以前にADX2を調査してワークフローを知っていたので、手が空いていたY君というメンバーにCRIのURLだけ渡して、一切を任せることにしました。
http://www.adx2le.com/

サウンド周り、何を収録するか、という仕様はgoogleのSpreadSheetを使って以下のようにまとめました。
![WS000275.JPG](https://qiita-image-store.s3.amazonaws.com/0/116199/5413dbe8-f0b3-9fbc-eba7-5dc42ce8627f.jpeg)

まず、最初にこちらに渡される波形はwavファイルでした。収録した全ボイスを1ファイルでまとめて頂戴する、といった受け渡し方法でした。
まずそれを1ファイルずつに切り出す作業をやってもらいましたが、大変そうでした。（ヒトゴトｗ）

作成したキューシート(ADX2の用語)は以下の3個です

- SE 
    - 波形:15個
- VOICE 
    - 波形:90個(30個x3人)
- BGM
    - 波形:9個

作業フローは以下のようにしました。
Y君はADX2は初作業だったにもかかわらず、少ないリテイクでうまくいきました。
サウンド用のリポジトリをゲーム側のプロジェクトと完全に切り分けたのもポイントだったかもしれません。たまたま企画用のファイルのリポジトリを作っていたので、そこをサウンド用に利用しました。１つのリポジトリで全部やる、というのは若干無理があると今回感じました。サウンドファイル、でかいので。

![WS000274.JPG](https://qiita-image-store.s3.amazonaws.com/0/116199/da5927ba-d0f4-0e36-7fd7-b2ee0726eea4.jpeg)

ADX側の作業は以下のような感じです。
![WS000273.JPG](https://qiita-image-store.s3.amazonaws.com/0/116199/f59cae9c-adff-4519-61d4-d0825e02cc9a.jpeg)


波形ファイルの容量の総和が162MBでした。
それを最低品質のHCAで圧縮すると、以下のようになり、約11MBほどになったので、6.8%の圧縮率となりました(削減率93.2%)

- BGM
    - 9082KB
- SE
    - 230KB
- Voice
    - 1152KB

これだけファイル数が多いと、データ管理・作業分担・圧縮率などでADX2のありがたみが出たなと感じました。

# 収録環境

収録は専門の施設ではなく、普通の会議室でした。一部はこもったようなボイスになっており、それは収録ミスなのだと思います。
キャラクタの性格等を考えていったのですが、発声してもらった後、"どうですか？"と聞かれて困りました。良いですね、以外の返答ができないので。比較できないと良いも悪いも分からないな、と思ったので、同じボイスを3回発生してもらい、後で良いものを切り出す、というやり方にしました。結果的に、そのやり方でうまくいきました。

# ソース

ソースを晒しますが、CRIさんのExampleのプロジェクトを理解して使ったほうが１００倍いいと思います。


```SoundManagerAdx2.cs
using UnityEngine;
using System.IO;
using System.Collections;

public class SoundManagerADX2 : MonoBehaviour {

	public CriAtomSource atomSourceBGM;
	public CriAtomSource atomSourceSE;
	public CriAtomSource atomSourceVOICE;
	public CriAtom criAtom;

	public float volume = 0.35f;

	void OnLevelWasLoaded(int level){
		StartCoroutine(initialize ());
	}

	// Use this for initialization
	IEnumerator Start () {

		StartCoroutine(initialize ());
		yield return null;

	}
	
	// Update is called once per frame
	void Update () {
	
	}

	public void playSE(string cueSheet, string cueName)
	{
		atomSourceSE.Stop ();
		atomSourceSE.cueSheet = cueSheet;
		atomSourceSE.cueName = cueName;
		atomSourceSE.volume = volume;
		atomSourceSE.Play ();
	}

	public void playBGM(string cueSheet, string cueName)
	{
		atomSourceBGM.Stop ();
		atomSourceBGM.cueSheet = cueSheet;
		atomSourceBGM.cueName = cueName;
		atomSourceBGM.volume = volume;
		atomSourceBGM.loop = true;
		atomSourceBGM.Play ();
	}

	public void playVoice(string cueSheet, string cueName)
	{
		atomSourceVOICE.Stop ();
		atomSourceVOICE.cueSheet = cueSheet;
		atomSourceVOICE.cueName = cueName;
		atomSourceVOICE.volume = volume;
		atomSourceVOICE.Play ();
	}

	public void playVoiceNoStop(string cueSheet, string cueName)
	{

		atomSourceVOICE.cueSheet = cueSheet;
		atomSourceVOICE.cueName = cueName;
		atomSourceVOICE.volume = volume;
		atomSourceVOICE.Play ();
	}


	IEnumerator initialize()
	{


		if (gameObject.GetComponent<CriAtomSource> () == null) {
			atomSourceBGM = gameObject.AddComponent<CriAtomSource> ();
			atomSourceSE = gameObject.AddComponent<CriAtomSource> ();
			atomSourceVOICE = gameObject.AddComponent<CriAtomSource> ();
			DontDestroyOnLoad (gameObject);
		}

			
		string[] files = {"SE", "BGM", "Voice"};


		foreach (var file in files) {
			var cueSheets = CriAtom.GetCueSheet(file);
			if(cueSheets != null){
				continue;
			}
			var file1 = Path.Combine (Path.Combine (CriWare.streamingAssetsPath, "ADX2"), file);
			var cueSheetName = Path.GetFileNameWithoutExtension (file1);
			var acbFilePath = file1 + ".acb";
			CriAtom.AddCueSheet (cueSheetName, acbFilePath, acbFilePath, null);
			CriAtom.GetAcb (cueSheetName);
		}

		yield return null;


	}
}

```

Singletonパターンとあわせて https://gist.github.com/tsubaki/e0406377a1b014754894
以下のように使ってました

```Sample.cs
SoundManagerADX2.Instance.playVoice("Voice", "nomal_voice_jin_01");
```

## 余談

ADX2はUnityから利用する時、初期化周りにやや難があると思っています。余談ですが、そこに軽く触れておきます。
まず、CRIWARE, Initializer, ErrorHandlerという3つのオブジェクトを置くことを強制されます。正直 "これはちょっとないな"と思ってます。新しいシーンを作るたびに３つ置くの？と思うからです。
すべてをソースコードでコントロールするやり方が許容されていないように感じます。UnityのCRIでない、本来のAudio周り(AudioSource)は、コンポーネントとソースコード、両方から扱えるので、そこに至ってないのでは、と感じます。UnityのAudio周りは、妙なgameobjectの配置を強制しないですよね？同じような使用感にしてほしいと感じます。たとえば、以下のようにすれば、CRIWAREは置かなくて良くなりますよね・・・。同じようにしてInitializerとErrorHandlerもナシにすればいいのではないかと思います。あるいはひとつのコンポーネントにまとめればいいのでは。少なくとも、３つ置くというのは"ない"かなぁ。
勝手なことを言って申し訳ございません。そもそも無料ではなく有料の製品ですので、色々な事情があると思います。

```sample2.cs
		var m = GameObject.Find ("CRIWARE");
		if (m == null) {
			m = new GameObject ("CRIWRE");
			DontDestroyOnLoad (m.gameObject);
		}

		if (m.GetComponent<CriAtom> () == null) {
			criAtom = m.GetComponent<CriAtom> ();
			string acfFilePath = Path.Combine (Path.Combine (CriWare.streamingAssetsPath, "ADX2"),"app.acf");
			CriAtomEx.RegisterAcf (null, acfFilePath);
		}
```

# 最後に

サウンド周りのアートワーク、かなりうまくいって満足です。
動画をみてもらえると分かりますが、このゲームはまだ完成してないので完成させたいです。