NLDClient-yudde/ProjectNLD/Assets/Editor/CharacterBuilder/BaseRoleBuildData.cs

479 lines
18 KiB
C#

using System.Collections.Generic;
using System.IO;
using cfg.EmojCfg;
using cfg.WeaponCfg;
using Framework;
using Gameplay.Character;
using Gameplay.Performance.Cmp;
using Gameplay.Utils;
using PhxhSDK.Phxh.AutoCombine;
using SkillEditor;
using Tools;
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<string> _keepAnims = new List<string>()
{
"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<string, AnimationClip> 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<AnimatorController>("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<string, AnimationClip> clipDict,
string nameId)
{
// 创建overrideController
var overrideController = new AnimatorOverrideController();
// 建立动画关系
var loadAnimator = AssetDatabase.LoadAssetAtPath<AnimatorController>("Assets/Art/Animations/animator/animator_pure_n.controller");
overrideController.runtimeAnimatorController = loadAnimator;
var oList = new List<KeyValuePair<AnimationClip, AnimationClip>>();
var allClips = loadAnimator.animationClips;
var reCacheDict = new Dictionary<string, AnimationClip>();
for (int i = 0; i < allClips.Length; i++)
{
var oldClip = allClips[i];
var oldClipName = oldClip.name;
// 保持不变的
if (_keepAnims.Contains(oldClipName))
{
var pairOld = new KeyValuePair<AnimationClip, AnimationClip>(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<AnimationClip, AnimationClip>(oldClip, newClip);
oList.Add(pair);
reCacheDict.TryAdd(oldClipName, newClip);
}
else
{
// 找不到的
DebugUtil.LogError($"找不到动画{newClipName}");
var pairOld = new KeyValuePair<AnimationClip, AnimationClip>(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<SkinnedMeshRenderer>();
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<Texture2D>(texturePath);
if (emojiTex == null)
{
DebugUtil.LogError("找不到表情贴图:" + texturePath);
return t2d;
}
var maskTexPath = texturePath.Replace(".png", "_mask.png");
var maskTex = AssetDatabase.LoadAssetAtPath<Texture2D>(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<CmpCharacterEffectPoints>();
if (oldCmp == null)
{
oldCmp = animatorObj.AddComponent<CmpCharacterEffectPoints>();
}
oldCmp._AutoSet();
}
private bool _RebuildAnimator(Dictionary<string, AnimationClip> clipDict,
string prefabPath,
string nameId)
{
// 加载prefab
var loadPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (loadPrefab == null)
{
Debug.LogError($"{nameId}找不到原始prefab:{prefabPath},取消导出");
return false;
}
// 找到animator
var animator = loadPrefab.GetComponentInChildren<Animator>(true);
if (animator == null)
{
Debug.LogError($"{nameId}原始prefab没有animator,取消导出");
return false;
}
animator.enabled = true;
var overrideController = _AutoBuildAnimator(clipDict, nameId);
// Assets/Art/AutoGen/Characters/character_{id}
var parentPath = $"Assets/Art/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<SkinnedMeshRenderer>();
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/AutoGen/Characters/character_{nameId}";
var loadPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(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<SkinnedMeshRenderer>();
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<Texture2D>(savePath);
return newT2d;
}
protected void _Rebuild(Dictionary<string, AnimationClip> 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<string, AnimationClip> dict);
public abstract void RebuildAll(Dictionary<string, AnimationClip> clipDict);
}
}