美術(shù)導(dǎo)入的資源格式千奇百怪,我們程序需要的預(yù)制體一般為標(biāo)準(zhǔn)格式,比如模型放一層,物理放一層,腳本放一層。所以我們需要手動(dòng)設(shè)置一次結(jié)構(gòu),我做了個(gè)自動(dòng)化工具就可以一鍵設(shè)置好結(jié)構(gòu)了
美術(shù)所給的動(dòng)畫文件

image.png
自動(dòng)生成工具

image.png
生成一個(gè)程序預(yù)制體和一個(gè)Animator文件

image.png
自動(dòng)生成的controller文件

image.png
自動(dòng)生成的預(yù)制體結(jié)構(gòu)

image.png
下面是源碼
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace XFramework.AutoHandleAnimator
{
public class AutoHandleAnimatorEditorWindow : EditorWindow
{
//動(dòng)畫文件
public static UnityEngine.Object aniObj;
//Prefab
public static UnityEngine.Object prefabObj;
[MenuItem("美術(shù)工具/生成程序預(yù)制體")]
public static void OpenWindow()
{
GetWindowWithRect<AutoHandleAnimatorEditorWindow>(new Rect(500, 500, 360, 360), false, "生成程序預(yù)制體", true);
}
private void OnGUI()
{
GUILayout.BeginVertical();
aniObj = EditorGUILayout.ObjectField("動(dòng)畫文件", aniObj, typeof(UnityEngine.GameObject), false);
prefabObj = EditorGUILayout.ObjectField("預(yù)制體文件", prefabObj, typeof(UnityEngine.GameObject), false);
if (GUILayout.Button("生成"))
{
if (aniObj != null && prefabObj != null)
{
Generate();
base.Close();
}
}
GUILayout.EndVertical();
}
private void Generate()
{
if (!aniObj.name.EndsWith("_Ani"))
{
Debug.LogError("動(dòng)畫文件不以 _Ani 結(jié)尾");
return;
}
if (!prefabObj.name.EndsWith("_Skin"))
{
Debug.LogError("預(yù)制體文件不以 _Skin 結(jié)尾");
return;
}
new AutoHandleAnimatorRequest().Generate(aniObj, prefabObj);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
namespace XFramework.AutoHandleAnimator
{
public class AutoHandleAnimatorRequest
{
public void Generate(Object aniObj, Object prefabObj)
{
var aniPath = AssetDatabase.GetAssetPath(aniObj);
Debug.Log("正在處理:" + aniPath);
Object[] objs = AssetDatabase.LoadAllAssetsAtPath(aniPath);
List<AnimationClip> clipsList = new List<AnimationClip>();
for (int i = 0; i < objs.Length; i++)
{
var obj = objs[i];
if (obj is AnimationClip)
{
AnimationClip ac = obj as AnimationClip;
if (ac.name.Contains("Take 001")) continue;//去除預(yù)覽片段
if (ac.name.Contains("__preview__")) continue;//去除預(yù)覽片段
clipsList.Add(ac);
}
}
var prefabName = GetModelName(aniPath);
var aniController = CreateAnimatorController(prefabName, clipsList);
CreatePrefab(prefabName, aniController, prefabObj);
}
private void CreatePrefab(string prefabName, AnimatorController controller, Object prefabObj)
{
GameObject prefabRoot = new GameObject(prefabName);
GameObject modelGo = GameObject.Instantiate(prefabObj as GameObject);
modelGo.name = prefabObj.name;
modelGo.transform.parent = prefabRoot.transform;
var animator = modelGo.AddComponent<Animator>();
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
animator.runtimeAnimatorController = controller;
var targetPath = AutoHandleAnimatorPathSetting.ModelExportPath + prefabName + ".prefab";
//保存角色預(yù)制體
PrefabUtility.SaveAsPrefabAssetAndConnect(prefabRoot, targetPath, InteractionMode.UserAction);
GameObject.DestroyImmediate(prefabRoot);
Debug.Log("create gameobject: " + targetPath);
}
private AnimatorController CreateAnimatorController(string prefabName, List<AnimationClip> clips)
{
var path = AutoHandleAnimatorPathSetting.ModelExportPath + prefabName + "_ani.controller";
AssetDatabase.DeleteAsset(path);
AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath(path);
int rowCount = 8;//一列幾個(gè)
int currentRow = 0;//當(dāng)前列數(shù)
int currentRowCount = 0;//當(dāng)前列clip個(gè)數(shù)
AnimatorStateMachine baseLayerMachine = animatorController.layers[0].stateMachine;
//空狀態(tài)
baseLayerMachine.entryPosition = Vector3.zero;
baseLayerMachine.anyStatePosition = new Vector3(0f, 200f);
baseLayerMachine.AddState("null", new Vector3(0, 100f));
baseLayerMachine.exitPosition = new Vector3(0f, 300f);
//添加片段
for (int i = 0; i < clips.Count; i++)
{
if (i % rowCount == 0)
{
currentRow += 1;
currentRowCount = 0;
}
var x = currentRow * 300;
var y = currentRowCount * 100;
currentRowCount++;
AnimationClip clip = clips[i];
AnimatorState tempState = baseLayerMachine.AddState(clip.name, new Vector3(x, y, 0));
tempState.motion = clip;
EditorUtility.SetDirty(tempState);
}
EditorUtility.SetDirty(baseLayerMachine);
EditorUtility.SetDirty(animatorController);
Debug.Log("create controller: " + path);
return animatorController;
}
private string GetModelName(string path)
{
//女主角_Ani.FBX
var strs = path.Split('/');
strs = strs[strs.Length - 1].Split('.');
strs = strs[0].Split('_');
var modelName = strs[0];
return modelName;
}
}
}
路徑設(shè)置
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace XFramework.AutoHandleAnimator
{
public static class AutoHandleAnimatorPathSetting
{
/// <summary>
/// 需要處理的模型根目錄
/// </summary>
public const string ModelRootPath = "Assets/Arts/模型/角色/";
/// <summary>
/// 模型動(dòng)畫導(dǎo)出路徑
/// </summary>
public const string ModelExportPath = "Assets/HotUpdateResources/Prefab/AutoCreateModel/";
}
}
最后再附帶一個(gè)動(dòng)畫預(yù)覽小工具

image.png
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
[ExecuteAlways]
[CustomEditor(typeof(Animator), true)]//bool 子類是否可用
public class AniEditor : Editor
{
//目標(biāo)組件
private Animator animator;
//運(yùn)行時(shí)狀態(tài)機(jī)控制器
private RuntimeAnimatorController controller;
//動(dòng)畫片段
private AnimationClip[] clips;
//選中的片段下標(biāo)
private int curIndex;
//模擬時(shí)間
private float timer;
//上一幀時(shí)間
private float lastFrameTime;
//是在播放中
private bool isPlaying;
public void OnEnable()
{
Init();
}
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
isPlaying = false;
curIndex = 0;
timer = 0f;
animator = target as Animator;
controller = animator.runtimeAnimatorController;
if (controller)
{
clips = controller.animationClips;
}
else
{
clips = new AnimationClip[0];
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (clips.Length == 0)
{
EditorGUILayout.HelpBox("沒有動(dòng)畫片段!", MessageType.Warning);
return;
};
//檢查代碼塊中是否有任何控件被更改
EditorGUI.BeginChangeCheck();
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.Label("===========");
GUILayout.Label("<color=#00F5FF>動(dòng)畫預(yù)覽</color>", new GUIStyle() { richText = true, fontStyle = FontStyle.Normal, fontSize = 16 });
GUILayout.Label("===========");
GUILayout.Space(10);
GUILayout.EndHorizontal();
GUILayout.Label("當(dāng)前片段:");
//創(chuàng)建一個(gè)通用的彈出式選擇字段。
curIndex = EditorGUILayout.Popup(curIndex, clips.Select(p => p.name).ToArray()); //還原clip狀態(tài)
AnimationClip clip = clips[curIndex];
GUILayout.Label("播放長(zhǎng)度:");
timer = EditorGUILayout.Slider(timer, 0, clip.length);
string playAniBtnStr = isPlaying ? "暫停" : "播放動(dòng)畫";
if (GUILayout.Button(playAniBtnStr))
{
SetPlayAni(timer >= clip.length);
}
if (GUILayout.Button("返回到當(dāng)前片段第一幀"))
{
isPlaying = false;
timer = 0;
clip.SampleAnimation(animator.gameObject, timer);
}
if (isPlaying)
{
timer += Time.realtimeSinceStartup - lastFrameTime;
if (timer >= clip.length)
{
if (clip.isLooping)
{
timer = 0;
}
else
{
isPlaying = false;
timer = clip.length;
}
}
}
//重新繪制顯示此編輯器的檢查器。
Repaint();
lastFrameTime = Time.realtimeSinceStartup;
if (EditorGUI.EndChangeCheck() || isPlaying)
{
clip.SampleAnimation(animator.gameObject, timer);
}
}
/// <summary>
/// 播放動(dòng)畫
/// </summary>
/// <param name="rePlay"></param>
private void SetPlayAni(bool rePlay)
{
if (rePlay)
{
timer = 0f;
isPlaying = true;
}
else
{
isPlaying = !isPlaying;
}
}
}