首页 > Unity3D频道 > 【Unity2D研究院之游戏开发】 > Unity2D研究院之自动生成动画、AnimationController、Prefab(一)
2014
10-03

Unity2D研究院之自动生成动画、AnimationController、Prefab(一)

国庆了,回家了。时刻还是要吃一颗学习的心,在家了也要抽出时间好好学习一下。之前MOMO一直没研究过Unity2D,今天研究了一下,还是把自己今天的研究笔记记录下来。现在网络上已经有很多Unity2D的技术分享了,我这篇主要说说自动生成先关的东西。

Unity2D的制作流程

1、拿到美术给的帧动画

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

3、创建AnimationController 手动连线

4、创建Prefab文件。

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

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

Unity2D研究院之自动生成动画、AnimationController、Prefab(一) - 雨松MOMO程序研究院 - 1

 

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

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

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

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

动画播放的很正常的。

Unity2D研究院之自动生成动画、AnimationController、Prefab(一) - 雨松MOMO程序研究院 - 2

代码下载地址:http://pan.baidu.com/s/1eQEe3nW

欢迎大家一起讨论unity2d游戏开发,如果您有更好的方法或者建议欢迎在下面给我留言,谢谢。

雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!

--

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。

Unity2D研究院之自动生成动画、AnimationController、Prefab(一)》有 52 条评论

  1. sdk1D2A 说:

    请问怎么用AssetDatabase.LoadAssetAtPath读取已经自动分割的素材中单独的一张呢?我得到的素材不是一张一张的而是一张大图,用unity自带的分割工具切好之后要怎么读入?

  2. dsk1D2A 说:

    雨松大哥。。我不知道2017年了还能不能得到回复。。。我自动生成的clip跟手动拖动素材后生成的clip对照了半天好像没区别。但是代码自动生成的clip既不会动,从别的clip切换过来的时候也不会有反应。比如我向左走的clip是自动生成的,向右走的事手动拖拽的。我按右键会正常播放向右走的动画,按左键的时候物体机会切回默认的sprite图,不会播放向左走的动画, 可能是什么原因导致的呢?

  3. freebee 说:

    using UnityEngine;
    using System.Collections;
    using System.IO;
    using System.Collections.Generic;
    using UnityEditor;
    using UnityEditor.Animations;

    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 clips = new List ();
    foreach (DirectoryInfo dictoryAnimations in dictorys.GetDirectories()) {
    //每个文件夹就是一组帧动画,这里把每个文件夹下的所有图片生成出一个动画文件
    clips.Add (BuildAnimationClip (dictoryAnimations));
    }
    //把所有的动画文件生成在一个AnimationController里
    UnityEditor.Animations.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 ();
    #if UNITY_5
    #else
    AnimationUtility.SetAnimationType (clip, ModelImporterAnimationType.Generic);
    #endif
    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 = AssetDatabase.LoadAssetAtPath (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 clips, string name)
    {
    AnimatorController animatorController = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath (AnimationControllerPath + “/” + name + “.controller”);
    AnimatorControllerLayer layer = animatorController.layers [0];
    AnimatorStateMachine sm = layer.stateMachine;
    foreach (AnimationClip newClip in clips) {
    //AnimatorStateMachine machine = sm.AddStateMachine(newClip.name);
    AnimatorState state = sm.AddState (newClip.name);
    state.motion = newClip;
    //AnimatorStateTransition trans = sm.AddAnyStateTransition(state);
    if (newClip.name == “idle”) {
    sm.defaultState = state;
    }
    //sm.AddEntryTransition(machine);
    //sm.AddStateMachineExitTransition(machine);
    //trans.RemoveCondition(0);
    }
    AssetDatabase.SaveAssets ();
    return animatorController;
    }

    static void BuildPrefab (DirectoryInfo dictorys, UnityEditor.Animations.AnimatorController animatorCountorller)
    {
    //生成Prefab 添加一张预览用的Sprite
    FileInfo images = dictorys.GetDirectories () [0].GetFiles (“*.jpg”) [0];
    GameObject go = new GameObject ();
    go.name = dictorys.Name;
    SpriteRenderer spriteRender = go.AddComponent ();
    spriteRender.sprite = AssetDatabase.LoadAssetAtPath (DataPathToAssetPath (images.FullName));
    Animator animator = go.AddComponent ();
    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; } }
    }

    }

  4. 謝小遜 说:

    帧数少的倒是正常 但是为什么我生成了一个50帧的 排列出现了问题 变成0 1 10 11 12 … 这样排下去了 _(:з」∠)_

    • 謝小遜 说:

      不知道怎么弄 只好在99行生成FileInfo[] images之后重新排列 加一行Sorter.Sort(ref images); 只是把文字里面的全部数字提取出来然后按照大小重新排列的 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄ 上Sorter类:
      public class Sorter
      {
      public static void Sort(ref FileInfo[] arr)
      {
      int[] ascending = new int[arr.Length];
      //字符转换
      for(int i = 0; i < arr.Length; i++) {
      char[] charCache = arr[ i].Name.ToCharArray();
      int[] intCache = new int[charCache.Length – 4];
      string strCache = "";
      for(int j = 0; j < charCache.Length – 4; j++) {
      int.TryParse(charCache[ j].ToString(), out intCache[ j]);
      }
      foreach(int item in intCache) {
      strCache += "" + item;
      }

      int.TryParse(strCache, out ascending[ i]);
      }

      //排序
      for(int i = 0; i 0) && (ascending[j – 1] > t)) {
      ascending[ j] = ascending[j – 1];
      arr[ j] = arr[j – 1];
      j–;
      }
      ascending[ j] = t;
      arr[ j] = T;
      }
      }
      }

  5. 你好 我用的是Unity5 前面几处过时的地方修改了 可是还有一个trans.RemoveCondition(0); 提示无法从int 转为 AnimationCondition类型 那么这里的参数应该填什么呢? 谢谢喽

  6. 雨松哥 ,我用5.0的unity编译这个项目,报11个错,要么是xx已过时,要么是不包含xx的定义,这可怎么办,现在急用啊

  7. 伦子 说:

    我修改了一下,可以在5.x下面正常运行了,把改的地方贴上来给大家static AnimatorController BuildAnimationController(List clips ,string name){AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath(AnimationControllerPath “/” name “.controller”);AnimatorControllerLayer layer = animatorController.layers[0];AnimatorStateMachine sm = layer.stateMachine;foreach(AnimationClip newClip in clips){//AnimatorStateMachine machine = sm.AddStateMachine(newClip.name);AnimatorState state = sm.AddState(newClip.name);state.motion = newClip;//AnimatorStateTransition trans = sm.AddAnyStateTransition(state);if(newClip.name == “idle”){sm.defaultState = state;}//sm.AddEntryTransition(machine);//sm.AddStateMachineExitTransition(machine);//trans.RemoveCondition(0);}AssetDatabase.SaveAssets();return animatorController;}

  8. 伦子 说:

    我修改了一下,可以在5.x下面正常运行了,把改的地方贴上来给大家static AnimatorController BuildAnimationController(List clips ,string name) { AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath(AnimationControllerPath +”/”+name+”.controller”); AnimatorControllerLayer layer = animatorController.layers[0]; AnimatorStateMachine sm = layer.stateMachine; foreach(AnimationClip newClip in clips) { //AnimatorStateMachine machine = sm.AddStateMachine(newClip.name); AnimatorState state = sm.AddState(newClip.name); state.motion = newClip; //AnimatorStateTransition trans = sm.AddAnyStateTransition(state); if(newClip.name == “idle”){ sm.defaultState = state; } //sm.AddEntryTransition(machine); //sm.AddStateMachineExitTransition(machine); //trans.RemoveCondition(0); } AssetDatabase.SaveAssets(); return animatorController; }

  9. NCjia 说:

    雨松大哥,我用这个工具生成的动画,有些动画和原图不一样,这个可能是什么原因产生的呢?

  10. 守望 说:

    momo神你好:PrefabUtility.CreatePrefab 我保存prefab的时候报错 :’AssetsAppResourcesMountmount_01.prefab’ is not a valid asset file name.不知道咋回事。大家有碰到么

  11. paranoia 说:

    为什么animator.play()只能播放一次动画?

  12. longroads 说:

    MOMO大神,我也用了你这个工具自动生成动画了,很好用,谢谢先~~ 不过还有个问题:即使文件没有修改,貌似不同的机器生成的controller和prefab也是不同的。我看了一下,主要是里面有文件的guid不同。比如在controller文件中,主要就是下面的guid在变化。 m_Motions: – {fileID: 7400000, guid: d99bf5e29c01d4e4baf15aa3467ca00d, type: 2}不知道大神有没有遇到过?或者知道怎么解决?

  13. 煎饼 说:

    MOMO老师你好,我想问一下,你代码中的“AnimationUtility.SetAnimationType”,这个函数是在哪里看到的?我查官网的API时,发现完全没有关于这个函数的文档啊~

  14. 源代码 说:

    用鼠标多选几个Sprites,拖到game视图,自动提示你生成帧动画。想怎么用就怎么用。简单粗暴,省事省心省力。。。。。。

    • 雨松MOMO 说:

      这样有点不太好, 因为假如有1000组动画。。 都得人手动拖,, 有可能会出错。。 最好批量自动化。。。。 而且如果想设置动画的一些参数 比如循环 什么的。 都得手动勾这~~~~

      • 我酷游戏 说:

        这样1000组动画就要生成1000个animator了,不大妥当吧?如果每组动画有8个动作,那一个animator对应8个animtion,一共8000个animation根本吃不消。。再如一些图片资源是assetBundle动态加载的,就没法在编辑器里提前生成动画了。我是用代码解决图片循环的,这样比较可控。不知道动画能否带参数?比如动画循环的是enemy1_01 ~ enemy1_04 这是固定的,搞个变量的话 {name}_01 ~ {name}_04 这样这个animator不论什么图片都能放了,不知道这个支持不?

  15. 雨松MOMO 说:

    sprite package 就可以合并图集。。

  16. LOVE张闽豫 说:

    请问: 将具有保存本地文件功能的unity发布成网页之后,运行之后,却不能在本地保存一个xml文件,无法执行file.creat,或者xmlDoc.Save()之类的保存功能,请问这是为什么呢?提示错误:MethodAccessException: Attempt to access a private/protected method failed. at System.Security.SecurityManager.ThrowException (System.Exception ex) [0x00000] in :0 好像是权限问题,要怎么解决呢

  17. 陈靖 说:

    我本来不想说脏话的,你个二货,懂个什么雨松大神,无私奉献,这才是程序员的典范无条件支持雨松大神,我看的Unity第一本书就是大神写的,获益良多 Unity2D研究院之自动生成动画、AnimationController、Prefab(一) - 雨松MOMO程序研究院 - 1

  18. xuhe 说:

    请教一下 如何 修改生成动画的锚点呢 Unity2D研究院之自动生成动画、AnimationController、Prefab(一) - 雨松MOMO程序研究院 - 1

  19. 张洁勇 说:

    为什么不用一张图做动画呢?

    • 雨松MOMO 说:

      Unity那个sprite package。

      • 张洁勇 说:

        嗯,我的意思的用一个有部件的人物图片,包含脚,头,身子之类的,然后使用动画曲线进行编辑,通过改变其相对位置来形成动画,这样的话,2D的动画就需要一张图就行了,Spriete Package的话他其实本质还是很多张图。至于为什么这么做,包的大小很容易就压下去了。

  20. 不错!~O(∩_∩)O 说:

    Unity2D研究院之自动生成动画、AnimationController、Prefab(一) - 雨松MOMO程序研究院 - 1

  21. 张浩 说:

    你的序列帧播放每次都换图集了,是不是把美术给的资源用Texture Packer打到一张图集上,后者的内存占用比前者更小?

留下一个回复

你的email不会被公开。