using System.Collections.Generic; using System.IO; using cfg.WeaponCfg; using Gameplay.Character; using Gameplay.Utils; using PhxhSDK.Phxh.AutoCombine; using SkillEditor; using UnityEditor; using UnityEditor.Animations; using UnityEngine; namespace CharacterBuilder { public abstract class BaseRoleBuildData { public DataWeapon weaponConfig; public EmojiExtInfo emojiExt; public string errorInfo; public bool isChecked; public int attackAnimOrder; public int reloadAnimOrder; protected List _keepAnims = new List() { "ani_common_dizziness001", "common_retreat001", "common_dead001", }; public string weaponGroupName { get { if (weaponConfig == null) { return "无武器配置"; } var groupName = string.IsNullOrEmpty(weaponConfig.GroupName) ? "未配置" : weaponConfig.GroupName; return groupName + "-" + weaponConfig.NameRead; } } protected string _GetNewAnimName(string oldAnimName, string idName) { if (oldAnimName == "stand_ready001") { return $"ani_{weaponConfig.GroupName}_stand_idle001"; } if (oldAnimName == "skill_01") { return $"ani_{idName}_skill01"; } var splits = oldAnimName.Split('_'); var action1 = splits[0]; var action2 = splits[1]; if (action2 == "attack001") { switch (attackAnimOrder) { case 2: action2 = "attack002"; break; } } if (action2 == "reload001") { switch (reloadAnimOrder) { case 2: action2 = "reload002"; break; } } return $"ani_{weaponConfig.GroupName}_{action1}_{action2}"; } protected string _CheckAnims(Dictionary dict, string nameId) { /*** ani_common_dizziness001 ani_common_retreat001 ani_common_dead001 保持原样 ani_g01_stand_ready001 使用 ani_g04_stand_idle001 skill_01 使用 ani_a00005_skill01 其他使用 ani_g05_stand_idle001 */ var loadAnimator = AssetDatabase.LoadAssetAtPath("Assets/Art/Animations/animator/animator_pure_n.controller"); var allClips = loadAnimator.animationClips; for (int i = 0; i < allClips.Length; i++) { var oldClip = allClips[i]; var oldClipName = oldClip.name; if (_keepAnims.Contains(oldClipName)) { continue; } var newClipName = _GetNewAnimName(oldClipName, nameId); if (!dict.ContainsKey(newClipName)) { return $"动画{oldClipName}找不到对应的{newClipName}"; } } return null; } private AnimatorOverrideController _AutoBuildAnimator(Dictionary clipDict, string nameId) { // 创建overrideController var overrideController = new AnimatorOverrideController(); // 建立动画关系 var loadAnimator = AssetDatabase.LoadAssetAtPath("Assets/Art/Animations/animator/animator_pure_n.controller"); overrideController.runtimeAnimatorController = loadAnimator; var oList = new List>(); var allClips = loadAnimator.animationClips; var reCacheDict = new Dictionary(); for (int i = 0; i < allClips.Length; i++) { var oldClip = allClips[i]; var oldClipName = oldClip.name; // 保持不变的 if (_keepAnims.Contains(oldClipName)) { var pairOld = new KeyValuePair(oldClip, oldClip); oList.Add(pairOld); reCacheDict.TryAdd(oldClipName, oldClip); continue; } // 需要替换的 var newClipName = _GetNewAnimName(oldClipName, nameId); if (clipDict.TryGetValue(newClipName, out var newClip)) { var pair = new KeyValuePair(oldClip, newClip); oList.Add(pair); reCacheDict.TryAdd(oldClipName, newClip); } else { // 找不到的 DebugUtil.LogError($"找不到动画{newClipName}"); var pairOld = new KeyValuePair(oldClip, oldClip); oList.Add(pairOld); reCacheDict.TryAdd(oldClipName, oldClip); } } overrideController.ApplyOverrides(oList); // 导出动画数据 AnimatorInfoExporter.ExportAnimatorInfo(loadAnimator, reCacheDict, nameId); return overrideController; } private bool _CombineEmoji(GameObject prefab) { var emojiObj = _DeadSearchTrans(prefab.transform, "F_emoji"); if (emojiObj != null) { Object.DestroyImmediate(emojiObj.gameObject); } emojiObj = _DeadSearchTrans(prefab.transform, "G_emoji"); if (emojiObj != null) { Object.DestroyImmediate(emojiObj.gameObject); } if (emojiExt.emojiId == 0) { DebugUtil.LogError("没有表情id,取消创建默认表情:" + prefab.name); return false; } // 找到头部 var playerObj = prefab.transform.GetChild(0); var headObj = playerObj.GetChild(1); if (!headObj.name.EndsWith("_face")) { DebugUtil.LogError("找不到头部模型,取消创建默认表情:" + playerObj.name); return false; } var skinMeshRender = headObj.GetComponent(); if (skinMeshRender == null) { DebugUtil.LogError("找不到头部模型skninmeshrenderer,取消创建默认表情:" + playerObj.name); return false; } var oldMat = skinMeshRender.sharedMaterial; var mainTex = oldMat.GetTexture("_MainTex") as Texture2D; var newMat = new Material(oldMat); mainTex = _DrawFace(mainTex); newMat.SetTexture("_MainTex", mainTex); // 设置材质 skinMeshRender.sharedMaterial = newMat; return true; } private Texture2D _DrawFace(Texture2D t2d) { // Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_01.png // Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_01_mask.png var numStr = emojiExt.emojiId.ToString("D2"); var texturePath = $"Assets/Art/Character/Models/Emoji/Tex/T_emoji_lian_{numStr}.png"; var emojiTex = AssetDatabase.LoadAssetAtPath(texturePath); if (emojiTex == null) { DebugUtil.LogError("找不到表情贴图:" + texturePath); return t2d; } var maskTexPath = texturePath.Replace(".png", "_mask.png"); var maskTex = AssetDatabase.LoadAssetAtPath(maskTexPath); // 画到主贴图上 var newTex = _DrawEmojiToMainTex(t2d, emojiTex, maskTex); return newTex; } private Texture2D _DrawEmojiToMainTex(Texture2D mainTex, Texture2D emojiTex, Texture2D maskTex) { var eyebowsColor = ColorEx.FromStr(emojiExt.eyebowsColor); if (string.IsNullOrEmpty(emojiExt.eyebowsColor)) { eyebowsColor = ColorEx.RandomColor(); } var eyesColor = ColorEx.FromStr(emojiExt.eyesColor); if (string.IsNullOrEmpty(emojiExt.eyesColor)) { eyesColor = ColorEx.RandomColor(); } var shader = Shader.Find("Unlit/DrawFace"); var material = new Material(shader); material.SetTexture("_MainTex", mainTex); material.SetTexture("_FaceTex", emojiTex); material.SetTexture("_FaceMask", maskTex); material.SetColor("_EyebowsColor", eyebowsColor); material.SetColor("_EyeColor", eyesColor); // 创建一个新的贴图 var newTex = new Texture2D(mainTex.width, mainTex.height, TextureFormat.RGBA32, false); // 画到新的贴图上 var rt = RenderTexture.GetTemporary(mainTex.width, mainTex.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(mainTex, rt, material); RenderTexture.active = rt; newTex.ReadPixels(new Rect(0, 0, mainTex.width, mainTex.height), 0, 0); newTex.Apply(); RenderTexture.active = null; RenderTexture.ReleaseTemporary(rt); return newTex; } private Transform _DeadSearchTrans(Transform rootTrans, string findName) { var childCount = rootTrans.childCount; for (int i = 0; i < childCount; i++) { var child = rootTrans.GetChild(i); if (child.name == findName) { return child; } var result = _DeadSearchTrans(child, findName); if (result != null) { return result; } } return null; } private void _SetHitEffectPoints(string prefabPath, GameObject animatorObj) { var samplePath = "Assets/Art/Character/Prefabs/Players/P_A00002.prefab"; if (prefabPath == samplePath) { return; } var oldCmp = animatorObj.GetComponent(); if (oldCmp == null) { oldCmp = animatorObj.AddComponent(); } oldCmp._AutoSet(); } private bool _RebuildAnimator(Dictionary clipDict, string prefabPath, string nameId) { // 加载prefab var loadPrefab = AssetDatabase.LoadAssetAtPath(prefabPath); if (loadPrefab == null) { Debug.LogError($"{nameId}找不到原始prefab:{prefabPath},取消导出"); return false; } // 找到animator var animator = loadPrefab.GetComponentInChildren(true); if (animator == null) { Debug.LogError($"{nameId}原始prefab没有animator,取消导出"); return false; } animator.enabled = true; var overrideController = _AutoBuildAnimator(clipDict, nameId); // Assets/Art_Out/AutoGen/Characters/character_{id} var parentPath = $"Assets/Art_Out/AutoGen/Characters/character_{nameId}"; if (Directory.Exists(parentPath)) { Directory.Delete(parentPath, true); } Directory.CreateDirectory(parentPath); // 保存overrideController // {parentPath}/character_{id}_{weapon}.overrideController var savePath = $"{parentPath}/ov_{nameId}_{weaponConfig.GroupName}.overrideController"; AssetDatabase.CreateAsset(overrideController, savePath); _SetHitEffectPoints(prefabPath, animator.gameObject); // 原来的prefab也需要保存 // 设置overrideController到animator上 animator.runtimeAnimatorController = overrideController; animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; EditorUtility.SetDirty(loadPrefab); AssetDatabase.SaveAssets(); return true; } private string[] _PreSetAll(GameObject prefab) { var allSkin = prefab.GetComponentsInChildren(); string[] allTexNames = new string[] { "_MainTex", "_MaskTex" }; foreach (SkinnedMeshRenderer skin in allSkin) { var material = skin.sharedMaterial; if (material.shader.name != "NLD_URP/NLD_Charactor") { // 修改为NLD_URP/NLD_Charactor material.shader = Shader.Find("NLD_URP/NLD_Charactor"); } // 判断所有的贴图是否已开启read/write foreach (var texName in allTexNames) { var tex = material.GetTexture(texName); if (tex != null) { var texPath = AssetDatabase.GetAssetPath(tex); var importer = AssetImporter.GetAtPath(texPath) as TextureImporter; if (importer.isReadable == false) { importer.isReadable = true; importer.SaveAndReimport(); } } } } return allTexNames; } private string _CombineSkins(string prefabPath, string nameId) { var parentPath = $"Assets/Art_Out/AutoGen/Characters/character_{nameId}"; var loadPrefab = AssetDatabase.LoadAssetAtPath(prefabPath); var instPrefab = Object.Instantiate(loadPrefab); var allTexNames = _PreSetAll(instPrefab); AssetDatabase.Refresh(); // 将表情画到头上 var result = _CombineEmoji(instPrefab); if (!result) { Debug.LogError("合并表情失败,该角色进入战场后将没有表情"); } AutoCombine.CombineSkinMesh(instPrefab, allTexNames); // 合并完了将中间生成的素材进行保存,这里合并了之后理论上只有一个 var skinMeshRenderer = instPrefab.GetComponentInChildren(); var material = skinMeshRenderer.sharedMaterial; // textures foreach (var texName in allTexNames) { var tex = material.GetTexture(texName); var texSavePath = $"{parentPath}/{texName}.png"; var newTex = _SaveAndReloadTexture2D(tex as Texture2D, texSavePath); material.SetTexture(texName, newTex); } // material var matSavePath = $"{parentPath}/mat_{nameId}.mat"; AssetDatabase.CreateAsset(material, matSavePath); // mesh var mesh = skinMeshRenderer.sharedMesh; var meshSavePath = $"{parentPath}/mesh_{nameId}.asset"; AssetDatabase.CreateAsset(mesh, meshSavePath); // 保存新的prefab // {parentPath}/character_{id}_{weapon}.prefab var prefabSavePath = $"{parentPath}/character_{nameId}.prefab"; PrefabUtility.SaveAsPrefabAsset(instPrefab, prefabSavePath); // 删除临时对象 Object.DestroyImmediate(instPrefab); return parentPath; } private Texture2D _SaveAndReloadTexture2D(Texture2D t2d, string savePath) { var bytes = t2d.EncodeToPNG(); File.WriteAllBytes(savePath, bytes); AssetDatabase.Refresh(); var newT2d = AssetDatabase.LoadAssetAtPath(savePath); return newT2d; } protected void _Rebuild(Dictionary clipDict, string prefabPath, string nameId) { var result = _RebuildAnimator(clipDict, prefabPath, nameId); if (!result) { return; } var parentPath = _CombineSkins(prefabPath, nameId); _AddToAddressables(parentPath); // 打印日志 Debug.Log($"重建{prefabPath}成功"); } private void _AddToAddressables(string assetPath) { #if USE_ADDRESSABLES // 获取 AddressableAssetSettings var settings = AddressableAssetSettingsDefaultObject.Settings; // 获取或创建一个资源组 var group = settings.FindGroup("SplitCharacter"); if (group == null) { Debug.LogError("找不到资源组:SplitCharacter"); return; } // 创建或移动条目 var guid = AssetDatabase.AssetPathToGUID(assetPath); settings.CreateOrMoveEntry(guid, group); #endif } public abstract string CheckAnims(Dictionary dict); public abstract void RebuildAll(Dictionary clipDict); } }