标签:
昨天研究了下Unity中AnimatorController自动生成。今天稍微完善了操作流程,并且补充说明了在MMO中如何使用模型资源(AssetBundle),这个使用方法是MMO这样模型资源非常大的情况下才需要的。如果是比较小的情况下,直接塞到Resources目录下,加载Prefab就OK了,根本不用操心这些问题。
先附上最终修改后的脚本。它创建了一个菜单选项,选择一个文件夹(或者文件),右键选择CreateAnimation就可以自动遍历所有的模型资源,然后创建AnimatorController并创建Prefab文件。在Prefab都生成完毕后(或者在生成的同时),可以使用创建AssetBundle的功能在磁盘任意目录创建AssetBundle。
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System.IO;
using System.Collections;
using System.Collections.Generic;
public class CreateAnimation : Editor
{
//生成出的Prefab的路径
private static readonly string PREFAB_PATH = "Assets/Prefab/";
private static readonly string PATH_TAG = "Assets/";
private static bool _ingnoreExist = true;
// 这些动画是循环播放的(除此之外的循环动画需要手动创建)
private static readonly string[] LOOP_TAG = new string[] { "idle", "walk", "run" };
[MenuItem("Assets/CreateAnimation")]
static void DoCreateAnimation()
{
UnityEngine.Object obj = Selection.activeObject;
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (Directory.Exists(path)) {
// 如果是路径的话
Walk(path, (string fbxPath)=>{
fbxPath = fbxPath.Replace("\\", "/").Replace(".FBX", ".fbx");
string relativePath = fbxPath.Substring(fbxPath.IndexOf(PATH_TAG));
if (!relativePath.EndsWith(".fbx")) {
return;
}
DoCreateController(relativePath);
DoCreatePrefab(relativePath);
});
} else if (File.Exists(path)) {
// 如果是文件的话
string fbxPath = path.Replace("\\", "/").Replace(".FBX", ".fbx");
string relativePath = fbxPath.Substring(fbxPath.IndexOf(PATH_TAG));
if (!relativePath.EndsWith(".fbx")) {
return;
}
DoCreateController(relativePath);
DoCreatePrefab(relativePath);
} else {
// 路径不存在
Debug.LogError(string.Format("路径不存在: {0}", path));
}
AssetDatabase.Refresh();
Debug.Log(string.Format("转换完毕: {0}", path));
}
// 遍历文件夹
delegate void PathCallBack(string path);
static void Walk(string dir, PathCallBack callback)
{
DirectoryInfo d = new DirectoryInfo(dir);
if (d.GetDirectories() != null) {
FileInfo[] allf = d.GetFiles();
for (int i = 0; i < allf.Length; i++) {
if (callback != null) {
callback(allf[i].FullName);
}
}
DirectoryInfo[] alldr = d.GetDirectories();
if (alldr != null) {
for (int i = 0; i < alldr.Length; i++) {
Walk(alldr[i].FullName, callback);
}
}
}
}
static void DoCreateController(string assetPath)
{
// 获取animationclip
List<AnimationClip> clips = new List<AnimationClip>();
UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(assetPath);
foreach (var obj in objects) {
AnimationClip clip = obj as AnimationClip;
if (clip != null && clip.name.IndexOf("__preview__") == -1) {
clips.Add(clip);
}
}
if (clips.Count <= 0) {
return;
}
// controller文件可以和fbx放在一起
string acPath = assetPath.Replace(".fbx", ".controller");
// 创建动画控制器
AnimatorController animatorController = null;
// 更新controller无法及时更新,所以创建controller,但是不能单独prefab,否则会丢失关联
animatorController = AnimatorController.CreateAnimatorControllerAtPath(acPath);
AnimatorControllerLayer layer = animatorController.GetLayer(0);
UnityEditorInternal.StateMachine sm = layer.stateMachine;
Vector3 anyStatePosition = sm.anyStatePosition;
float OFFSET_X = 220;
float OFFSET_Y = 60;
float ITEM_PER_LINE = 4;
float originX = anyStatePosition.x - OFFSET_X * (ITEM_PER_LINE / 2.5f);
float originY = anyStatePosition.y + OFFSET_Y;
float x = originX;
float y = originY;
State defaultState = null;
foreach (AnimationClip newClip in clips) {
string clipName = newClip.name.ToLower();
State state = sm.AddState(clipName);
// 设置defaultState,优先选取idle动画
if (defaultState == null) {
if (clipName.IndexOf("idle") != -1) {
sm.defaultState = state;
defaultState = state;
}
} else {
if (clipName == "idle") {
sm.defaultState = state;
defaultState = state;
}
}
foreach (var tag in LOOP_TAG) {
//有些动画我希望天生它就动画循环
if (clipName.IndexOf(tag) != -1) {
SerializedObject serializedClip = new SerializedObject(newClip);
AnimationClipSettings clipSettings = new AnimationClipSettings(serializedClip.FindProperty("m_AnimationClipSettings"));
clipSettings.loopTime = true;
clipSettings.loopBlend = true;
serializedClip.ApplyModifiedProperties();
break;
}
}
state.SetAnimationClip(newClip, layer);
state.position = new Vector3(x, y, 0);
x += OFFSET_X;
if (x >= originX + OFFSET_X * ITEM_PER_LINE) {
x = originX;
y += OFFSET_Y;
}
}
AssetDatabase.SaveAssets();
}
static void DoCreatePrefab(string assetPath)
{
// 创建对应文件夹
string destPath = assetPath;
if (!destPath.StartsWith(PREFAB_PATH)) {
destPath = assetPath.Replace("Assets/", PREFAB_PATH);
}
string dirPath = System.IO.Path.GetDirectoryName(destPath);
if (!System.IO.Directory.Exists(dirPath)) {
System.IO.Directory.CreateDirectory(dirPath);
}
string acPath = assetPath.Replace(".fbx", ".controller");
string prefabPath = destPath.Replace(".fbx", ".prefab");
string name = assetPath.Substring(assetPath.LastIndexOf("/") + 1).Replace(".fbx", "");
// 创建prefab
GameObject fbx = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject;
GameObject go = Instantiate(fbx) as GameObject;
go.name = name;
// 如果controller文件存在,则创建对应的animator
if (System.IO.File.Exists(acPath)) {
AnimatorController animatorController = AssetDatabase.LoadAssetAtPath(acPath, typeof(AnimatorController)) as AnimatorController;
Animator animator = go.GetComponent<Animator>();
if (animator == null) {
animator = go.AddComponent<Animator>();
}
animator.applyRootMotion = false;
animator.runtimeAnimatorController = animatorController;
}
// 创建prefab
if (System.IO.File.Exists(prefabPath)) {
// 替换原来的prefab (每个prefab都有对应的udid不能直接使用新的,否则所有相关引用都将失效)
GameObject prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject;
PrefabUtility.ReplacePrefab(go, prefab);
} else {
// 创建新的prefab
PrefabUtility.CreatePrefab(prefabPath, go, ReplacePrefabOptions.ConnectToPrefab);
}
GameObject.DestroyImmediate(go, true);
AssetDatabase.SaveAssets();
}
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; } }
}
}1、生成AnimatorController和Prefab的时间不快,所以一次转换的模型不要太多。
2、原本可以更新AnimatorController内容会更好些,但是没有尝试成功,所以创建完controller就要更新Prefab,这样关联信息不会丢失(miss animator)
3、创建Prefab的时候,如果已存在,则更新Prefab。这样可以保证其他使用Prefab的地方不会丢失其信息。因为创建一个新的Prefab,则会生成一个新的uuid,这样原来的引用就不存在了。
4、在生成controller的时候,有判断animationclip,如果它是休闲或者跑步动作,则自动设置其为loop模式。这个不在脚本中处理,就需要自己一个模型一个模型去修改。如果不属于LOOP_TAG中的动画,然后又需要设置loop可以自己在Unity中操作修改。这里原本我希望可以通过ModelImporter来修改,但是没有找到正确的方法。所以参考雨松的方式,直接修改AnimationClip的属性。
这种资源(AssetBundle)使用流程有这么几个好处:
1、开发项目工程不会很大。以程序的思路加载资源。否则一个大的MMO,资源量可能几G。运行Unity都需要等几分钟。
2、AssetBundle可以完全由美术搞定,程序只关心资源加载使用。
3、创建AssetBundle的项目可以分类。比如场景美术一个项目,角色美术一个项目等等。这样可以进一步减小项目,并且减少冲突的机会。
标签:
原文地址:http://blog.csdn.net/langresser_king/article/details/43888771