yumeの日記: Unity制作 メデューサ・ゲーム #47 2
--
エピソード1のオープニングアニメーションができたので、それをUnityにインポートして実際に動かしていく。
……のだが、前回のテストでのやり方は:
・Animatorにすべてのアニメーションを保持させる(アニメーションの名前は"Line + n"、nは2ケタ)
・Animatorにint変数lineCountとdoAnimeトリガーを持たせる
・Animatorで各アニメーションの偏移をポチポチつなげる
・各アニメーションの末尾に「リターンキー許可」の命令イベントを打つ
・テキストを送るごとに、各アニメのlinecountを送る
という流れだが、アニメーションの偏移をポチポチつなげるのが面倒臭すぎる。
リターン許可のイベントも、キャラクターアニメ内に入れるとアニメーションを修正するたびに打ち直すことになる。どこにイベントキーがあったか覚えなければならないのも不便だ。
そこで、各アニメーションの偏移をスクリプトで制御することにする。
新しいやり方は:
・各アニメーターにアニメーションを保持させる
・テキストを送るごとに、ディレクターが各アニメーターに「"Line + n"の名前のクリップを持ってるか」を尋ねて、持ってるなら再生する。
・リターンキーの許可は、カメラに付属するアニメーターがすべて制御する。
こうするとポチポチつなげる作業が不要になる。はずだ。
しかし、ちょっと厄介な部分もある。UnityのAnimatorには「この名前のClipって持ってますか?」と尋ねる関数が無いのだ。
尋ねずに強引にStateをPlayする、という手もあるにはあるがエラーがすごい量になっちゃうし、気持ち悪いし、ビルドによって問題が起きるかもしれない。
そこで、Playする前にAnimatorが指定する名前のStateを持っているかをなんとかして自前でチェックすることにした。
/// <summary>
/// すべてのActorに、lineCountに応じた演技を再生させる。
/// </summary>
/// <param name="lineCount">実行するライン番号</param>
private void PlayAct(int lineCount)
{
//番号を2桁ぞろえにする。
string ketazoroe = "";
if (lineCount < 10) ketazoroe = "0";
//各アニメーターが、lineCountと同じ番号のLine00アニメを持っているかチェック。
//持っているなら、アニメーションをプレイ。
for (int i = 0; i < charactorAnimes.Count; i++)
{
for(int j = 0; j < animeClips[i].Length; j++)
{
if(animeClips[i][j].name == ($"Line{ketazoroe}{lineCount}"))
{
charactorAnimes[i].Play($"Line{ketazoroe}{lineCount}", 0);
break;
}
}
}
}
animeClipsはAnimatorClip配列のリストで、AnimatorClip配列にはひとつのAnimatorが持つすべてのClipが格納されている。
Clipは番号順に保持されている。例えばクロノスはLine番号でいうところの、01~09までのラインを持つ。クリップインデックスにして0~8までのclipを持つ。
ディアティスは00、11、13~24のラインを持つ。クリップインデックスにして00~12までのclipを持つ。
必然的に各クリップのインデックスとLine番号はずれているので、ラインを送るたびにすべてのClipをチェックして名前を尋ねることになる。ちょっとバカっぽいけど他にうまい方法が思いつかない。
あらかじめすべてのLine番号と同数のクリップを(空クリップなどで埋めて)持たせればチェックする必要はなくなるが、それはそれでばかばかしいし、待機中ループするアニメーション、今回の場合ミスティのいびきのような処理をする場合にちょっと不便だ。
まぁとにかく綺麗に動くのでこれはこれでよし。
カメラの制御はUnity側ですべて処理する。イベントキーもカメラが持つので、アニメーションを修正しても、ただSpineから出力されるファイルをすべてUnityにすでにあるファイルに上書きするだけで、問題なく動く(Clipを追加する場合は、AnimatorにそのClipを登録する必要はある)。
そういうわけで、アニメーションを作る>Unityでチェックする>修正する の流れが滞りなくできるようになった。もちろんアニメーションの制作作業自体は、確認も含めてほとんどはSpine上で完結する。
テクスチャ解像度の件が気になったので、1024に分割した背景をSpineに配置し、それをUnityにインポートしたが、半端な解像度の時に境界線が見える問題が起きてしまった。ここで悩んでも仕方ないので一旦ミスティの部屋の画像は一枚のテクスチャとしてインポートしておく。この部分をあとで直すのは、それほど難しくはない。
というか、もしかすると、Unityのテクスチャは2048でインポートされているような……? ズームした状態で背景を見ると少しボケている。まぁとりあえずOK。背景はボケてても(この場合は)そんなに問題はないので、このままいってもいいかも。
ともあれ、カットシーン1-1が完成した。
webgl版でも正常に動いているように見える。よかったよかった。
重箱の隅的な (スコア:0)
微々たる差かもしれないけど、2重ループの内側で毎回文字列変換するのではなく、
PlayAct()の先頭で文字列変換して使いまわした方が良さそう。
// ライン番号を("Line" + 数値2桁)のクリップ名に変換する
var targetClipName = $"Line{lineCount:00}";
// Assert(lineCount < 100);
...
if(animeClips[i][j].name == targetClipName)
Linq (スコア:0)
使えるかな?
内部ループは
if(animeClips[i].Any(a => a.name == ($"Line{ketazoroe}{lineCount}")))
{
charactorAnimes[i].Play($"Line{ketazoroe}{lineCount}", 0);
}
で等価だと思う。