Unity3D的Mecanim動畫系統是非常強大的,而且作為Unity推薦的動畫系統,其未來會完全代替老的一套動畫系統,即Legacy動畫系統。目前的情況是Mecanim與Legacy兩套動畫系統同時共存,但是並不是說Legacy動畫系統就沒有任何價值了,作為Unity4.0以前使用的動畫系統,我認為還是很有必要去了解和學習的,所以就有了這篇筆記。
Legacy動畫系統
http://docs.unity3d.com/Manual/Animations.html
我們可以使用Unity自帶的資源來學習老版本的動畫系統,新建Unity3D項目,選擇菜單“Assets”->“Import Package”->“Character Controller”,導入的資源里的那個小人就是使用Legacy動畫系統的模型,我們的學習可以基於他來進行。
模型文件
在骨骼這一項中,我們發現動畫類型的設置就是Legacy,說明這個模型使用的動畫類型為老版本的動畫系統。
我們再看看動畫頁:
動畫頁中,我們可以對動畫剪輯進行編輯。
控制動畫
我們直接將FBX文件拖入場景,Unity會自動幫我們添加Transform和Animation兩個組件(注意Mecanim動畫系統使用的是Animator組件,Legacy動畫系統使用的是Animation組件)。
Animation組件的設置還是比較簡單的:
- Animation:當前播放的動畫。
- Animations:所有可以播放的動畫。
- Play Automatically:是否自動播放。
- Animate Physics:動畫是否和物理世界進行交互。
- Culling Type:動畫在不可見時是否還繼續播放,優化選項默認即可。
點擊播放按鈕就可以看見動畫正常播放了。
腳本控制
http://docs.unity3d.com/ScriptReference/Animation.html
下面我們來看看如何使用腳本控制動畫的播放,我們將下面的腳本綁定到人物身上即可。
1 using UnityEngine; 2 using System.Collections; 3 4 public class AnimationScript : MonoBehaviour 5 { 6 private Animation _animation; 7 8 void Start() 9 { 10 _animation = this.animation; 11 } 12 13 void OnGUI() 14 { 15 //直接播放動畫 16 if(GUI.Button(new Rect(0, 0, 100, 30), "idle")) 17 { 18 _animation.Play("idle"); 19 } 20 if(GUI.Button(new Rect(100, 0, 100, 30), "walk")) 21 { 22 _animation.Play("walk"); 23 } 24 if(GUI.Button(new Rect(200, 0, 100, 30), "run")) 25 { 26 _animation.Play("run"); 27 } 28 if(GUI.Button(new Rect(300, 0, 100, 30), "jump_pose")) 29 { 30 _animation.Play("jump_pose"); 31 } 32 //使用融合來播放動畫 33 if(GUI.Button(new Rect(0, 30, 100, 30), "idle")) 34 { 35 _animation.CrossFade("idle"); 36 } 37 if(GUI.Button(new Rect(100, 30, 100, 30), "walk")) 38 { 39 _animation.CrossFade("walk"); 40 } 41 if(GUI.Button(new Rect(200, 30, 100, 30), "run")) 42 { 43 _animation.CrossFade("run"); 44 } 45 if(GUI.Button(new Rect(300, 30, 100, 30), "jump_pose")) 46 { 47 _animation.CrossFade("jump_pose"); 48 } 49 } 50 }
運行程序,會看見兩排按鈕,其中第一排按鈕使用Play方法來切換動畫,而第二排按鈕則使用CrossFade來播放動畫。
Play與CrossFade的區別
以從跑步切換到站立動畫為例來看:
Play:直接切換動畫,如果人物之前處於傾斜跑步狀態,則會立即變成站立狀態,表現上比較不真實,特別是當兩個動畫姿勢差別較大時。
CrossFade:通過動畫融合來切換動畫,第二個參數可以指定融合的時間,如果人物之前處於傾斜跑步狀態,則會在指定的融合時間內逐漸變成站立狀態,表現上接近真實的人物動作切換效果。
但是當使用CrossFade播放跳躍動畫時會出現問題,主要問題是跳躍動畫不是循環播放且其持續時間小於動畫融合的時間,我們修改為下面的腳本指定融合時間短一點就可以正常進行跳躍的融合播放了:
_animation.CrossFade("jump_pose", 0.1f);
PlayQueued
該方法可以指定當當前的動畫播放完畢后接下來播放的動畫,如下:
_animation.PlayQueued("run", QueueMode.CompleteOthers, PlayMode.StopSameLayer);
文件格式和資源加載
我們的模型使用通用的FBX格式,那么動畫文件的儲存一般有兩種情況,一是所有的動畫和模型都一起存放到一個文件中,還有一種情況是模型單獨一個文件而動畫單獨一個文件。
模型動畫都存放在一個文件中的情況
類似於Unity提供的Character Controller中的資源就是這樣的結構,一個FBX文件保存了模型、骨骼和動畫:
Resources加載
我們直接加載該資源就可以直接使用,將下面的腳本綁定到攝像機即可,腳本如下:
1 using UnityEngine; 2 using System.Collections; 3 4 public class AllInOneResourcesLoad : MonoBehaviour 5 { 6 private Animation _animation; 7 8 void Start() 9 { 10 GameObject go = Resources.Load<GameObject>("Standard Assets/Character Controllers/Sources/PrototypeCharacter/Constructor"); 11 12 GameObject man = Instantiate(go) as GameObject; 13 _animation = man.animation; 14 } 15 16 void OnGUI() 17 { 18 //直接播放動畫 19 if(GUI.Button(new Rect(0, 0, 100, 30), "idle")) 20 { 21 _animation.Play("idle"); 22 } 23 if(GUI.Button(new Rect(100, 0, 100, 30), "walk")) 24 { 25 _animation.Play("walk"); 26 } 27 if(GUI.Button(new Rect(200, 0, 100, 30), "run")) 28 { 29 _animation.Play("run"); 30 } 31 if(GUI.Button(new Rect(300, 0, 100, 30), "jump_pose")) 32 { 33 _animation.Play("jump_pose"); 34 } 35 //使用融合來播放動畫 36 if(GUI.Button(new Rect(0, 30, 100, 30), "idle")) 37 { 38 _animation.CrossFade("idle"); 39 } 40 if(GUI.Button(new Rect(100, 30, 100, 30), "walk")) 41 { 42 _animation.CrossFade("walk"); 43 } 44 if(GUI.Button(new Rect(200, 30, 100, 30), "run")) 45 { 46 _animation.CrossFade("run"); 47 } 48 if(GUI.Button(new Rect(300, 30, 100, 30), "jump_pose")) 49 { 50 _animation.CrossFade("jump_pose", 0.1f); 51 } 52 } 53 }
AssetBundle加載
打包
使用下面的腳本打包:
1 using UnityEditor; 2 using UnityEngine; 3 4 public class CreateAllInOneAB 5 { 6 [MenuItem("Tool/CreateAllInOneAB")] 7 private static void Create() 8 { 9 BuildPipeline.BuildAssetBundle(null, new[] 10 { 11 AssetDatabase.LoadAssetAtPath("Assets/Resources/Standard Assets/Character Controllers/Sources/PrototypeCharacter/Constructor.FBX", typeof(GameObject)) 12 }, 13 Application.streamingAssetsPath + "/AllInOne.assetbundle", 14 BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.UncompressedAssetBundle, 15 BuildTarget.StandaloneWindows64); 16 } 17 }
加載
將下面的腳本綁定到攝像機即可:
1 using UnityEngine; 2 using System.Collections; 3 4 public class AllInOneAssetBundleLoad : MonoBehaviour 5 { 6 private Animation _animation; 7 8 9 void Start() 10 { 11 AssetBundle assetBundle = AssetBundle.CreateFromFile(Application.streamingAssetsPath + "/AllInOne.assetbundle"); 12 13 GameObject go = assetBundle.Load("Constructor", typeof(GameObject)) as GameObject; 14 15 GameObject man = Instantiate(go) as GameObject; 16 _animation = man.animation; 17 } 18 19 void OnGUI() 20 { 21 //直接播放動畫 22 if(GUI.Button(new Rect(0, 0, 100, 30), "idle")) 23 { 24 _animation.Play("idle"); 25 } 26 if(GUI.Button(new Rect(100, 0, 100, 30), "walk")) 27 { 28 _animation.Play("walk"); 29 } 30 if(GUI.Button(new Rect(200, 0, 100, 30), "run")) 31 { 32 _animation.Play("run"); 33 } 34 if(GUI.Button(new Rect(300, 0, 100, 30), "jump_pose")) 35 { 36 _animation.Play("jump_pose"); 37 } 38 //使用融合來播放動畫 39 if(GUI.Button(new Rect(0, 30, 100, 30), "idle")) 40 { 41 _animation.CrossFade("idle"); 42 } 43 if(GUI.Button(new Rect(100, 30, 100, 30), "walk")) 44 { 45 _animation.CrossFade("walk"); 46 } 47 if(GUI.Button(new Rect(200, 30, 100, 30), "run")) 48 { 49 _animation.CrossFade("run"); 50 } 51 if(GUI.Button(new Rect(300, 30, 100, 30), "jump_pose")) 52 { 53 _animation.CrossFade("jump_pose", 0.1f); 54 } 55 } 56 }
模型動畫分開存放的情況
還有一種情況是模型和動畫是分為多個FBX文件存放的,比如下面是模型文件:
雖然有一個Take 001的動畫,但是實際上我們並不使用該動畫,而是使用下面僅保存了動畫的FBX文件:
Resources加載
首先我們要清楚的是,無論是保存了模型還是保存了動畫的FBX文件在Unity看來都是同樣的一種類型,即添加了Animation組件的GameObject,下面我們看看如何在Resources中加載並顯示這個角色,代碼如下,掛載到主攝像機即可:
1 using UnityEngine; 2 using System.Collections; 3 4 public class ResourcesLoad : MonoBehaviour 5 { 6 private Animation _animation; 7 8 void Start() 9 { 10 GameObject go = Resources.Load<GameObject>("ZombieNurse/Zombienurse_Rig"); 11 12 GameObject man = Instantiate(go) as GameObject; 13 _animation = man.animation; 14 15 //添加動畫剪輯 16 _animation.AddClip(LoadAnimationClip("ZombieNurse/Animation/Zombienurse@attack"), "attack"); 17 _animation.AddClip(LoadAnimationClip("ZombieNurse/Animation/Zombienurse@death"), "death"); 18 _animation.AddClip(LoadAnimationClip("ZombieNurse/Animation/Zombienurse@idle"), "idle"); 19 _animation.AddClip(LoadAnimationClip("ZombieNurse/Animation/Zombienurse@run"), "run"); 20 21 _animation.Play("idle"); 22 } 23 24 private AnimationClip LoadAnimationClip(string path) 25 { 26 GameObject go = Resources.Load<GameObject>(path); 27 return go.animation.clip; 28 } 29 30 void OnGUI() 31 { 32 if(GUI.Button(new Rect(0, 0, 100, 30), "idle")) 33 { 34 _animation.CrossFade("idle"); 35 } 36 if(GUI.Button(new Rect(100, 0, 100, 30), "run")) 37 { 38 _animation.CrossFade("run"); 39 } 40 if(GUI.Button(new Rect(200, 0, 100, 30), "attack")) 41 { 42 _animation.CrossFade("attack"); 43 } 44 if(GUI.Button(new Rect(300, 0, 100, 30), "death")) 45 { 46 _animation.CrossFade("death"); 47 } 48 } 49 }
AssetBundle加載
打包
使用下面的腳本打包:
1 using UnityEngine; 2 using UnityEditor; 3 4 public class CreateAB : MonoBehaviour 5 { 6 [MenuItem("Tool/CreateAB")] 7 private static void Create() 8 { 9 BuildPipeline.BuildAssetBundle(null, new[] 10 { 11 AssetDatabase.LoadAssetAtPath("Assets/Resources/ZombieNurse/Zombienurse_Rig.FBX", typeof(GameObject)), 12 AssetDatabase.LoadAssetAtPath("Assets/Resources/ZombieNurse/Animation/Zombienurse@attack.FBX", typeof(GameObject)), 13 AssetDatabase.LoadAssetAtPath("Assets/Resources/ZombieNurse/Animation/Zombienurse@death.FBX", typeof(GameObject)), 14 AssetDatabase.LoadAssetAtPath("Assets/Resources/ZombieNurse/Animation/Zombienurse@idle.FBX", typeof(GameObject)), 15 AssetDatabase.LoadAssetAtPath("Assets/Resources/ZombieNurse/Animation/Zombienurse@run.FBX", typeof(GameObject)) 16 }, 17 Application.streamingAssetsPath + "/AB.assetbundle", 18 BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.UncompressedAssetBundle, 19 BuildTarget.StandaloneWindows64); 20 } 21 }
加載
將下面的腳本綁定到攝像機即可:
1 using UnityEngine; 2 using System.Collections; 3 4 public class AssetBundleLoad : MonoBehaviour 5 { 6 private Animation _animation; 7 8 void Start() 9 { 10 AssetBundle assetBundle = AssetBundle.CreateFromFile(Application.streamingAssetsPath + "/AB.assetbundle"); 11 12 GameObject go = assetBundle.Load("Zombienurse_Rig", typeof(GameObject)) as GameObject; 13 14 GameObject man = Instantiate(go) as GameObject; 15 _animation = man.animation; 16 17 //添加動畫剪輯 18 _animation.AddClip(LoadAnimationClip(assetBundle, "Zombienurse@attack"), "attack"); 19 _animation.AddClip(LoadAnimationClip(assetBundle, "Zombienurse@death"), "death"); 20 _animation.AddClip(LoadAnimationClip(assetBundle, "Zombienurse@idle"), "idle"); 21 _animation.AddClip(LoadAnimationClip(assetBundle, "Zombienurse@run"), "run"); 22 23 _animation.Play("idle"); 24 } 25 26 private AnimationClip LoadAnimationClip(AssetBundle assetBundle, string path) 27 { 28 GameObject go = assetBundle.Load(path, typeof(GameObject)) as GameObject; 29 return go.animation.clip; 30 } 31 32 void OnGUI() 33 { 34 if(GUI.Button(new Rect(0, 0, 100, 30), "idle")) 35 { 36 _animation.CrossFade("idle"); 37 } 38 if(GUI.Button(new Rect(100, 0, 100, 30), "run")) 39 { 40 _animation.CrossFade("run"); 41 } 42 if(GUI.Button(new Rect(200, 0, 100, 30), "attack")) 43 { 44 _animation.CrossFade("attack"); 45 } 46 if(GUI.Button(new Rect(300, 0, 100, 30), "death")) 47 { 48 _animation.CrossFade("death"); 49 } 50 } 51 }