Unity2D的制作流程

1、拿到美术给的帧动画

2、打开Animation windows 手动创建动画文件

3、创建AnimationController 手动连线

4、创建Prefab文件。

这也太麻烦了。全都手动来美术每次给你好几十个动画资源那岂不是要累死程序员了。所以我们不能手动,必须自动。

如下图所示,先看看我生成出来的结果。

Unity2D研究院之自动生成动画、AnimationController、Prefab(一)_下载地址


我们的目标是Raw文件夹下放所有美术提供的帧动画,每个文件夹就是一组帧动画,文件夹名子就是动画的名子,代码如下所示。


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

using UnityEngine; using System.Collections; using System.IO; using System.Collections.Generic; using UnityEditor; using UnityEditorInternal; public class BuildAnimation : Editor { //生成出的Prefab的路径 private static string PrefabPath = "Assets/Resources/Prefabs"; //生成出的AnimationController的路径 private static string AnimationControllerPath = "Assets/AnimationController"; //生成出的Animation的路径 private static string AnimationPath = "Assets/Animation"; //美术给的原始图片路径 private static string ImagePath = Application.dataPath +"/Raw"; [MenuItem("Build/BuildAnimaiton")] static void BuildAniamtion() { DirectoryInfo raw = new DirectoryInfo (ImagePath); foreach (DirectoryInfo dictorys in raw.GetDirectories()) { List<AnimationClip> clips = new List<AnimationClip>(); foreach (DirectoryInfo dictoryAnimations in dictorys.GetDirectories()) { //每个文件夹就是一组帧动画,这里把每个文件夹下的所有图片生成出一个动画文件 clips.Add(BuildAnimationClip(dictoryAnimations)); } //把所有的动画文件生成在一个AnimationController里 AnimatorController controller = BuildAnimationController(clips,dictorys.Name); //最后生成程序用的Prefab文件 BuildPrefab(dictorys,controller); } } static AnimationClip BuildAnimationClip(DirectoryInfo dictorys) { string animationName = dictorys.Name; //查找所有图片,因为我找的测试动画是.jpg FileInfo []images = dictorys.GetFiles("*.jpg"); AnimationClip clip = new AnimationClip(); AnimationUtility.SetAnimationType(clip,ModelImporterAnimationType.Generic); EditorCurveBinding curveBinding = new EditorCurveBinding(); curveBinding.type = typeof(SpriteRenderer); curveBinding.path=""; curveBinding.propertyName = "m_Sprite"; ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[images.Length]; //动画长度是按秒为单位,1/10就表示1秒切10张图片,根据项目的情况可以自己调节 float frameTime = 1/10f; for(int i =0; i< images.Length; i++){ Sprite sprite = Resources.LoadAssetAtPath<Sprite>(DataPathToAssetPath(images[i].FullName)); keyFrames[i] = new ObjectReferenceKeyframe (); keyFrames[i].time = frameTime *i; keyFrames[i].value = sprite; } //动画帧率,30比较合适 clip.frameRate = 30; //有些动画我希望天生它就动画循环 if(animationName.IndexOf("idle") >=0 ) { //设置idle文件为循环动画 SerializedObject serializedClip = new SerializedObject(clip); AnimationClipSettings clipSettings = new AnimationClipSettings(serializedClip.FindProperty("m_AnimationClipSettings")); clipSettings.loopTime = true; serializedClip.ApplyModifiedProperties(); } string parentName = System.IO.Directory.GetParent(dictorys.FullName).Name; System.IO.Directory.CreateDirectory(AnimationPath +"/"+parentName); AnimationUtility.SetObjectReferenceCurve(clip,curveBinding,keyFrames); AssetDatabase.CreateAsset(clip,AnimationPath +"/"+parentName +"/" +animationName+".anim"); AssetDatabase.SaveAssets(); return clip; } static AnimatorController BuildAnimationController(List<AnimationClip> clips ,string name) { AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath(AnimationControllerPath +"/"+name+".controller"); AnimatorControllerLayer layer = animatorController.GetLayer(0); UnityEditorInternal.StateMachine sm = layer.stateMachine; foreach(AnimationClip newClip in clips) { State state = sm.AddState(newClip.name); state.SetAnimationClip(newClip,layer); Transition trans = sm.AddAnyStateTransition(state); trans.RemoveCondition(0); } AssetDatabase.SaveAssets(); return animatorController; } static void BuildPrefab(DirectoryInfo dictorys,AnimatorController animatorCountorller) { //生成Prefab 添加一张预览用的Sprite FileInfo images = dictorys.GetDirectories()[0].GetFiles("*.jpg")[0]; GameObject go = new GameObject(); go.name = dictorys.Name; SpriteRenderer spriteRender =go.AddComponent<SpriteRenderer>(); spriteRender.sprite = Resources.LoadAssetAtPath<Sprite>(DataPathToAssetPath(images.FullName)); Animator animator = go.AddComponent<Animator>(); animator.runtimeAnimatorController = animatorCountorller; PrefabUtility.CreatePrefab(PrefabPath+"/"+go.name+".prefab",go); DestroyImmediate(go); } public static string DataPathToAssetPath(string path) { if (Application.platform == RuntimePlatform.WindowsEditor) return path.Substring(path.IndexOf("Assets\\")); else return path.Substring(path.IndexOf("Assets/")); } class AnimationClipSettings { SerializedProperty m_Property; private SerializedProperty Get (string property) { return m_Property.FindPropertyRelative(property); } public AnimationClipSettings(SerializedProperty prop) { m_Property = prop; } public float startTime { get { return Get("m_StartTime").floatValue; } set { Get("m_StartTime").floatValue = value; } } public float stopTime { get { return Get("m_StopTime").floatValue; } set { Get("m_StopTime").floatValue = value; } } public float orientationOffsetY { get { return Get("m_OrientationOffsetY").floatValue; } set { Get("m_OrientationOffsetY").floatValue = value; } } public float level { get { return Get("m_Level").floatValue; } set { Get("m_Level").floatValue = value; } } public float cycleOffset { get { return Get("m_CycleOffset").floatValue; } set { Get("m_CycleOffset").floatValue = value; } } public bool loopTime { get { return Get("m_LoopTime").boolValue; } set { Get("m_LoopTime").boolValue = value; } } public bool loopBlend { get { return Get("m_LoopBlend").boolValue; } set { Get("m_LoopBlend").boolValue = value; } } public bool loopBlendOrientation { get { return Get("m_LoopBlendOrientation").boolValue; } set { Get("m_LoopBlendOrientation").boolValue = value; } } public bool loopBlendPositionY { get { return Get("m_LoopBlendPositionY").boolValue; } set { Get("m_LoopBlendPositionY").boolValue = value; } } public bool loopBlendPositionXZ { get { return Get("m_LoopBlendPositionXZ").boolValue; } set { Get("m_LoopBlendPositionXZ").boolValue = value; } } public bool keepOriginalOrientation { get { return Get("m_KeepOriginalOrientation").boolValue; } set { Get("m_KeepOriginalOrientation").boolValue = value; } } public bool keepOriginalPositionY { get { return Get("m_KeepOriginalPositionY").boolValue; } set { Get("m_KeepOriginalPositionY").boolValue = value; } } public bool keepOriginalPositionXZ { get { return Get("m_KeepOriginalPositionXZ").boolValue; } set { Get("m_KeepOriginalPositionXZ").boolValue = value; } } public bool heightFromFeet { get { return Get("m_HeightFromFeet").boolValue; } set { Get("m_HeightFromFeet").boolValue = value; } } public bool mirror { get { return Get("m_Mirror").boolValue; } set { Get("m_Mirror").boolValue = value; } } } }


因为新版的动画系统Unity没有提供直接的API来设置动画的循环状态,所以我们只能通过写文件的形式来修改动画的天生属性。需要用到自己写封装的类 AnimationClipSettings 具体方法请看上面的代码。

有了自动生成动画的代码,就不怕美术一次给你多少组图片,或者更新了多少组图片都能很快的生成出来。

随便写一条测试脚本,来测试一下播放动画。


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

using UnityEngine; using System.Collections; public class NewBehaviourScript : MonoBehaviour { Animator animator ; void Start () { animator = GetComponent<Animator>(); } void OnGUI() { if(GUILayout.Button("idle")) { animator.Play("idle"); } } }


动画播放的很正常的。