Unity のアニメーションシステムを使うと、スキンメッシュされたキャラクターを見事にアニメーション化することができます。アニメーションシステムは、アニメーションのブレンド、ミキシング、加法的アニメーション、歩行周期の同期、アニメーションのレイヤー、さまざまな方法によるアニメーション再生のすべての要素 (時間、速度、ブレンドウェイト) の制御、頂点ごとに 1、2 または 4 ボーンを割り当てたメッシュスキニングだけでなく、物理ベースのラグドール、プロシージャルなアニメーションもサポートしています。最良の結果を得る為に、最適なパフォーマンスのためのキャラクターモデリング を参照してください。Unity で最適なパフォーマンスのリグキャラクターを作成するための、最良の方法と技法について説明しています。
キャラクターのアニメーション化は、キャラクターにゲーム世界を 移動 させ、必要に応じて適切な アニメーション をさせる、という、2 点から成り立っています。キャラクターの移動に関してもっと知りたい場合は、キャラクターコントローラー を参照してください。このページでは、アニメーションに焦点をあわせています。実際のキャラクターのアニメーション化は Unity のスクリプトインターフェースを通して行われます。
実例デモ でダウンロードしてアニメーション化したキャラクターの事前設定を見ることができます。このページで基礎を学んだ後、アニメーションスクリプトインターフェース も参照してください。
最近のゲームでは、キャラクターをスムーズにアニメーション化するために、アニメーションのブレンディングが必要不可欠な機能になっています。アニメーターは、例えば、歩く繰り返し、走る繰り返し、アイドル (休止) 状態のアニメーション、射撃のアニメーションなど、個別のアニメーションを作成します。ゲーム中のどの時点でも、アイドルのアニメーションから歩行サイクルまたはその逆へと、望みの移行ができなければなりません。当然、移行はスムーズで突然の急なつかえは避ける必要があります。
ここでアニメーションのブレンディングの出番です。Unity 内では、同じキャラクター上で再生するアニメーションの数は制限されていません。すべてのアニメーションが混合または追加されて、最終的なアニメーションを作成します。
最初のステップは、アイドルと歩行アニメーションの滑らかなキャラクターブレンドを作成することです。スクリプトでの作業を楽にするために、まずアニメーションの Wrap Mode を Loop に設定します。それから、スクリプトのアニメーション 1 つだけを再生するために Play Automatically をオフにしておきます。
最初のキャラクターをアニメーション化するためのスクリプトは、とても単純です。キャラクターの移動する速度を検知し、その後、歩行、またはアイドルのアニメーションにフェードさせるだけです。この簡単なテストのために、標準の入力軸を使う事にします。
function Update () {
if (Input.GetAxis("Vertical") > 0.2)
animation.CrossFade ("walk");
else
animation.CrossFade ("idle");
}
プロジェクトで、このスクリプトを使うには:
再生ボタンを押すと、キャラクターは、上向きの矢印キーを押し続けると、その場で歩き始め、キーを離すとアイドルのポーズに戻ります。
レイヤーは、アニメーションのグループ化とウェイト (重み付け) 優先順位の決定を行う上で、非常に有用な概念です。
Unity のアニメーションシステムでは、ブレンドするアニメーションクリップの数に上限はありません。手動でブレンドウェイトを設定する事もできますが、ウェイトを自動的にアニメーション化する animation.CrossFade() を使うと簡単です。
例えば、歩行サイクルと走行サイクルがあり、両方ともウェイトは 1 (100%) であるとします。Unity が最終的なアニメーションを生成するときにはウェイトを正規化するので、歩行サイクルのアニメーションが 50% 、走行サイクルも 50% になります。
ただし、一般的に、2 つのアニメーションを再生する場合、よりウェイトを持つアニメーションを優先したほうが良いでしょう。もちろん手動で重みの合計値を 100% にすることも可能ですが、この目的であればレイヤーを使う方が簡単です。
例えば、射撃のアニメーション、アイドル、歩行サイクルのアニメーションがあるとします。歩行とアイドルのアニメーションは、プレイヤーの速度をベースにブレンドされますが、プレイヤーが射撃するときは、射撃アニメーションだけを表示する必要があります。従って、基本的に射撃アニメーションの優先順位が高い、という事になります。
これを行う簡単な方法は、射撃中にも歩行やアイドルのアニメーションを単純に再生し続けることです。これを行うには、射撃アニメーションを、アイドルと歩行のアニメーションより高位のレイヤーに作成する必要があります。つまり、射撃アニメーションが最初にブレンドウェイトを受け取る事になります。射撃アニメーションがブレンドウェイトを 100% 使っていない場合に限り、歩行とアイドルのアニメーションはウェイトを分配されます。射撃アニメーションに CrossFadeするときは、ウェイトは短時間で 0 から 100% に変化します。最初のうちは、歩行とアイドルのレイヤーにもブレンドウェイトが分配されていますが、完全に射撃アニメーションへ移行すると、まったく配分を受け取りません。これがまさに必要な機能です。
function Start () {
// すべてのアニメーションがループするように設定します
animation.wrapMode = WrapMode.Loop;
// "射撃" は除きます
animation["shoot"].wrapMode = WrapMode.Once;
// idle と walk を下層レイヤーに加えます (デフォルトのレイヤーは常に 0)
// これによって 2 つのことが起こります
// shoot と idle/walk は違うレイヤーにあるので
// CrossFade を呼び出すときに互いの再生に影響を与えません。
// shoot は上層のレイヤーなので、フェードインするときに、その アニメーションは
// idle/walk のアニメーションに取って代わります。
animation["shoot"].layer = 1;
// すでに再生中のアニメーションを停止します。
//(ユーザーが play automatically を無効にするのを忘れた場合)
animation.Stop();
}
function Update () {
// 押されたキーによって
// walk アニメーションか idle アニメーションを再生します
if (Mathf.Abs(Input.GetAxis("Vertical")) > 0.1)
animation.CrossFade("walk");
else
animation.CrossFade("idle");
// 射撃
if (Input.GetButtonDown ("Fire1"))
animation.CrossFade("shoot");
}
デフォルトでは、animation.Play() と animation.CrossFade() が同じレイヤー内にあるアニメーションを停止したりフェードアウトします。多くの場面では、これらの機能が必要となります。射撃、アイドル、走行の例において、アイドルと走行の再生は、射撃アニメーションに影響しません。また、その逆も同様です (この動作は、animation.CrossFade の必須でないパラメーターで変更できます)。
アニメーションミキシングを使うと、ボディに部分的にだけ適用するアニメーションをいくつか用意することで、作成しなければいけないアニメーションの数を削減することができます。つまり、この方法ではアニメーションをさまざまに組み合わせて使う事ができる、という事です。
任意の AnimationState 上で AddMixingTransform() を呼び出すことにより、アニメーションへアニメーションミキシングのトランスフォームを加えます。
ミキシングの一例として、手を振るアニメーションのようなものが考えられます。キャラクターがアイドリング状態、または歩行状態のときに、手を振らせたいとします。アニメーションミキシング無しでは、アイドリング状態と歩行状態それぞれ用に、手を振るアニメーションを個別に作成しなくてはなりません。ただし、肩のトランスフォームをミキシングトランスフォームとして手を振るアニメーションに加えると、手を振るアニメーションは、肩の付け根から手の部分だけを完全に制御することができます。その場合、体の残りの部分は手を振るアニメーションには影響されず、アイドルか歩行アニメーションを再生し続けます。従って、体の残りの部分がアイドルか歩行のアニメーションを行っている間、手を振るアニメーションを 1 つだけ作成すれば良いことになります。
/// Transform 変数を使ってミキシングトランスフォームを加えます
var shoulder : Transform;
animation["wave_hand"].AddMixingTransform(shoulder);
もう 1 つの例、パスを使用します。
function Start () {
// 代わりに、パスを使ってミキシングトランスフォームを加えます
var mixTransform : Transform = transform.Find("root/upper_body/left_shoulder");
animation["wave_hand"].AddMixingTransform(mixTransform);
}
加法的アニメーションとアニメーションミキシングを使うと、ゲーム作成に必要なアニメーション数を削減でき、表情のアニメーションの作成にとっても重要です。
歩行中や走行中に、ターンのため一方に体を傾けるような、キャラクターを作成したいとします。これには、4 つの組み合わせ (歩く-傾く-左、歩く-傾く-右、走る-傾く-左、走る-傾く-右 )、それぞれのアニメーションが必要になります。それぞれの組み合わせ用に、個別のアニメーションを作成するのは、この簡単な例でさえ明らかに多くの余計な仕事が増加しますが、アクションが増えるごとに組み合わせの数はさらに増加します。幸いなことに、加法的アニメーションとミキシングを利用すると、単純な動きの組み合わせによる個別アニメーションの製作を避けることができます。
加法的アニメーションを使うと、 単体のアニメーションを他の再生アニメーションに重ねて、その効果を重ねることができます。加法的アニメーションを生成する場合に、Unity は、アニメーションクリップの最初のフレームと現在のフレームの差を計算します。その後、この差分を再生されているアニメーション全てに適用します。
先ほどの例で言うと、左右に傾斜するアニメーションを作成したら、Unity 上で歩行やアイドル、走行サイクルに重ね合わせる事ができます。これは、以下のようなコードで実現する事ができます。
private var leanLeft : AnimationState;
private var leanRight : AnimationState;
function Start () {
leanLeft = animation["leanLeft"];
leanRight = animation["leanRight"];
// 傾斜するアニメーションを異なるレイヤーに加えます
// ですから、呼び出した CrossFade は互いに影響しません
leanLeft.layer = 10;
leanRight.layer = 10;
// 傾斜するアニメーションを加算的になるよう設定します
leanLeft.blendMode = AnimationBlendMode.Additive;
leanRight.blendMode = AnimationBlendMode.Additive;
// 傾斜するアニメーションを ClampForever に設定します
// ClampForever を使うと、クリップの終わりに達しても
// アニメーションは自動的に停止することはありません
leanLeft.wrapMode = WrapMode.ClampForever;
leanRight.wrapMode = WrapMode.ClampForever;
// アニメーションを有効にして完全にフェードインさせます
// ここでは、animation.Play を使いません。 Update 関数内で
// 手動で時間を調整するからです。
// 代わりに、アニメーションを有効にして重力を最大にするだけです
leanRight.enabled = true;
leanLeft.enabled = true;
leanRight.weight = 1.0;
leanLeft.weight = 1.0;
// テストでは、"walk" アニメーションだけを再生し、ループさせてください。
}
// すべてのフレームは、適用する傾きに基づいて
// 正規化された時間を設定します
function Update () {
var lean = Input.GetAxis("Horizontal");
// normalizedTime はクリップの最初のフレームで 0、最後のフレームでは 1
leanLeft.normalizedTime = -lean;
leanRight.normalizedTime = lean;
}
ヒント 加法的アニメーションを使う場合、すでに加法的アニメーションが使われている各トランスフォーム上で、加法的アニメーションでないものを再生するのは良くありません。それらをいっしょに再生すると、加法的ではないアニメーションは最終フレームの結果の上に追加されてしまい、全く望まない結果となってしまいます。
キャラクターのボーンをプロシージャルに動かしたい場合があります。例えば、キャラクターの頭部を 3D 空間の特定のポイントに向かせたい場合、ターゲットポイントを追跡するスクリプトで制御するのが最善策となります。幸いな事に Unity では、ボーンはスキンメッシュを動かすトランスフォームにすぎないので、これをとても簡単に実現できます。この場合、ゲームオブジェクトのトランスフォームと同じように、キャラクターのボーンをスクリプトから制御できます。
知っておくべき重要な事は、アニメーションシステムは、Update() 関数の後、かつ、LateUpdate() 関数の前に Transform を更新します。したがって、もし、LookAt() 関数を使用する場合、LateUpdate() 関数内で使用して、本当にアニメーションを上書きしていることを確認する必要があります。
ラグドールも同じ方法で作成されます。単純にリジッドボディやキャラクタージョイント、カプセルコライダーを様々なボーンにアタッチするだけです。これにより、スキンされたキャラクターが物理演算によって動くようになります。
このセクションでは、 Unity 内のアニメーションがエンジンによって再生される時に、どのようにサンプリングされるかを説明します。
アニメーションクリップは通常、固定フレームレートでオーサリングされます。例えば、Autodesk® 3ds Max® または Autodesk® Maya® でフレームレート 60 fps でアニメーションを作成できます。Unity でアニメーションをインポートするとき、このフレームレートがインポーターによって読み取られるため、インポートされたアニメーションのデータも 60 fps でサンプリングされます。
ただし、一般的にゲーム実行時のフレームレートは変動します。あるコンピューターでは他のコンピューターよりもフレームレートが高いかもしれません。また、その瞬間にカメラの視界に映っているビューの複雑さによって、その時々に変化する場合もあります。これは、基本的にゲーム実行時の正確なフレームレートを推定するのは困難である事を意味します。つまり、アニメーションを 60fps で作成したとしても、異なるフレームレート、例えば 56.72fps や 83.14fps、もしくは全く違う値で再生されるかもしれないのです。
結果として Unity は、可変フレームレートでアニメーションをサンプルする必要があり、そのためにもともと設計されたフレームレートを保証していません。幸いなことに 3D コンピューターグラフィックスアニメーションは、個々のフレームから成り立っている訳ではなく、連続したカーブになっています。これらのカーブは、元のアニメーションのフレームに一致するポイントだけでなく、任意の時点でサンプルすることができます。実際、アニメーション製作時より高いフレームレートでゲームを実行すると、アニメーションソフトウェア上で見るよりも、ゲーム内のほうが実際にはスムーズでより流れるように滑らかなアニメーションに見えます。
多くの実用的な目的により、Unity がアニメーションを様々なフレームレートでサンプルするという事実を考慮する必要はありません。ただし、トランスフォームやプロパティが非常に特殊な形状に動くアニメーションに依存するゲーム実行ロジックの場合、シーンの裏側で、リサンプリングが行われている事を知っておく必要があります。例えば、30 フレーム上の間に 0 から 180 度まで回転するオブジェクトのアニメーションがあり、半分に達したときにそれをコードから検知したいとします。その場合、現在の角度が 90 度かどうかを確認するコードに条件文を利用すべきではありません。なぜなら、Unity は、ゲームの可変フレームレートによってアニメーションをサンプリングするので、回転が 90 度に達する直前にサンプリングし、次は、90 度に達した直後にサンプリングする場合があります。アニメーションの特定のポイントに達したかどうかを確認するには、代わりに アニメーションイベント を使います。
可変フレームレートのサンプリングの結果として、WrapMode.Once を使って再生するアニメーションでは、最終フレームの正確な時間にサンプリングされない場合があります。ゲームのあるフレームではアニメーションの終了直前にサンプリングされ、次のフレームでは時間がアニメーションの長さを超えることがあるため無効にされて、それ以上サンプリングされません。アニメーションの最終フレームを確実にサンプリングする必要がある場合は、アニメーションを明示的に停止するまで最終フレームを無限にサンプリングし続ける WrapMode.ClampForever を使います。