这个是我现在所知最正确的MecAnim的用法了。先上代码:
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System;
using System.Collections;
using System.Collections.Generic;
public class GetAnimationClips : AssetPostprocessor
{
void OnPostprocessModel(GameObject go)
{
List clips = new List();
UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(assetPath);
Debug.Log(objects.Length);
foreach (var obj in objects) {
AnimationClip clip = obj as AnimationClip;
if (clip != null && clip.name.IndexOf("__preview__") == -1) {
clips.Add(clip);
}
}
CreateAnimationController(assetPath, clips);
}
static void CreateAnimationController(string path, List clips)
{
path = path.Replace("\\", "/").Replace(".FBX", ".fbx");
string acPath = path.Replace(".fbx", ".controller");
string prefabPath = path.Replace(".fbx", ".prefab");
// 创建动画控制器
AnimatorController 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;
foreach (AnimationClip newClip in clips) {
State state = sm.AddState(newClip.name.ToLower());
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;
}
}
// 创建prefab
string name = path.Substring(path.LastIndexOf("/") + 1).Replace(".fbx", "");
prefabPath = "Assets/Resources/" + name + ".prefab";
GameObject go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject;
go.name = name;
Animator animator = go.GetComponent();
if (animator == null) {
animator = go.AddComponent();
}
animator.applyRootMotion = false;
animator.runtimeAnimatorController = animatorController;
//AssetDatabase.CreateAsset(go, prefabPath);
PrefabUtility.CreatePrefab(prefabPath, go);
GameObject.DestroyImmediate(go, true);
AssetDatabase.SaveAssets();
}
}
1、AssetDatabase.LoadAllAssetsAtPath通过这个函数可以加载fbx文件(包含所有动画信息)。这个是根据fbx文件获取AnimationClip的方法。
2、AssetPostprocessor这个是Unity资源加载的接口。自动生成动画控制器的代码可以不放在这里,可以扩展Menu菜单完成对应功能(如遍历选择文件夹下的fbx文件并进行相关处理)。
这个是我现在所知的MMO中最正确的MecAnim的用法:
1、不要手工编辑.controller文件,无论是加载动画还是连线,在MMO中动画量相当庞大,人工编辑这些绝对是无法让人接受的。
2、MecAnim有一定优势,如重定向,现在几乎所有的MMO都是用的老的动画系统。不过我认为,我的这个方法可以兼容老的动画系统和新的动画系统,或者说,可以使老的动画系统的项目平滑过渡到新的动画系统
3、我们维护动画的方式可能是这样的,美术给出模型,如果可能的话,最好切分好动画(貌似maya是支持的,blender也是支持的,但是3dmax未知,比如我使用blender导出火炬之光的模型到unity中,unity可以直接识别出切分好的动画,而不需要手动切动画)。如果没有的话,就需要一个配置文件(比如excel)维护好动画的起始、结束帧的信息。在导入模型的时候,可以根据这个配置文件通过代码添加AnimationClip。
4、自动生成AnimatorController的思路一样可以应用到老的动画系统,因为其最核心的内容就是如何通过fbx获取到AnimationClip信息。如果是老的动画系统就是把AnimationClip添加到prefab的Animation的动画列表中。
5、我是如何使用新的动画系统的:
GameObject prefab = Resources.Load("player") as GameObject;
GameObject go = Instantiate(prefab) as GameObject;
Animator animator = go.GetComponent();
animator.CrossFade("idle", 0.25f, 0);
controller里面不连线,也不用param控制,它只是一个容器。代码中直接使用CrossFade进行动画切换。这个有一个额外的好处,AssetBundle打包的prefab无法在Unity的Animator分页中看到其数据。自己控制动画切换可以做的更加自由。
补充 Unity5的修改:
UnityEditor.Animations.AnimatorState state = sm.AddState(clipName, new Vector3(x, y, 0));
state.motion = newClip;
API有一些修改,直接通过motion来设置clip。