同じ内容を勉強会で解説した動画
概要
Unityでアニメーションを制御する際に避けては通れないMecanimの機能を使ってみる。
Mecanimとは
Unity4から追加されたアニメーションシステム。実際にはAnimator
を使ってAvatar
やAnimation Clip
、状態遷移を扱うワークフロー全体のことでありMecanimという機能やコンポーネントがあるわけではない。以前のバージョンから存在するアニメーション機能も残されてはいるが、Mecanimを使う事で下記のメリットがある。
- 人間型のキャラクタ間でモーションデータを共有可能
- 連続するアニメーションの切り替えをGUI上で設定可能
- IK(インバース・キネマティクス)などの高度なアニメーション
- パーツごとに異なるアニメーションを適用する(ボディマスク)
生産性とクオリティを同時に上げる事ができ、デザイナとプログラマの協業も推進できるすごい機能。旧アニメーションシステムを使うメリットはほぼ無い。
Animatorコンポーネント
Mecanimを使ったアニメーション制御を行うコンポーネント。MecanimあるところにAnimatorあり。スクリプトからAnimatorにアクセスする事で状態の切り替えや現在のアニメーションの再生状況などにアクセスできる。
Animationコンポーネント
旧来のアニメーションを司るコンポーネント。複数のアニメーションクリップをリストに登録しておき、再生させる形。古くからあるアセットなどに最初から付いていたりもする。削除してしまって問題ない。名前が似ているので間違って追加しないように注意。
アニメーションを構成する要素
アニメーションを行うには複数のファイルやコンポーネントが連携して動作する。具体的には下記のような要素がある。
名前 | 説明 |
---|---|
メッシュ(モデル) | .fbx形式などの3Dデータ。ストアなどで入手するか、3Dソフトで作成する。 |
アバター | メッシュに対してボーンを埋め、人間型かどうかを設定したインターフェース |
アニメーションクリップ | アニメーションのモーションデータ。手動で作ったり、モーションキャプチャで作ったりする。 |
アニメーターコントローラー | オブジェクトの状態とアニメーションの対応関係、遷移条件を管理する。 |
素材の調達
Robot Kyle
アセットストアからダウンロードする。
Unity Chan
アセットストア版は声は少ないがモーションデータは入っている。Web版でももちろんOK。今回はモーションデータを使う。
モデルの設定
アセットストアからRobot Kyleをインポートした直後。インスペクタを見るとリグの設定を開くとアニメーションタイプがLegacy
、すなわちMecanimを使わない状態になっている。ドロップダウンからGenericないしはHumanoidを選択するとMecanimに向けた設定を行ったAvatarが生成される。今回はユニティちゃんのモーションなどをリターゲティングして使いたいのでHumanoidを選択してApplyする。これでAvatarが生成される。
アバターが生成されている。Configureをクリックするとさらに詳細な設定画面を確認できる。
この画面ではHumanoidのアバターとして必須のボーンやオプションのボーンがどのメッシュに対応しているかが設定されている。元のメッシュがTポーズを取っていればある程度自動で判定される。この画面で手動でボーンを設定して頑張るよりは元のメッシュデータをTポーズにしたりするといった手間を加えてやるほうが良さそう。
AnimatorとAnimator Controllerの設定
Robot Kyleをシーンに配置する。Avatarを含んでいるので自動でAnimatorコンポーネントが設定されている。Avatarが無いモデルだとAnimationコンポーネントが設定されてしまっている場合がある。その場合は落ち着いてアバターを設定し、Animatorコンポーネントを設定する。アニメーターコンポーネントの設定項目は次のとおり。
名前 | 意味 |
---|---|
Controller | アタッチされたコントローラー |
Avatar | このキャラクタのアバター設定 |
Apply Root Motion | キャラクタの位置をアニメーションで制御する |
Update Mode | 物理演算をするかどうか |
Culling Mode | 表示されていないオブジェクトをアニメーションさせるか |
Controllerはプレハブを利用しているのでなければ自作する。プロジェクトビューからCreate > Animator Controller
として作成し、モデル名などを元に命名する。
Animator Controllerを作成したらAnimatorコンポーネントのControllerの部分に適用する。
Stateの設定
作成したAnimator ControllerをダブルクリックするとAnimator編集のウインドウが開く。最初はAny Stateというアイテムしか無いが、ここにStateを追加し、対応するアニメーションクリップを設定することで動作を設定する。
何もないところで右クリックしCreate State > Empty
とすると新しいStateができる。最初の一つ目は自動的にデフォルトステートとなってオレンジ色になる。作成したステートを選択しインスペクタのMotionの部分をみる。
Noneとなっている部分にHumanoid用のアニメーションクリップを設定する事で自由にステートに動きを設定できる。試しにUnityChanの中にあるWIN01
を設定して実行してみよう。カメラの位置を調整し、プレイモードにするとロボットがかわいくはしゃぎ始める。
さらにStateを作成しそちらにはLOSE01
を設定する。これだけでは2つ目のステートは再生されないが、1つめのステートを選択しMake Transition
で2つ目のステートへ線を引くと、再生が終わると2つめのステートに遷移する。さらに2つ目のステートから1つ目のステートに線を引けば交互に再生される。
スクリプトからのコントロール
自動でステートが変わるだけではなく、外部のスクリプトから制御したい場合。例えばスペースキーを押すとジャンプするというのを考える。まず状態を管理する為のフラグをAnimator Controllerに追加する。アニメーター設定の左下にある+をクリックし、BoolタイプでJumpというパラメータを作成。
さらに新しいステートを作成し、Jump00を割り当てる。ジャンプさせたいステートからTransitionを作成し線を引く。ここでジャンプするステートへ向かっている線を選択する。
ここでインスペクタのConditionsの部分、Exit Timeはクリップの再生後に自動で遷移する条件だが、ここをJumpパラメータがTrueの時にというふうに設定する。こうすることでスクリプトからTrueが設定された時にJumpへの遷移が発生する。
Robot KyleにAdd Componetし、New Scriptとして次のスクリプトをアタッチするとスペースキーでジャンプする。
using UnityEngine;
using System.Collections;
public class RoboScript : MonoBehaviour {
void Update () {
bool jump = false;
if (Input.GetKeyDown (KeyCode.Space)) {
jump = true;
}
GetComponent<Animator>().SetBool("Jump",jump);
}
}
JavaScriptだとこんな感じ。
#pragma strict
function Update () {
var jump = false;
if (Input.GetKeyDown (KeyCode.Space)) {
jump = true;
}
var anim:Animator = GetComponent("Animator");
anim.SetBool("Jump",jump);
}
現状だとLose再生中からしかジャンプできない。さらにWinのステートからも線を引くことでいつジャンプできるのかを表現することができる。このような形でかつてはIf文の集合体で表現されていた状態遷移をデザイナーでも扱える形にできるのがMecanimの強みである。
スクリプトからアニメーションの状態を取得する。
現在のステートマシンの状態を取得するにはAnimator
からAnimatorStateInfo
オブジェクトを取得し、メソッドを通じて状態にアクセスする。
比較を行うにはステート名をハッシュに変換してから比較する必要がある。またステート名にはレイヤー名が含まれるので、記述の方法に注意する。デフォルトのレイヤー名はBase
となりドットで区切ってステート名を記述する。
using UnityEngine;
using System.Collections;
public class RoboStateC : MonoBehaviour {
// Update is called once per frame
void Update () {
AnimatorStateInfo state = GetComponent<Animator> ().GetCurrentAnimatorStateInfo (0);
if (state.nameHash == Animator.StringToHash("Base Layer.Lose") ) {
transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
} else {
transform.localScale = new Vector3(1f,1f,1f);
}
}
}
JavaScriptで記述すると以下のような形。
#pragma strict
function Update () {
var anim:Animator = GetComponent("Animator");
var state:AnimatorStateInfo = anim.GetCurrentAnimatorStateInfo(0);
if (state.nameHash == Animator.StringToHash("Base Layer.Lose") ) {
transform.localScale = Vector3(0.5, 0.5, 0.5);
} else {
transform.localScale = Vector3(1,1,1);
}
}
ハッシュにしないでメソッドで確認する事もできる。
using UnityEngine;
using System.Collections;
public class RoboStateC : MonoBehaviour {
// Update is called once per frame
void Update () {
AnimatorStateInfo state = GetComponent<Animator> ().GetCurrentAnimatorStateInfo (0);
if (state.IsName("Base Layer.Lose") ) {
transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
} else {
transform.localScale = new Vector3(1f,1f,1f);
}
}
}
スピード変更
Animator
を通じてアニメーションの再生速度を変更する事もできる。
using UnityEngine;
using System.Collections;
public class RoboSpeedC : MonoBehaviour {
// Update is called once per frame
void Update () {
if (Input.GetKey(KeyCode.RightArrow)) {
GetComponent<Animator>().speed = 3;
} else {
GetComponent<Animator>().speed = 1;
}
}
}
アニメーションクリップの編集
アセットに含まれているアニメーションクリップは設定変更ができない。EditメニューからDupulicateして新規クリップにするとオプションの編集が可能になる。モデルの移動や回転が意図しない形で起こる場合に設定を見直す必要がある。
項目 | 内容 |
---|---|
Root Transform Rotation | Bakeすると回転がクリップの中だけに閉じる |
Root Transform Position (Y) | Bakeすると上下移動がクリップの中だけに閉じた見た目になる。 |
Root Transform Position (XZ) | Bakeすると平面移動がクリップの中だけに閉じた見た目になる。 |
ジャンプや回転などがアニメーションで表現されていた場合に、見た目だけになるか、オブジェクト自体が動くかを左右する項目。
アニメーションのブレンド
静止、歩く、走るといった複数のモーションを混ぜあわせて再生させることができる。
アナログな歩きから走りへの移行、方向転換などのミックスに使われる。