国庆了,回家了。时刻还是要吃一颗学习的心,在家了也要抽出时间好好学习一下。之前MOMO一直没研究过Unity2D,今天研究了一下,还是把自己今天的研究笔记记录下来。现在网络上已经有很多Unity2D的技术分享了,我这篇主要说说自动生成先关的东西。
Unity2D的制作流程
1、拿到美术给的帧动画
2、打开Animation windows 手动创建动画文件
3、创建AnimationController 手动连线
4、创建Prefab文件。
这也太麻烦了。全都手动来美术每次给你好几十个动画资源那岂不是要累死程序员了。所以我们不能手动,必须自动。
如下图所示,先看看我生成出来的结果。
我们的目标是Raw文件夹下放所有美术提供的帧动画,每个文件夹就是一组帧动画,文件夹名子就是动画的名子,代码如下所示。
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 具体方法请看上面的代码。
有了自动生成动画的代码,就不怕美术一次给你多少组图片,或者更新了多少组图片都能很快的生成出来。
随便写一条测试脚本,来测试一下播放动画。
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");
}
}
}
动画播放的很正常的。
代码下载地址:http://pan.baidu.com/s/1eQEe3nW
欢迎大家一起讨论unity2d游戏开发,如果您有更好的方法或者建议欢迎在下面给我留言,谢谢。
- 本文固定链接: https://www.xuanyusong.com/archives/3243
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!



雨松大哥:我在动态生成的时候出现 sprite:Sprite(missing) 样的问题 ,是什么原因啊
请问怎么用AssetDatabase.LoadAssetAtPath读取已经自动分割的素材中单独的一张呢?我得到的素材不是一张一张的而是一张大图,用unity自带的分割工具切好之后要怎么读入?
雨松大哥。。我不知道2017年了还能不能得到回复。。。我自动生成的clip跟手动拖动素材后生成的clip对照了半天好像没区别。但是代码自动生成的clip既不会动,从别的clip切换过来的时候也不会有反应。比如我向左走的clip是自动生成的,向右走的事手动拖拽的。我按右键会正常播放向右走的动画,按左键的时候物体机会切回默认的sprite图,不会播放向左走的动画, 可能是什么原因导致的呢?
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; } }
}
}
下面是更改后可用于5.X的
另外再来一发
http://blog.csdn.net/dingkun520wy/article/details/50971544
帧数少的倒是正常 但是为什么我生成了一个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;
}
}
}
你好 我用的是Unity5 前面几处过时的地方修改了 可是还有一个trans.RemoveCondition(0); 提示无法从int 转为 AnimationCondition类型 那么这里的参数应该填什么呢? 谢谢喽
unity5上这代码用不成了, 要重写了, 你可以参考一下下面朋友重写的代码。。。
才看到 谢谢啦 下面的代码确实帮了我