参考文章:https://cloud.tencent.com/developer/article/1833109
最近项目需求需要对工程中的动画文件进行优化,经分析发现,工程存在两种动画文件,一种是后缀为.anim的普通Animation文件,还有一种是.fbx的模型动画文件,本文主要针对于fbx动画文件的优化,普通Animation文件优化空间不大。通过查阅网上资料,基本上优化思路也都一致,由于网上的各种教程并没有教怎么去自动提取并绑定fbx中的动画,因此本人记录一下自身总结出来的方法,优化思路大致分为两点(经过研究发现还有第三点思路,等我测试完毕整理后再发出来,大致思路是直接检测动画中是否存在没有变化的帧曲线数据,如果存在就去掉,感觉也是一个很大的优化项):
一、缩短动画曲线数据产生的浮点数精度
因为最终打包动画文件都会转成assetbundle二进制格式文件,不管怎么缩短浮点数精度,最终都是占用一个float的空间(之所以看起来减少了文件大小,是因为unityEditor下存储的是text格式,所以会导致文件表面上减小了),那究竟是怎么达到压缩效果的呢?
原来,通过降低浮点数位数后,将曲线上过于接近的数值(例如相差数值出现在小数点4位以后)直接变为一致,可以产生更多的const曲线,从而让引擎达到更高效存储的效果,进而达到所谓的“压缩”结果。缩短float类型的精度,导致动画文件内点的位置发生了变化,引起Constant Curve和Dense Curve的数量也有可能发生变化,最终可能导致动画的点更稀疏,而连续相同的点更多了。所以Dense Curve是减少了,Constant Curve是增多了,总的内存是减小了。
代码如下:
Tips:网上有的文章说获取动画Clip的EditorCurveBinding有两种方法,非Legacy(选中动画的Inspector面板的右上角点击…切换为Debug模式就可以看到Legacy选项了)动画使用GetCurveBindings、GetEditorCurve和SetEditorCurve方法,Legacy动画要使用GetObjectReferenceCurveBindings、GetObjectReferenceCurve和SetObjectReferenceCurve方法。但是经过本人亲测,GetCurveBindings这套方式两种动画皆适用,而且另一套方法由于返回值不一样,导致使用起来极为繁琐,有知道这两套方法详细区别的大佬麻烦在评论区告知一下,感激不尽。
////// 压缩浮点数精度 /// /// private static void CompressAnimFloatNum(AnimationClip clip) { if (clip == null) return; EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip); foreach (var curveBinding in curveBindings) { var curve = AnimationUtility.GetEditorCurve(clip, curveBinding); if (curve == null || curve.keys == null) { var path = AssetDatabase.GetAssetPath(clip); OptimizeAnimTools_Auto.optimizedPaths.Remove(path); Debug.LogError("资源发生错误,重新导入,path:" + path); AssetDatabase.ImportAsset(path); return; } var keyFrames = curve.keys; for (int i = 0; i < keyFrames.Length; i++) { var key = keyFrames[i]; key.value = float.Parse(key.value.ToString("f" + limitFloatNum)); key.inTangent = float.Parse(key.inTangent.ToString("f" + limitFloatNum)); key.outTangent = float.Parse(key.outTangent.ToString("f" + limitFloatNum)); key.inWeight = float.Parse(key.inWeight.ToString("f" + limitFloatNum)); key.outWeight = float.Parse(key.outWeight.ToString("f" + limitFloatNum)); keyFrames[i] = key; } curve.keys = keyFrames; //优化普通Animation动画有时会出现错误修改未发生变化的scale值,一番踩坑之后,发现调用一次这个就好了,希望有懂的大佬可以解答一下,感激不尽! AnimationUtility.SetEditorCurve(clip, curveBinding, curve); clip.SetCurve(curveBinding.path, curveBinding.type, curveBinding.propertyName, curve); } }
二、去除fbx动画文件中生成的冗余Scale曲线数据
从下图可以看到,fbx文件中的动画几乎每一个节点都生成了Scale曲线数据,然而fbx动画基本却不会用到Scale曲线来做动画(为了以防万一,如果需要用到这种方式来优化,请向美术同事确认是否没有用过Scale缩放来做动画),所以这部分数据是可以去除的,这部分优化程度比较大,最好尽可能优化掉。还需注意一点,这种优化方式仅限于fbx中的模型动画,如果是UI上的普通Animation动画,是不需要优化Scale曲线的,因为Unity不会生成冗余的Scale数据,而且美术同学很可能会在普通Animation动画中使用Scale缩放。
代码如下:
////// 删除scale曲线(一般用于模型动画)相关 /// /// private static void ReduceScaleKey(AnimationClip clip) { EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip); foreach (EditorCurveBinding curveBinding in curveBindings) { string name = curveBinding.propertyName.ToLower(); if (name.Contains("scale")) { AnimationUtility.SetEditorCurve(clip, curveBinding, null); } } }
以上介绍完了动画的两种优化方式,接下来开始介绍怎么从Fbx文件中提取出包含的Aniamtion动画文件,并重新绑定优化新创建的Aniamtion动画文件。
因为Fbx文件在Unity中是只读的,内含的动画文件也是只读的,修改动画的结果实际上是保存在Library/metadata,并不会修改Fbx文件,也就是说这个修改是本地化的操作,无法放入版本管理。fbx中的animation是read-only的,要编辑需要将动画文件复制出来,可以选中fbx中的动画文件,ctrl+D复制一份出来,可以跟优化后的animation文件做个对比,复制出的文件是可以编辑的,但是作为程序员,我们肯定是希望自动化操作的。从Fbx拷贝出动画后记得一定要重新绑定新生成的Animation文件,一般Fbx动画都会生成一个AnimatorController来控制动画,修改其中的引用就好了,代码中生成的逻辑是按照我们自己项目的文件结构来的,新的Animation会输出到Fbx所在目录的Animation文件夹下,如有需要,可以自行修改,代码如下:
////// 从Fbx文件中拷贝动画并优化和重新绑定引用 /// /// fbx文件的相对路径 private static void CopyClipAndCompress_FromFbx(string path) { Object[] objs = AssetDatabase.LoadAllAssetsAtPath(path); //fbx相关的模型prefeb所在文件夹相对路径(根据自己项目的文件架构自行修改路径相关代码) string modelDirectoryPath = ConvertToAssetPath(new DirectoryInfo(path).Parent.Parent.FullName); //提取出的Animation动画输出到父目录的父目录下的Animation文件夹下 string outAnimDirectoryPath = $"{modelDirectoryPath}/Animation"; if (!AssetDatabase.IsValidFolder(outAnimDirectoryPath)) { AssetDatabase.CreateFolder(modelDirectoryPath, "Animation"); } string fbxFileName = Path.GetFileName(path); var animatorGuids = AssetDatabase.FindAssets("t:AnimatorController", new[] {modelDirectoryPath}); if (animatorGuids == null || animatorGuids.Length == 0) { Debug.LogError($"从{fbxFileName} 提取动画失败,没有找到{modelDirectoryPath}目录下的AnimatorController文件!"); return; } foreach (var _obj in objs) { if (_obj is AnimationClip && !_obj.name.Contains("__preview__")) { EditorUtility.DisplayProgressBar($"从{fbxFileName}提取动画", _obj.name, 0); //重新绑定AnimatorController引用 bool isBindSuccess = ReBindAnimatorController(path, _obj as AnimationClip, animatorGuids, outAnimDirectoryPath,out AnimationClip newAnimationClip); if (!isBindSuccess) { Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,没有找到对应的AnimatorController引用!"); continue; } if (newAnimationClip == null || newAnimationClip.length==0) { Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,未能成功生成Animation文件!"); continue; } } } } ////// 重新绑定AnimatorController引用 /// private static bool ReBindAnimatorController(string fbxPath, AnimationClip animationClip, string[] animatorGuids, string outAnimDirectoryPath,out AnimationClip newClip) { newClip = null; bool isBindSuccess = false; for (int i = 0; i < animatorGuids.Length; i++) { var animatorPath = AssetDatabase.GUIDToAssetPath(animatorGuids[i]); string newAnimatorPath = $"{outAnimDirectoryPath}/{Path.GetFileName(animatorPath)}"; //把对应的AnimatorController文件也移动到animation输出文件夹 AssetDatabase.MoveAsset(animatorPath, newAnimatorPath); AnimatorController animatorController = AssetDatabase.LoadAssetAtPath(newAnimatorPath); if (animatorController == null) { return false; } var animLayers = animatorController.layers; foreach (var animLayer in animLayers) { foreach (var animatorState in animLayer.stateMachine.states) { if (animatorState.state.motion == null) { Debug.LogError($"{animatorPath}的动画状态{animatorState.state.name}没有绑定动画引用"); continue; } if (AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(animatorState.state.motion)) == AssetDatabase.AssetPathToGUID(fbxPath) || animatorState.state.motion.name == animationClip.name) { if (newClip == null) { newClip = new AnimationClip(); EditorUtility.CopySerialized(animationClip, newClip); string outAnimPath = $"{outAnimDirectoryPath}/{animationClip.name}.anim"; //优化提取出来的animation动画 ReduceScaleKey(newClip); CompressAnimFloatNum(newClip); OptimizeAnimToolsAuto.optimizedPaths.Add(outAnimPath); AssetDatabase.CreateAsset(newClip, outAnimPath); Debug.Log($"从{Path.GetFileName(fbxPath)} 提取动画 {animationClip.name} 输出到 {outAnimPath} 成功!"); } isBindSuccess = true; animatorState.state.motion = newClip; } } } } return isBindSuccess; }
还有监听导入时自动优化的代码,需要自取:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace EditorTools
{
///
/// 监听导入资源时自动优化文件
///
public class OptimizeAnimTools_Auto : AssetPostprocessor
{
///
/// 当前已经导入的文件,用于防止重复导入
///
public static List optimizedPaths = new List();
//资源导入之后
private static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths)
{
AssetDatabase.Refresh();
int length = imported.Length;
for (int i = 0; i < length; i++)
{
// Debug.LogError("导入:"+imported[i]);
var obj = AssetDatabase.LoadMainAssetAtPath(imported[i]);
if (obj != null && (imported[i].EndsWith(".anim", StringComparison.OrdinalIgnoreCase)
||imported[i].EndsWith(".fbx", StringComparison.OrdinalIgnoreCase))
&& !optimizedPaths.Contains(imported[i]))
{
EditorUtility.DisplayProgressBar("导入时自动优化动画文件", imported[i], (float) i / length);
OptimizeAnimTools.OptimizeAssetFile(obj);
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.ClearProgressBar();
}
}
}
最后,直接奉献我的Editor工具脚本全部代码吧,方便一些人可以直接复制使用:
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.AccessControl;
using System.Text;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using UnityEngine.Profiling;
using Object = UnityEngine.Object;
namespace EditorTools
{
///
/// 优化动画文件
/// 通过降低float精度,去除无用的scale曲线,从而降低动画的存储占用、内存占用和加载时间.
///
public class OptimizeAnimTools : EditorWindow
{
///
/// 限制动画精度位数
///
private const int limitFloatNum = 3;
[MenuItem("Assets/动画优化(选中文件或文件夹)/优化压缩普通Animation文件")]
static void OptimizeAssetFileTools()
{
try
{
if (Selection.assetGUIDs.Length <= 0)
{
EditorUtility.DisplayDialog("提示", "请先选择至少一个文件或文件夹!!! ", "OK");
return;
}
for (int i = 0; i < Selection.assetGUIDs.Length; i++)
{
Debug.Log("当前选中的文件或文件夹:" + AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[i]));
}
OptimizeAnimToolsAuto.optimizedPaths.Clear();
Object[] selectionAsset = Selection.GetFiltered(typeof(object), SelectionMode.DeepAssets);
int length = selectionAsset.Length;
for (int i = 0; i < length; i++)
{
string path = AssetDatabase.GetAssetPath(selectionAsset[i]);
if (string.IsNullOrEmpty(path) || Tools.EditorTools.isDirectoryPath(path) || !path.EndsWith(".anim"))
{
continue;
}
EditorUtility.DisplayProgressBar("优化animation文件", path, (float) i / length);
OptimizeAssetFile(selectionAsset[i]);
}
AssetDatabase.Refresh();
OptimizeAnimToolsAuto.optimizedPaths.Clear();
AssetDatabase.SaveAssets();
EditorUtility.UnloadUnusedAssetsImmediate();
EditorUtility.ClearProgressBar();
Debug.Log("-----------------优化完成-----------------");
}
catch (Exception e)
{
EditorUtility.ClearProgressBar();
Debug.LogException(e);
throw;
}
}
[MenuItem("Assets/动画优化(选中文件或文件夹)/提取并优化fbx中的动画")]
static void OptimizeAssetFileToolsFromFbx()
{
try
{
if (Selection.assetGUIDs.Length <= 0)
{
EditorUtility.DisplayDialog("提示", "请先选择至少一个文件或文件夹!!! ", "OK");
return;
}
for (int i = 0; i < Selection.assetGUIDs.Length; i++)
{
Debug.Log("当前选中的文件或文件夹:" + AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[i]));
}
OptimizeAnimToolsAuto.optimizedPaths.Clear();
Object[] selectionAsset = Selection.GetFiltered(typeof(object), SelectionMode.DeepAssets);
int length = selectionAsset.Length;
for (int i = 0; i < length; i++)
{
string path = AssetDatabase.GetAssetPath(selectionAsset[i]);
if (string.IsNullOrEmpty(path) || Tools.EditorTools.isDirectoryPath(path) || !path.EndsWith(".FBX"))
{
continue;
}
OptimizeAssetFile(selectionAsset[i]);
}
OptimizeAnimToolsAuto.optimizedPaths.Clear();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.UnloadUnusedAssetsImmediate();
EditorUtility.ClearProgressBar();
Debug.Log("-----------------优化完成-----------------");
}
catch (Exception e)
{
EditorUtility.ClearProgressBar();
Debug.LogException(e);
throw;
}
}
[MenuItem("Assets/动画优化(选中文件或文件夹)/提取优化fbx中的动画并生成ab包对比信息")]
public static void OptimizeAssetFileToolsAndCompareFromFbx()
{
if (Selection.assetGUIDs.Length <= 0)
{
EditorUtility.DisplayDialog("提示", "请先选择至少一个文件或文件夹!!! ", "OK");
return;
}
Dictionary outInfos_origin = AndroidBuild.exportResBundle(BuildAssetBundleOptions.ChunkBasedCompression, ".controller", true);
Dictionary outFileSizeInfos_orgin = new Dictionary();
foreach (var outInfo in outInfos_origin)
{
outFileSizeInfos_orgin.Add(outInfo.Key, EditorUtil.GetFileZie(outInfo.Value));
}
OptimizeAssetFileToolsFromFbx();
Dictionary outInfos_optimize = AndroidBuild.exportResBundle(BuildAssetBundleOptions.ChunkBasedCompression, ".controller", true);
StringBuilder sb = new StringBuilder();
long totalOriginSize = 0;
long totalOptimizeSize = 0;
foreach (var outInfo in outInfos_origin)
{
totalOriginSize += outFileSizeInfos_orgin[outInfo.Key] / 1024;
sb.Append($"{outInfo.Key} ab大小变化:{outFileSizeInfos_orgin[outInfo.Key]}B --> ");
if (outInfos_optimize.ContainsKey(outInfo.Key))
{
long optimizedSize = EditorUtil.GetFileZie(outInfos_optimize[outInfo.Key]);
totalOptimizeSize += (outFileSizeInfos_orgin[outInfo.Key] - optimizedSize) / 1024;
sb.AppendLine($"{optimizedSize}B = {(outFileSizeInfos_orgin[outInfo.Key] - optimizedSize) / 1024}KB");
AssetDatabase.DeleteAsset(EditorUtil.ConvertToAssetPath(outInfos_optimize[outInfo.Key]));
}
else
{
sb.AppendLine();
}
}
sb.Insert(0, $"AssetBundle包文件初始总大小:{totalOriginSize}KB 总共优化大小:{totalOptimizeSize}KBnn");
string filePath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]) + "/优化前后生成的ab文件信息对比.txt";
Debug.Log($"----------优化完成----------nab文件对比信息输出路径:{filePath}");
File.WriteAllText(filePath, sb.ToString());
AssetDatabase.SaveAssets();
EditorUtility.UnloadUnusedAssetsImmediate();
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
}
///
/// 优化资源文件
///
///
public static void OptimizeAssetFile(Object obj)
{
if (obj == null) return;
var path = AssetDatabase.GetAssetPath(obj);
OptimizeAnimToolsAuto.optimizedPaths.Add(path);
//如果是动画文件直接优化
if (obj is AnimationClip)
{
long originFileSize = GetFileZie(path);
long originMemorySize = GetMemorySize(obj);
AnimationClip clip = obj as AnimationClip;
// 普通animation文件不会存在多余无用的scale曲线数据
// ReduceScaleKey(clip);
CompressAnimFloatNum(clip);
Debug.Log(
$"{path} 优化前后 FileSize:{originFileSize}-->{GetFileZie(path)} MemorySize:{originMemorySize}-->{GetMemorySize(obj)}>");
}
else if (path.EndsWith(".fbx", StringComparison.OrdinalIgnoreCase))
{
//fbx文件先提取动画再优化
CopyClipAndCompress_FromFbx(path);
}
}
///
/// 从Fbx文件中拷贝动画并优化和重新绑定引用
///
/// fbx文件的相对路径
private static void CopyClipAndCompress_FromFbx(string path)
{
Object[] objs = AssetDatabase.LoadAllAssetsAtPath(path);
//fbx相关的模型prefeb所在文件夹相对路径(根据自己项目的文件架构自行修改路径相关代码)
string modelDirectoryPath = ConvertToAssetPath(new DirectoryInfo(path).Parent.Parent.FullName);
//提取出的Animation动画输出到父目录的父目录下的Animation文件夹下
string outAnimDirectoryPath = $"{modelDirectoryPath}/Animation";
if (!AssetDatabase.IsValidFolder(outAnimDirectoryPath))
{
AssetDatabase.CreateFolder(modelDirectoryPath, "Animation");
}
string fbxFileName = Path.GetFileName(path);
var animatorGuids = AssetDatabase.FindAssets("t:AnimatorController", new[] {modelDirectoryPath});
if (animatorGuids == null || animatorGuids.Length == 0)
{
Debug.LogError($"从{fbxFileName} 提取动画失败,没有找到{modelDirectoryPath}目录下的AnimatorController文件!");
return;
}
foreach (var _obj in objs)
{
if (_obj is AnimationClip && !_obj.name.Contains("__preview__"))
{
EditorUtility.DisplayProgressBar($"从{fbxFileName}提取动画", _obj.name, 0);
//重新绑定AnimatorController引用
bool isBindSuccess = ReBindAnimatorController(path, _obj as AnimationClip, animatorGuids, outAnimDirectoryPath,out AnimationClip newAnimationClip);
if (!isBindSuccess)
{
Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,没有找到对应的AnimatorController引用!");
continue;
}
if (newAnimationClip == null || newAnimationClip.length==0)
{
Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,未能成功生成Animation文件!");
continue;
}
}
}
}
///
/// 重新绑定AnimatorController引用
///
private static bool ReBindAnimatorController(string fbxPath, AnimationClip animationClip, string[] animatorGuids, string outAnimDirectoryPath,out AnimationClip newClip)
{
newClip = null;
bool isBindSuccess = false;
for (int i = 0; i < animatorGuids.Length; i++)
{
var animatorPath = AssetDatabase.GUIDToAssetPath(animatorGuids[i]);
string newAnimatorPath = $"{outAnimDirectoryPath}/{Path.GetFileName(animatorPath)}";
//把对应的AnimatorController文件也移动到animation输出文件夹
AssetDatabase.MoveAsset(animatorPath, newAnimatorPath);
AnimatorController animatorController = AssetDatabase.LoadAssetAtPath(newAnimatorPath);
if (animatorController == null)
{
return false;
}
var animLayers = animatorController.layers;
foreach (var animLayer in animLayers)
{
foreach (var animatorState in animLayer.stateMachine.states)
{
if (animatorState.state.motion == null)
{
Debug.LogError($"{animatorPath}的动画状态{animatorState.state.name}没有绑定动画引用");
continue;
}
if (AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(animatorState.state.motion)) ==
AssetDatabase.AssetPathToGUID(fbxPath) || animatorState.state.motion.name == animationClip.name)
{
if (newClip == null)
{
newClip = new AnimationClip();
EditorUtility.CopySerialized(animationClip, newClip);
string outAnimPath = $"{outAnimDirectoryPath}/{animationClip.name}.anim";
//优化提取出来的animation动画
ReduceScaleKey(newClip);
CompressAnimFloatNum(newClip);
OptimizeAnimToolsAuto.optimizedPaths.Add(outAnimPath);
AssetDatabase.CreateAsset(newClip, outAnimPath);
Debug.Log($"从{Path.GetFileName(fbxPath)} 提取动画 {animationClip.name} 输出到 {outAnimPath} 成功!");
}
isBindSuccess = true;
animatorState.state.motion = newClip;
}
}
}
}
return isBindSuccess;
}
///
/// 压缩浮点数精度
///
///
private static void CompressAnimFloatNum(AnimationClip clip)
{
if (clip == null) return;
EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
foreach (var curveBinding in curveBindings)
{
var curve = AnimationUtility.GetEditorCurve(clip, curveBinding);
if (curve == null || curve.keys == null)
{
var path = AssetDatabase.GetAssetPath(clip);
OptimizeAnimToolsAuto.optimizedPaths.Remove(path);
Debug.LogError("资源发生错误,将重新导入,path:" + path);
AssetDatabase.ImportAsset(path);
return;
}
var keyFrames = curve.keys;
for (int i = 0; i < keyFrames.Length; i++)
{
var key = keyFrames[i];
key.value = float.Parse(key.value.ToString("f" + limitFloatNum));
key.inTangent = float.Parse(key.inTangent.ToString("f" + limitFloatNum));
key.outTangent = float.Parse(key.outTangent.ToString("f" + limitFloatNum));
key.inWeight = float.Parse(key.inWeight.ToString("f" + limitFloatNum));
key.outWeight = float.Parse(key.outWeight.ToString("f" + limitFloatNum));
keyFrames[i] = key;
}
curve.keys = keyFrames;
//有时候会出现错误修改普通animation动画未发生变化的scale值,一番踩坑之后,发现调用一次这个就好了,希望有懂的大佬可以解答一下,感激不尽!QQ:943166811
AnimationUtility.SetEditorCurve(clip, curveBinding, curve);
clip.SetCurve(curveBinding.path, curveBinding.type, curveBinding.propertyName, curve);
}
}
///
/// 删除scale曲线(一般用于骨骼动画)相关
///
///
private static void ReduceScaleKey(AnimationClip clip)
{
EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
foreach (EditorCurveBinding curveBinding in curveBindings)
{
string name = curveBinding.propertyName.ToLower();
if (name.Contains("scale"))
{
AnimationUtility.SetEditorCurve(clip, curveBinding, null);
}
}
}
///-------------下面是用到的工具方法----------------------
///
/// 绝对路径转化成Unity资源目录相对路径
///
///
public static string ConvertToAssetPath(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
str = str.Replace('\', '/');
str = str.Substring(str.IndexOf("Assets"));
return str;
}
///
/// Unity资源目录相对路径转化成系统绝对路径
///
///
public static string ConvertToExplorePath(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
DirectoryInfo directoryInfo=new DirectoryInfo(str);
return directoryInfo.FullName;
}
///
/// 获取文件大小
///
///
public static long GetFileZie(string path)
{
FileInfo fi = new FileInfo(path);
return fi.Length;
}
///
/// 获取内存占用大小
///
///
public static long GetMemorySize(UnityEngine.Object obj)
{
return Profiler.GetRuntimeMemorySizeLong(obj);
}
///
/// 路径是否是文件夹
///
public static bool isDirectoryPath(string path)
{
if (File.GetAttributes(path).CompareTo(FileAttributes.Directory) == 0)
{
return true;
}
return false;
}
///
/// 选中的文件或文件夹打ab包
///
///
///
/// 默认生成的ab文件路径为源文件路径
public static Dictionary exportResBundle(BuildAssetBundleOptions op, string filterSuffix = null, bool isExportDefaultPath = false)
{
//<源文件名字,ab文件路径>
Dictionary outInfo = new Dictionary();
var guiDs = Selection.assetGUIDs;
if (guiDs == null || guiDs.Length == 0)
{
Debug.LogError("please select a object or a folder");
return outInfo;
}
string selPath = null;
if (!isExportDefaultPath)
{
selPath = EditorUtility.OpenFolderPanel("select out directory", new DirectoryInfo(AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0])).Parent.FullName, "");
if (selPath.Length <= 0)
{
isExportDefaultPath = true;
}
}
Object[] selectionAsset = Selection.GetFiltered(typeof(object), SelectionMode.DeepAssets);
int length = selectionAsset.Length;
for (int i = 0; i < length; i++)
{
var obj = selectionAsset[i];
string path = AssetDatabase.GetAssetPath(obj);
Debug.LogError("path 111:"+path);
if (string.IsNullOrEmpty(path) || EditorTools.Tools.EditorTools.isDirectoryPath(path) || (filterSuffix != null && !path.EndsWith(filterSuffix)))
{
continue;
}
Debug.LogError("path 222:"+path);
if (isExportDefaultPath)
{
selPath = new DirectoryInfo(path).Parent.FullName;
}
var compressType = op == BuildAssetBundleOptions.ChunkBasedCompression ? "lz4" : "nocompress";
var outPath = $"{selPath}/{obj.name}_{compressType}.bundle";
outInfo.Add(obj.name, outPath);
var ret = BuildPipeline.BuildAssetBundle(obj, null, outPath, op | BuildAssetBundleOptions.CollectDependencies, BuildTarget.Android);
if (ret)
{
D.Log("export bundle successful! " + outPath);
}
else
{
D.Error("export bundle failed");
}
}
return outInfo;
}
}
}



