using UnityEngine; using System.Collections.Generic; using UnityEditor; using UnityEditor.Animations; using System.IO; public class ResourcesBuilder { protected const byte param_NoExist = 1; protected const byte param_Prefab = 2; protected const byte param_Clip = 4; protected const byte param_AnimatorController = 8; protected class MountPointInfo { public string path; public string name; public Vector3 localPosition; public Quaternion localRotation; public Vector3 localScale; } protected class ColliderInfo { public Vector3 center; public float radius; public float height; } public static int buildParams = 0; public static bool OnlyBuildNoExist { get { return (buildParams & param_NoExist) != 0; } set { if (value) { buildParams |= param_NoExist; } else { buildParams &= ~param_NoExist; } } } public static bool IsBuildPrefab { get { return (buildParams & param_Prefab) != 0; } set { if (value) { buildParams |= param_Prefab; } else { buildParams &= ~param_Prefab; } } } public static bool IsBuildAnimationClip { get { return (buildParams & param_Clip) != 0; } set { if (value) { buildParams |= param_Clip; } else { buildParams &= ~param_Clip; } } } public static bool IsBuildAnimatorController { get { return (buildParams & param_AnimatorController) != 0; } set { if (value) { buildParams |= param_AnimatorController; } else { buildParams &= ~param_AnimatorController; } } } protected static List m_MountPointInfoList = new List(); public void BuildPrefab(string path, string resName, string suffix, string outName = null) { if (string.IsNullOrEmpty(outName)) { outName = resName; } string _absOriginalePath = string.Format("{0}/ART/Role/{1}/{2}.FBX", Application.dataPath, path, resName); string _originalAssetPath = string.Format("Assets/ART/Role/{0}/{1}.FBX", path, resName); string _absAssetPath = string.Format("{0}Mob/{1}{2}.prefab", ResourcesPath.ResourcesOutPath, suffix, outName); string _assetPath = string.Format("{0}Mob/{1}{2}.prefab", ResourcesPath.ResourcesOutAssetPath, suffix, outName); string _acAbsAssetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutPath, path, AnimatorControllerLoader.controllerSuffix, outName); string _acAssetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutAssetPath, path, AnimatorControllerLoader.controllerSuffix, outName); if (string.IsNullOrEmpty(_absOriginalePath)) { return; } // 指定动画控制器 if (!File.Exists(_acAbsAssetPath)) { Debug.LogWarningFormat("检测到此prefab资源: {0} 的AnimatorContorller(动画控制器)资源尚未生成, 这里需要注意啊此资源是否需要先生成AnimatorContorller(动画控制器), 再生成prefab资源.", path); } ColliderInfo _colliderInfo = null; if (File.Exists(_absAssetPath)) { if (OnlyBuildNoExist) { //Debug.LogFormat("当前为只生成不存在的资源模式, 这里检测到 {0} 已经存在, 不进行生成."); return; } GameObject _originalAssetRes = null; _originalAssetRes = AssetDatabase.LoadAssetAtPath(_assetPath); if (_originalAssetRes) { Transform _mpNode; // 这里需要记录旧的对象身上的绑点信息, 然后给新的prefab设置绑点 m_MountPointInfoList.Clear(); _mpNode = _originalAssetRes.transform.GetChildTransformDeeply("A_Down"); if (_mpNode) { CreateMountPointInfo(_mpNode); } _mpNode = _originalAssetRes.transform.GetChildTransformDeeply("A_Name"); if (_mpNode) { CreateMountPointInfo(_mpNode); } _mpNode = _originalAssetRes.transform.GetChildTransformDeeply("A_Name2"); if (_mpNode) { CreateMountPointInfo(_mpNode); } _mpNode = _originalAssetRes.transform.GetChildTransformDeeply("A_Hit"); if (_mpNode) { CreateMountPointInfo(_mpNode); } _mpNode = _originalAssetRes.transform.GetChildTransformDeeply("A_Stun"); if (_mpNode) { CreateMountPointInfo(_mpNode); } CapsuleCollider _collider = _originalAssetRes.GetComponent(); if (_collider) { _colliderInfo = new ColliderInfo(); _colliderInfo.center = _collider.center; _colliderInfo.height = _collider.height; _colliderInfo.radius = _collider.radius; } } } GameObject _fbxRes = AssetDatabase.LoadAssetAtPath(_originalAssetPath); if (_fbxRes == null) { _originalAssetPath = _originalAssetPath.Remove(_originalAssetPath.LastIndexOf(".")) + ".prefab"; _fbxRes = AssetDatabase.LoadAssetAtPath(_originalAssetPath); if (_fbxRes == null) { Debug.LogFormat("无法读取 {0}, 地址: {1}", resName, _originalAssetPath); return; } } // 为了防止fbx本身就带有材质, 这里去掉fbx上的Import material属性 ModelImporter _modelImport = AssetImporter.GetAtPath(_originalAssetPath) as ModelImporter; if (_modelImport != null) { if (_modelImport.importMaterials) { _modelImport.importMaterials = false; _modelImport.SaveAndReimport(); } } GameObject _prefab = Object.Instantiate(_fbxRes); // 设置绑点 if (m_MountPointInfoList.Count > 0) { MountPointInfo _mpInfo = null; for (int i = 0; i < m_MountPointInfoList.Count; ++i) { _mpInfo = m_MountPointInfoList[i]; GameObject _mp = new GameObject(_mpInfo.name); Transform _parent = null; if (string.IsNullOrEmpty(_mpInfo.path)) { _parent = _prefab.transform; } else { _parent = _prefab.transform.Find(_mpInfo.path); } if (_parent != null) { _mp.transform.SetParent(_parent); _mp.transform.localPosition = _mpInfo.localPosition; _mp.transform.localRotation = _mpInfo.localRotation; _mp.transform.localScale = _mpInfo.localScale; } } } // 加载特效 if (outName.StartsWith("M") || outName.StartsWith("N") || outName.StartsWith("B_") || outName.StartsWith("Zq")) { HandlerModelEffect("horseEffectConfig", null, outName, ref _prefab); } else if (InstanceResourcesLoader.raceSuffix.Equals(suffix)) { HandlerModelEffect("clothesEffectConfig", path, resName, ref _prefab); } if (_colliderInfo != null) { CapsuleCollider _collider = _prefab.AddMissingComponent(); _collider.center = _colliderInfo.center; _collider.radius = _colliderInfo.radius; _collider.height = _colliderInfo.height; } Transform _rendererNode = _prefab.transform.Find(resName); if (!_rendererNode) { _rendererNode = _prefab.transform; } Renderer _renderer = _rendererNode.GetComponent(); if (_renderer) { OnSetupRenderer(path, resName, ref _renderer); } Animation _animation = _prefab.GetComponent(); if (_animation) { Object.DestroyImmediate(_animation); } Animator _animator = _prefab.GetComponent(); if (!_animator) { _animator = _prefab.AddComponent(); } // 绑定动画控制器 RuntimeAnimatorController _runtimeAnimatorContorller = AssetDatabase.LoadAssetAtPath(_acAssetPath); if (_runtimeAnimatorContorller) { _animator.runtimeAnimatorController = _runtimeAnimatorContorller; } PrefabUtility.CreatePrefab(_assetPath, _prefab, ReplacePrefabOptions.Default); // 设置打包路径 AssetImporter _assetImport = AssetImporter.GetAtPath(_assetPath); _assetImport.assetBundleName = ResourcesPath.MOB_FOLDER_NAME + (InstanceResourcesLoader.raceSuffix + resName).ToLower(); EditorUtility.SetDirty(_prefab); AssetDatabase.Refresh(); Object.DestroyImmediate(_prefab); AssetDatabase.SaveAssets(); } private void HandlerModelEffect(string configName, string path, string modelName, ref GameObject _go) { if (_go == null) { return; } string _configPath = ResourcesPath.ResourcesOutAssetPath + "Mob/" + configName + ".asset"; HorseEffectConfig _horseEffectConfig = AssetDatabase.LoadAssetAtPath(_configPath); if (_horseEffectConfig == null || _horseEffectConfig.effectConfigList == null || _horseEffectConfig.effectConfigList.Count == 0) { return; } HorseEffectConfig.EffectConfig _effectConfig; int _index = -1; for (int i = 0; i < _horseEffectConfig.effectConfigList.Count; ++i) { _effectConfig = _horseEffectConfig.effectConfigList[i]; if (_effectConfig.modelName.Equals(modelName)) { _index = i; break; } } if (_index == -1) { Debug.LogFormat("HandlerModelEffect() => 模型 {0} 没有配置任何特效", modelName); return; } _effectConfig = _horseEffectConfig.effectConfigList[_index]; Transform _parent; GameObject _effect; string _bonePath; string _effectName; for (int i = 0; i < _effectConfig.boneNameList.Count; ++i) { _bonePath = _effectConfig.boneNameList[i]; _parent = _go.transform.GetChildTransformDeeply(_bonePath); if (_parent == null) { Debug.LogWarningFormat("HandlerModelEffect() => 坐骑 {0} 配置的骨骼名称: {1} 找不到", modelName, _bonePath); continue; } _effectName = _effectConfig.effectNameList[i]; if (string.IsNullOrEmpty(path)) { _configPath = string.Format("Assets/Art/Role/{0}/Effects/{1}.prefab", modelName, _effectName); } else { _configPath = string.Format("Assets/Art/Role/{0}/Effects/{1}.prefab", path, _effectName); } _effect = AssetDatabase.LoadAssetAtPath(_configPath); if (_effect == null) { Debug.LogWarningFormat("HandlerModelEffect() => 坐骑 {0} 配置的特效资源: {1} 找不到", modelName, _configPath); continue; } _effect = Object.Instantiate(_effect); _effect.name = _effect.name.Replace("(Clone)", ""); _effect.transform.SetParentEx(_parent, Vector3.zero, Quaternion.identity, Vector3.one); _effect.SetLayer(LayerUtility.BattleEffect, true); } } protected static void CreateMountPointInfo(Transform t) { MountPointInfo _info = new MountPointInfo(); _info.path = SampleEditorTool.PrintHierarchyPath(t); if (!string.IsNullOrEmpty(_info.path)) { if (_info.path.Contains("/")) { _info.path = _info.path.Replace("/" + t.name, ""); } else { _info.path = _info.path.Replace(t.name, ""); } } _info.name = t.name; _info.localPosition = t.localPosition; _info.localRotation = t.localRotation; _info.localScale = t.localScale; m_MountPointInfoList.Add(_info); } public static void BuildAnimationClip(string path) { string _absOriginaleDirectory = string.Format("{0}/ART/Role/{1}", Application.dataPath, path); string _assetOriginalDirectory = string.Format("Assets/ART/Role/{0}", path); string _absAssetDirectory = string.Format("{0}/ResourcesOut/Mob/{1}/AnimationClips", Application.dataPath, path); string _assetDirectory = string.Format("Assets/ResourcesOut/Mob/{0}/AnimationClips", path); if (!Directory.Exists(_absOriginaleDirectory)) { return; } // 生成目标文件夹是否存在, 不存在则创建 if (!Directory.Exists(_absAssetDirectory)) { Directory.CreateDirectory(_absAssetDirectory); } DirectoryInfo _dInfo = new DirectoryInfo(_absOriginaleDirectory); FileInfo[] _fInfos = _dInfo.GetFiles("*.fbx"); FileInfo _fInfo = null; Object _animationClip = null; AssetImporter _assetImport = null; int _count = _fInfos.Length; string _newName = string.Empty; string _assetDestPath = string.Empty; string _originalPath = string.Empty; List clips = new List(); for (int i = 0; i < _fInfos.Length; ++i) { _fInfo = _fInfos[i]; if (!_fInfo.Name.Contains("@")) { continue; } _newName = _fInfo.Name.Substring(_fInfo.Name.IndexOf("@") + 1); _newName = _newName.Substring(0, _newName.Length - 4); _assetDestPath = string.Format("{0}/{1}.anim", _absAssetDirectory, _newName); EditorUtility.DisplayProgressBar(string.Format("正在生成 {0} 的动画中...", path), string.Format("{0} => {1}.", _fInfo.Name, _assetDestPath), i * 1f / _count); if (File.Exists(_assetDestPath)) { if (OnlyBuildNoExist) { continue; } } _assetDestPath = string.Format("{0}/{1}.anim", _assetDirectory, _newName); _originalPath = _fInfo.FullName.Substring(Application.dataPath.Length + 1); _originalPath = Path.Combine("Assets/", _originalPath); _originalPath = _originalPath.Replace("\\", "/"); _animationClip = AssetDatabase.LoadAssetAtPath(_originalPath, typeof(AnimationClip)); AnimationClip _newClip = new AnimationClip(); EditorUtility.CopySerialized(_animationClip, _newClip); _newClip.legacy = false; if (_newClip.name.Contains("Idle") || _newClip.name.Contains("Run") || _newClip.name.Contains("Stun")) { if (_newClip.isLooping == false) { AnimationClipSettings _clipSetting = AnimationUtility.GetAnimationClipSettings(_newClip); _clipSetting.loopTime = true; AnimationUtility.SetAnimationClipSettings(_newClip, _clipSetting); } } AssetDatabase.CreateAsset(_newClip, _assetDestPath); _assetImport = AssetImporter.GetAtPath(_assetDestPath); _assetImport.assetBundleName = ResourcesPath.MOB_FOLDER_NAME + ResourcesPath.MOB_SUFFIX + path.ToLower(); clips.Add(_newClip); EditorUtility.SetDirty(_newClip); } EditorUtility.ClearProgressBar(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } public static void BuildAnimatorController(string resName, string templeName, string configName, string outName = null) { if (string.IsNullOrEmpty(outName)) { outName = resName; } string _absAssetPath = string.Format("{0}Mob/{1}", ResourcesPath.ResourcesOutPath, resName); if (!Directory.Exists(_absAssetPath)) { Directory.CreateDirectory(_absAssetPath); } _absAssetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutPath, resName, AnimatorControllerLoader.controllerSuffix, outName); string _assetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutAssetPath, resName, AnimatorControllerLoader.controllerSuffix, outName); if (templeName.Equals("Temple_AnimatorController_BossShow")) { _absAssetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutPath, resName, AnimatorControllerLoader.controllerShowSuffix, resName); _assetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutAssetPath, resName, AnimatorControllerLoader.controllerShowSuffix, resName); } if (templeName.Equals("Temple_AnimatorController_Realm")) { _absAssetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutPath, resName, AnimatorControllerLoader.controllerRealmSuffix, resName); _assetPath = string.Format("{0}Mob/{1}/{2}{3}.controller", ResourcesPath.ResourcesOutAssetPath, resName, AnimatorControllerLoader.controllerRealmSuffix, resName); } string _templePath = string.Format("{0}Mob/{1}.controller", ResourcesPath.ResourcesOutPath, templeName); if (File.Exists(_absAssetPath)) { if (OnlyBuildNoExist) { return; } else { File.Delete(_absAssetPath); } } File.Copy(_templePath, _absAssetPath); AssetDatabase.Refresh(); AnimatorController _animatorController = AssetDatabase.LoadAssetAtPath(_assetPath); if (!_animatorController) { Debug.LogErrorFormat("找不到所需要的动画状态机, 地址为: {0}", _assetPath); return; } AnimatorControllerLayer _layer = _animatorController.layers[0]; AnimatorStateMachine _stateMachine = _layer.stateMachine; // 读取配置文件 string _configPath = StringUtility.Contact(ResourcesPath.ResourcesOutAssetPath, "mob/", configName, ".asset"); AnimatorBuildConfig _buildConfig = AssetDatabase.LoadAssetAtPath(_configPath); HandleStateMachine(resName, _stateMachine, _buildConfig); EditorUtility.SetDirty(_animatorController); AssetImporter _assetImport = AssetImporter.GetAtPath(_assetPath); _assetImport.assetBundleName = ResourcesPath.MOB_FOLDER_NAME + ResourcesPath.MOB_SUFFIX + resName; EditorUtility.ClearProgressBar(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } protected static void HandleStateMachine(string modelName, AnimatorStateMachine stateMachine, AnimatorBuildConfig _buildConfig) { if (stateMachine.states == null || stateMachine.states.Length == 0) { return; } AnimatorState _animatorState = null; Motion _motion = null; int _configStateIndex = -1; for (int i = 0; i < stateMachine.states.Length; ++i) { _animatorState = stateMachine.states[i].state; _motion = _animatorState.motion; foreach (var _clipInfo in _buildConfig.clipConfigs) { if (_clipInfo.stateName.Equals(_animatorState.name)) { _configStateIndex = _buildConfig.clipConfigs.IndexOf(_clipInfo); } } if (_configStateIndex == -1) { Debug.LogWarningFormat("没有找到状态机上名称为: {0}在animatorBuildConfig里的对应配置. ", _animatorState.name); continue; } // 处理融合树类型 if (_motion is BlendTree) { } else {// 非融合树类型处理 if (_configStateIndex < 0 || _configStateIndex >= _buildConfig.clipConfigs.Count) { Debug.LogWarningFormat("state的名称{0}在配置表 animatorBuildConfig 找到索引{1} 找不到它对应的 clipConfigs 动画配置. ", _animatorState.name, _configStateIndex); continue; } // 读取配置上的动画片段 List _configClipNames = _buildConfig.clipConfigs[_configStateIndex].clipNames; if (_configClipNames == null || _configClipNames.Count == 0) { continue; } AnimationClip _configClip; for (int j = 0; j < _configClipNames.Count; ++j) { _configClip = LoadModelAnimationClip(modelName, _configClipNames[j]); if (_configClip != null) { _animatorState.motion = _configClip; break; } } if (_animatorState.motion == null) { Debug.LogWarningFormat("为{0}状态设置动画失败!!!", _animatorState.name); } } } for (int i = 0; i < stateMachine.stateMachines.Length; ++i) { HandleStateMachine(modelName, stateMachine.stateMachines[i].stateMachine, _buildConfig); } } protected static AnimationClip LoadModelAnimationClip(string modelName, string clipName) { string _assetPath = StringUtility.Contact(ResourcesPath.ResourcesOutAssetPath, "mob/", modelName, "/AnimationClips/", clipName, ".anim"); AnimationClip _animationClip = AssetDatabase.LoadAssetAtPath(_assetPath); return _animationClip; } protected virtual void OnSetupRenderer(string path, string resName, ref Renderer renderer) { } }