淘先锋技术网

首页 1 2 3 4 5 6 7
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using UnityEngine.Rendering;
using System;


public class ModelInfoChecker
{
    #region 通用函数
    /// <summary>
    /// 判断选的的路径是否合规
    /// </summary>
    /// <param name="checkPath"></param>
    /// <param name="selectPath"></param>
    /// <returns></returns>
    private static bool CheckSelectPath(string[] checkPathList,out string selectPath)
    {
        //右键选择文件夹
        string guid = Selection.assetGUIDs[0]; 
        selectPath = AssetDatabase.GUIDToAssetPath(guid);
        string findPathShow="";
        foreach (var path in checkPathList)
        {
            if (selectPath.Contains(path))
            {
                return true;
            }
            if (findPathShow == "")
            {
                findPathShow += path;
            }
            else
            {
                findPathShow += "或"+path;
            }
        }
        string tip = string.Format("当前检测文件夹不是{0}", findPathShow);
        EditorUtility.DisplayDialog("提示", tip, "确定");
        return false;
    }
    //参数1 为要查找的总路径, 参数2 保存路径
    private static void GetTargetResourceDirs(string dirPath, ref List<string> dirs,string[]ignoreList,string[]extensionList)
    {
        
        if (Path.GetExtension(dirPath)!="")
        {
            //当前选中是资源
            if (CheckName(dirPath,ignoreList))
            {
                dirs.Add(dirPath);
            }
            return;
        }

        foreach (string path in Directory.GetFiles(dirPath))
        {
            //获取所有文件夹中包含后缀为 .prefab 的路径
            foreach (string extension in extensionList)
            {
                if (Path.GetExtension(path) == extension)
                {
                    string final_path = path.Substring(path.IndexOf("Asset")).Replace(@"\", "/");
                
                    if (CheckName(final_path,ignoreList))
                    {
                        dirs.Add(final_path);
                    }
                    break;
                }
            }
            
        }         
        if (Directory.GetDirectories(dirPath).Length > 0)  //遍历所有文件夹
        {
            foreach (string path in Directory.GetDirectories(dirPath))
            {
                GetTargetResourceDirs(path, ref dirs,ignoreList,extensionList);
            }
        }
    }
    private static bool CheckName(string prefabName,string[] IgnoreList)
    {
        foreach (string name in IgnoreList)
        {
            if (prefabName.Contains(name))
            {
                return false;
            }
        }

        return true;
    }

    #endregion
    
    //模型资源修改
    public static void ModelInfoModifyFunc(string[] checkPathList,Action<string>checkCallBack,string tip,string[]ignoreList,string[]extensionList)
    {
        string selectPath;
        if (!CheckSelectPath(checkPathList,out selectPath))
            return;
        List<string> dirs = new List<string>();
        GetTargetResourceDirs(selectPath, ref dirs,ignoreList,extensionList);

        if (dirs.Count==0)
        {
            EditorUtility.DisplayDialog("提示", "未找到需要修改的资源", "确定");
            return;
        }

        for (int i=0;i<dirs.Count;++i)
        {
            int startIndex = dirs[i].LastIndexOf("/") + 1;
            int extensionLength = Path.GetExtension(dirs[i]).Length;
            int length = dirs[i].Length - startIndex - extensionLength;
            string fileName = dirs[i].Substring(startIndex,length);

            string msg = string.Format("修改资源  {0}   {1}.......{2}/{3}",fileName,tip,i,dirs.Count);
            
            EditorUtility.DisplayProgressBar("提示",msg,(float)i/dirs.Count);
            checkCallBack?.Invoke(dirs[i]);
        }
        EditorUtility.ClearProgressBar();
        AssetDatabase.Refresh();
        
        string messag =  string.Format("{0}修改完毕,请记得将修改后的资源上传P4", tip);
        EditorUtility.DisplayDialog("提示", messag, "确定");  //修改动画压缩格式的时候,此处会引发GUI报错,可忽视
    }

    public  delegate bool CheckFuncDelegate(string path);
    
    //对指定路径,指定后缀资源进行筛选
    public static void ModelInfoCheckFunc(string[] checkPath,CheckFuncDelegate checkFunc,string tip,string[]ignoreList,string[]extensionList,string writePath)
    {
        string selectPath;
        if (!CheckSelectPath(checkPath, out selectPath))
            return;
        List<string> dirs=new List<string>();
        GetTargetResourceDirs(selectPath,ref dirs,ignoreList,extensionList);
        
        List<string> validList=new List<string>();
        
        for (int i=0;i<dirs.Count;++i)
        {
            int startIndex = dirs[i].LastIndexOf("/") + 1;
            int extensionLength = Path.GetExtension(dirs[i]).Length;
            int length = dirs[i].Length - startIndex - extensionLength;
            string fileName = dirs[i].Substring(startIndex,length);
            string msg = string.Format("{0}{1}筛选中...",tip,fileName);
            EditorUtility.DisplayProgressBar("提示",msg,(float)i/dirs.Count);
            if (checkFunc(dirs[i]))
            {
                validList.Add(dirs[i]);
            }
        }
        EditorUtility.ClearProgressBar();
        if (validList.Count==0)
        {
            EditorUtility.DisplayDialog("提示", "未找到指定资源", "确定");
            return;
        }
        
        if (writePath!="")
        {
            //需要把获得路径保存
            WriteText(writePath,validList);
            AssetDatabase.Refresh();
        }
        EditorUtility.DisplayDialog("提示", "资源筛查完成", "确定");    
    }

    private static void WriteText(string path,List<string>dirList)
    {
        string savePath = Application.dataPath + path;
        if (File.Exists(savePath))
        {
            FileInfo fileInfo=new FileInfo(savePath);
            fileInfo.Delete();
        }
        FileStream fileStream = new FileStream(savePath,FileMode.OpenOrCreate);
        StreamWriter sw = new StreamWriter(fileStream,Encoding.UTF8);
        foreach (var item in dirList)
        {
            sw.WriteLine(item);
        }
        sw.Close();
        fileStream.Close();
    }


    #region 模型Actor,Collider相关初始化
    
    [MenuItem("Assets/角色模型/Actor及Collider初始化",false,2)]
    public static void CheckModelActorInfo()
    {  
        string[] findRootPath = {"Assets/ResourcesAB/Prefab/Models"};
        string tip = "Actor/Collider属性";
        string[] ignoreList = {"hair","head"};//剔除掉 头  头发 
        string[] extensionList = { ".prefab"};
        ModelInfoModifyFunc(findRootPath,ActorAndColliderInfoInit,tip,ignoreList,extensionList);
    }
    
    private static void ActorAndColliderInfoInit(string prefabPath)
    {
        //两种 一种是骨骼mesh分离,一种未分离
        //首先判断是否挂载有Actor
        GameObject prefabObj=AssetDatabase.LoadAssetAtPath(prefabPath,typeof(GameObject))as GameObject;

        Actor actor = prefabObj.GetComponent<Actor>();

        //没有挂在Actor,有可能属于分离的mesh
        if (actor == null)
        {
            if (prefabPath.Contains("_ske"))
            {
                Debug.LogError("prefabPath  没有挂载Actor,请在角色编辑器中重新生成");
            }
            return;
        }
        RefreshActorInfo(actor,prefabPath);
        
        //判断是否有Collider
        if (actor.capsuleCollider==null)
        {
            RefreshColliderInfo(actor,prefabPath);
        }
        EditorUtility.SetDirty(prefabObj);
        AssetDatabase.SaveAssets();
    }

    private static void RefreshActorInfo(Actor actor,string objPath)
    {
        GameObject avatarObj = actor.avatar;
        if (avatarObj==null)
        {
            avatarObj = actor.transform.FindChildRecursively("avatar").gameObject;
            actor.avatar = avatarObj;
        }
        if (actor.cachedAnimator==null)
        {
            Animator animator = avatarObj.GetOrAddComponent<Animator>();
            actor.cachedAnimator = animator;
        }
        if (actor.cachedAnimationEventListener==null)
        {
            AnimationEventListener ael = avatarObj.GetOrAddComponent<AnimationEventListener>();
            ael.actor = actor;
            actor.cachedAnimationEventListener = ael;
        }

        if (actor.particleRoot==null)
        {
            //缺少粒子节点,需要先实例化才能创建子物体
            GameObject skeObj = GameObject.Instantiate(actor.gameObject);
            GameObject paticleObj = new GameObject("particleRoot");
            paticleObj.transform.SetParent(skeObj.transform, false);
            Actor actorSke = skeObj.GetComponent<Actor>();
            actorSke.particleRoot = paticleObj.transform;
            PrefabUtility.SaveAsPrefabAsset(skeObj, objPath);
            GameObject.DestroyImmediate(skeObj);
        }
    }

    private static void RefreshColliderInfo(Actor actor,string objPath)
    {
        //判断是否是_ske
        if (objPath.Contains("_ske"))
        { 
            CapsuleCollider capsuleCollider = actor.gameObject.AddComponent<CapsuleCollider>();
            //载入对应的mesh文件,计算collider大小,类似采集物的没有骨骼mesh分离的 不需要Collider  Collider是用于鼠标点击模型的交互
            string meshObjPath = objPath.Replace("_ske","");
            GameObject meshObj = AssetDatabase.LoadAssetAtPath(meshObjPath,typeof(GameObject))as GameObject;

            Renderer[] renderers = meshObj.transform.GetComponentsInChildren<SkinnedMeshRenderer>();
            if (renderers.Length==0)
            {
                renderers = meshObj.transform.GetComponentsInChildren<MeshRenderer>();
            }

            if (renderers.Length!=0)
            {
                Bounds bounds = renderers[0].bounds;
                for (int i=1;i<renderers.Length;++i)
                {
                    bounds.Encapsulate(renderers[i].bounds);
                }

                capsuleCollider.radius = Mathf.Abs(Mathf.Max(bounds.extents.x,bounds.extents.z));
            }
            else
            {
                capsuleCollider.radius = 1.5f;
            }

            GameObject headObj = actor.transform.FindChildRecursively("fx_head").gameObject;
            GameObject avatarObj = actor.avatar;
            if (avatarObj==null)
                return;
            if (headObj!=null)
            {
                Vector3 offset = headObj.transform.position - avatarObj.transform.position;
                Vector3 offsetLocal = avatarObj.transform.InverseTransformVector(offset);
                capsuleCollider.height=Mathf.Abs(offsetLocal.y);
                if (Mathf.Approximately(capsuleCollider.height,0))
                {
                    capsuleCollider.height = 1.5f;
                }
                capsuleCollider.center=new Vector3(0,capsuleCollider.height/2f,0);
            }
            else
            {
                capsuleCollider.height = 1.5f;
                capsuleCollider.center=new Vector3(0,capsuleCollider.height/2f,0);
            }
            actor.capsuleCollider = capsuleCollider;
        }
    }

    #endregion
    
    #region 修改模型Render属性
    
    // 在菜单来创建 选项 , 点击该选项执行搜索代码
    [MenuItem("Assets/角色模型/Mesh属性修改",false,2)]
    public static void CheckMeshRenderInfo()
    {
        string[] findRootPath = {"Assets/ResourcesAB/Prefab/Models"};
        string tip = "MeshRender属性"; 
        string[] ignoreNameList = {"_ske"};
        string[] extensionList = { ".prefab"};
        ModelInfoModifyFunc(findRootPath,ChangePrefabPropety,tip,ignoreNameList,extensionList);
    }
    
    private static void ChangePrefabPropety(string prefabPath)
    {
        GameObject prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(System.Object)) as GameObject;
        if (prefab == null)
            return;
        //一类人型SkinMesh(挂载有MeshNodes),一类没有挂MeshNodes,一类采集物等MeshRender,
        MeshNodes meshNodes = prefab.GetComponent<MeshNodes>();
        if (meshNodes != null && meshNodes.skinnedMeshInfoList != null)
        {
            for (int i=0;i<meshNodes.skinnedMeshInfoList.Count;++i)
            {
                SkinnedMeshInfo meshInfo = meshNodes.skinnedMeshInfoList[i];
                SkinnedMeshRenderer smr = meshInfo.smr;
                if (smr!=null)
                {
                    RefreshSkinMeshSettings(smr);
                }
            }
        }
        else
        {
            MeshRenderer[] mrs = prefab.GetComponentsInChildren<MeshRenderer>();
            foreach (MeshRenderer mr in mrs)
            {
                RefreshMeshSettings(mr);
            }

            SkinnedMeshRenderer[] smrs = prefab.GetComponentsInChildren<SkinnedMeshRenderer>();
            foreach (SkinnedMeshRenderer smr in smrs)
            {
                RefreshSkinMeshSettings(smr);
            }
        }

        EditorUtility.SetDirty(prefab);
        AssetDatabase.SaveAssets();
    }
    private static void RefreshSkinMeshSettings(SkinnedMeshRenderer sm)
    {
        if (sm == null)
            return;
        sm.lightProbeUsage = LightProbeUsage.Off;
        sm.reflectionProbeUsage = ReflectionProbeUsage.Off;
        sm.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
    }
    private static void RefreshMeshSettings(MeshRenderer mr)
    {
        if (mr == null)
            return;
        mr.lightProbeUsage = LightProbeUsage.Off;
        mr.reflectionProbeUsage = ReflectionProbeUsage.Off;
        mr.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
    }
    #endregion

    #region 筛选动画文件压缩是否合规
    [MenuItem("Assets/动画工具/动画压缩格式筛选",false,2)]
    public static void CheckAnimationCompression()
    {
        string[] findRootPath = {"Assets/ArtAssets/Animation","Assets/ArtAssets/CutScene"};
        string tip = "动画文件压缩格式";
        string[] ignoreNameList = { };
        string[] extensionList = { ".FBX"};
        string saveTxtPath = "\\AnimationCompressionCheck.txt";
        ModelInfoCheckFunc(findRootPath,AnimationCompressionCheck,tip,ignoreNameList,extensionList,saveTxtPath);
    }

    private static bool AnimationCompressionCheck(string path)
    {
        ModelImporter model=AssetImporter.GetAtPath(path)as ModelImporter;
        if(model.animationCompression!= ModelImporterAnimationCompression.Optimal)
           return true;
        return false;
    }
    #endregion
    
    #region FBX文件 animation压缩格式修改
    [MenuItem("Assets/动画工具/修改动画压缩格式",false,2)]
    public static void CheckAnimationCompressionInfo()
    {
        string[] findRootPath = {"Assets/ArtAssets/Animation","Assets/ArtAssets/CutScene"};
        string tip = "动画文件压缩格式";
        string[] ignoreNameList = { };
        string[] extensionList = { ".FBX"};
        ModelInfoModifyFunc(findRootPath,ModifyAnimationCompression,tip,ignoreNameList,extensionList);
    }

    private static void ModifyAnimationCompression(string path)
    {
        ModelImporter model=AssetImporter.GetAtPath(path)as ModelImporter;
        if (model == null)
            return;
        if (model.animationCompression != ModelImporterAnimationCompression.Optimal)
        {
            model.animationCompression = ModelImporterAnimationCompression.Optimal;
            AssetDatabase.SaveAssets();
            AssetDatabase.ImportAsset(path);
        }
    }
    #endregion

}

可用于右键选中某文件夹,筛选符合规范的文件,输出到txt中,或修改资源属性

API总结:

1.右键选中资源路径

string guid = Selection.assetGUIDs[0]; 
selectPath = AssetDatabase.GUIDToAssetPath(guid);

2.获取后缀

Path.GetExtension

3.获取指定路径内资源以及子文件夹

foreach (string path in Directory.GetFiles(dirPath))

foreach (string path in Directory.GetDirectories(dirPath))

4.Editor提示弹窗

EditorUtility.DisplayDialog("提示", "未找到需要修改的资源", "确定");

5.Editor进度弹窗

EditorUtility.DisplayProgressBar("提示",msg,(float)i/dirs.Count);
            
EditorUtility.ClearProgressBar();

6.写入txt

private static void WriteText(string path,List<string>dirList)
    {
        string savePath = Application.dataPath + path;
        if (File.Exists(savePath))
        {
            FileInfo fileInfo=new FileInfo(savePath);
            fileInfo.Delete();
        }
        FileStream fileStream = new FileStream(savePath,FileMode.OpenOrCreate);
        StreamWriter sw = new StreamWriter(fileStream,Encoding.UTF8);
        foreach (var item in dirList)
        {
            sw.WriteLine(item);
        }
        sw.Close();
        fileStream.Close();
    }

7.Editor载入资源,修改完毕后设置脏标记,保存     将实例化后的午提重新保存为预制体,覆盖原预制体

//第一种  只载入资源
GameObject prefabObj=AssetDatabase.LoadAssetAtPath(prefabPath,typeof(GameObject))as GameObject;

 EditorUtility.SetDirty(prefabObj);
 AssetDatabase.SaveAssets();


//第二种 将载入的资源实例化,修改后重新保存为预制体, 适用于需要实例化后设置子物体的操作
 GameObject skeObj = GameObject.Instantiate(actor.gameObject);
            
 PrefabUtility.SaveAsPrefabAsset(skeObj, objPath);
 GameObject.DestroyImmediate(skeObj);

8.载入FBX资源

 private static void ModifyAnimationCompression(string path)
    {
        ModelImporter model=AssetImporter.GetAtPath(path)as ModelImporter;
        if (model == null)
            return;
        if (model.animationCompression != ModelImporterAnimationCompression.Optimal)
        {
            model.animationCompression = ModelImporterAnimationCompression.Optimal;
          
            AssetDatabase.ImportAsset(path);
        }
    }